diff --git a/.github/workflows/code_analysis.yml b/.github/workflows/code_analysis.yml index e1d32abdd75d4..b76fa9880398b 100644 --- a/.github/workflows/code_analysis.yml +++ b/.github/workflows/code_analysis.yml @@ -90,8 +90,8 @@ jobs: run: | files=$(grep '\.py$' changed_files.txt || echo "") if [ -n "$files" ]; then - diff_command="" apply_command="" + failure=false for file in $files; do while IFS=- read -r start length; do [ -z "$start" ] && continue @@ -101,20 +101,14 @@ jobs: continue fi end=$((start + length)) - diff_command+="ruff format --diff --range $start-$end $file && " + ruff format --diff --preview --range ${start}-${end} ${file} || failure=true apply_command+="ruff format --range $start-$end $file && " done < <(git diff --unified=0 HEAD~1 -- "$file" | grep '^@@' | sed -E 's/^@@ -[0-9]+(,[0-9]+)? \+([0-9]+)(,([0-9]+))? @@.*/\2-\4/') done - if [ -n "$diff_command" ]; then - diff_command=${diff_command% && } - if ! eval "$diff_command"; then - apply_command=${apply_command% && } - echo -e "::error::Formatting failed. To apply the changes locally, run the following command:\n$apply_command" - exit 123 - fi - else - echo "No ranges detected to format." + if ${failure}; then + echo -e "::error::Formatting failed. To apply the changes locally, run the following command:\n$apply_command" + exit 123; fi else echo "No python files to format" diff --git a/.github/workflows/root-ci-config/buildconfig/alma10-benchmark.txt b/.github/workflows/root-ci-config/buildconfig/alma10-benchmark.txt index 99f88be784e71..ce7742559e68f 100644 --- a/.github/workflows/root-ci-config/buildconfig/alma10-benchmark.txt +++ b/.github/workflows/root-ci-config/buildconfig/alma10-benchmark.txt @@ -1,7 +1,5 @@ CMAKE_CXX_STANDARD=20 builtin_vdt=ON -builtin_zlib=ON -builtin_zstd=ON pythia8=ON r=OFF rootbench=ON diff --git a/.github/workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt b/.github/workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt index 7512de26c3071..6950ed9898087 100644 --- a/.github/workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt +++ b/.github/workflows/root-ci-config/buildconfig/alma10-clang_ninja.txt @@ -1,12 +1,13 @@ CMAKE_C_COMPILER=clang CMAKE_CXX_COMPILER=clang++ CMAKE_GENERATOR=Ninja -builtin_vdt=ON +builtin_freetype=ON builtin_gif=ON builtin_jpeg=ON -builtin_png=ON builtin_lz4=ON builtin_lzma=ON +builtin_png=ON +builtin_vdt=ON builtin_zlib=ON builtin_zstd=ON pythia8=ON diff --git a/.github/workflows/root-ci-config/buildconfig/alma10-minimal.txt b/.github/workflows/root-ci-config/buildconfig/alma10-minimal.txt index c98f232c6151f..a05a0aea30429 100644 --- a/.github/workflows/root-ci-config/buildconfig/alma10-minimal.txt +++ b/.github/workflows/root-ci-config/buildconfig/alma10-minimal.txt @@ -1,7 +1,5 @@ ccache=ON builtin_vdt=ON -builtin_zlib=ON -builtin_zstd=ON fail-on-missing=ON minimal=ON roottest=ON diff --git a/.github/workflows/root-ci-config/buildconfig/alma10.txt b/.github/workflows/root-ci-config/buildconfig/alma10.txt index 0a84001142aaa..f265d40f10e5a 100644 --- a/.github/workflows/root-ci-config/buildconfig/alma10.txt +++ b/.github/workflows/root-ci-config/buildconfig/alma10.txt @@ -1,5 +1,3 @@ builtin_vdt=ON -builtin_zlib=ON -builtin_zstd=ON pythia8=ON r=OFF diff --git a/.github/workflows/root-ci-config/buildconfig/fedora43.txt b/.github/workflows/root-ci-config/buildconfig/fedora43.txt index 3563fe5a20b6a..465ff7b6fa487 100644 --- a/.github/workflows/root-ci-config/buildconfig/fedora43.txt +++ b/.github/workflows/root-ci-config/buildconfig/fedora43.txt @@ -1,6 +1,4 @@ CMAKE_CXX_STANDARD=23 -builtin_zlib=ON -builtin_zstd=ON experimental_adaptivecpp=ON pythia8=ON roofit_multiprocess=ON diff --git a/.github/workflows/root-ci-config/buildconfig/fedora44.txt b/.github/workflows/root-ci-config/buildconfig/fedora44.txt index a01ed2484f58a..b4c72b29cadc9 100644 --- a/.github/workflows/root-ci-config/buildconfig/fedora44.txt +++ b/.github/workflows/root-ci-config/buildconfig/fedora44.txt @@ -1,6 +1,4 @@ CMAKE_CXX_STANDARD=23 -builtin_zlib=ON -builtin_zstd=ON experimental_adaptivecpp=ON pythia8=ON roofit_multiprocess=ON diff --git a/.github/workflows/root-ci-config/buildconfig/rawhide.txt b/.github/workflows/root-ci-config/buildconfig/rawhide.txt index a1098e2f490e0..393a11a08eaf8 100644 --- a/.github/workflows/root-ci-config/buildconfig/rawhide.txt +++ b/.github/workflows/root-ci-config/buildconfig/rawhide.txt @@ -1,5 +1,3 @@ -builtin_zlib=ON -builtin_zstd=ON pythia8=ON test_distrdf_dask=OFF test_distrdf_pyspark=OFF diff --git a/CMakeLists.txt b/CMakeLists.txt index 146a23312ca38..6e05dc897ac2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -411,6 +411,9 @@ else() if(asimage) message(SEND_ERROR "minimal=ON is not compatible with asimage=ON. Consider setting instead gminimal=ON") endif() + if(asimage_tiff) + message(SEND_ERROR "minimal=ON is not compatible with asimage_tiff=ON. Consider setting instead gminimal=ON") + endif() if(x11) message(SEND_ERROR "minimal=ON is not compatible with x11=ON. Consider setting instead gminimal=ON") endif() diff --git a/README/ReleaseNotes/v640/index.md b/README/ReleaseNotes/v640/index.md index bf2ae6264b139..92cf45e722b71 100644 --- a/README/ReleaseNotes/v640/index.md +++ b/README/ReleaseNotes/v640/index.md @@ -48,6 +48,9 @@ The following people have contributed to this new version: The ZeroMQ library and its C++ bindings are used by the experimental RooFit multiprocessing package, enabled by the `roofit_multiprocess` build option. The ZeroMQ versions it requires (>=4.3.6 or 4.3.5 with the draft API) are now available in the package managers of several platforms, for example Conda, Homebrew, Fedora and the Extra Packages for Enterprise Linux (EPEL). The `roofit_multiprocess` feature is only required by a small set of RooFit power uses, who are using one of these environments and therefore don't require the builtin ZeroMQ library. +* The overloads of `RooAbsReal::createChi2()` and `RooAbsReal::chi2FitTo()` that take unbinned **RooDataSet** data objects are deprecated and will be removed in ROOT 6.42. + These methods implemented a specialized chi-square fit for x-y-data with errors in y and optional errors in x, which is conceptually different from the standard histogram-based chi-square in the **RooDataHist** case and can lead to ambiguous results. + To fit 2D data with errors in and `x` and `y`, use specialized tools like `TGraphErrors::Fit()`, or build an explicit likelihood model if you want to stay with RooFit. ## Removals @@ -69,6 +72,12 @@ The following people have contributed to this new version: ## Build System +### Moving from builtin dependencies to system-provided packages + +* The general direction of the ROOT project is to become more and more reliant on system packages. It is *recommended* to make the packages required by ROOT available on the system, e.g. via a package manager, and not with the builtin mechanism. This allows for timely updates and reduces the size of the installed binaries. +* The previously vendored builtins `freetype`, `zlib`, `lzma`, `zstd`, `lz4`, `libpng`, `giflib`, `libjpeg`, and `openssl` should be installed in the system if possible. ROOT will not automatically fall-back to their builtin versions if these are not found: the user is informed of that with a helpful message. If installing these dependencies in the system is not possible, the CMake option `-Dbuiltin_XYZ=ON` has to be consciously chosen by the user. +* For the builtin versions of `freetype`, `zlib`, `lzma`, `zstd`, `lz4`, `libpng`, `giflib`, `libjpeg`, the source tarballs are now fetched from [SPI](https://spi.web.cern.ch)'s [website](https://lcgpackages.web.cern.ch/), as for the vast majority of ROOT's builtins, e.g. `openssl` or `xrootd`. + ## Core Libraries * ROOT now adds a RUNPATH to compiled macros. This ensures that when compiled macros are loaded, they load the libraries that belong to the ROOT installation @@ -253,6 +262,14 @@ Now: Results may differ from previous ROOT versions for `n > 100` or `nSigma != 1`. The new implementation is statistically consistent and recommended. +### Removal of global expensive object caching in RooFit + +The global “expensive object cache” used in RooFit to store numeric integrals and intermediate histogram results has been removed. + +While originally intended as a performance optimization, this mechanism could lead to **incorrect results due to cache collisions**: cached integrals or histograms created in one context (e.g. during plotting) could be reused unintentionally in a different context, even when the underlying configuration had changed. + +Given the risk of silently incorrect physics results, and the absence of known workflows that depend critically on this feature, this global caching mechanism has been removed. If you encounter performance regressions in workflows involving expensive integrals or convolutions, we encourage you to report your use case and performance regression as a GitHub issue, so that targeted and robust optimizations can be developed, + ## RDataFrame - The message shown in ROOT 6.38 to inform users about change of default compression setting used by Snapshot (was 101 before 6.38, became 505 in 6.38) is now removed. @@ -369,6 +386,29 @@ after importing ROOT. This option is intended **for debugging only and will be removed in ROOT 6.44**. +### Drop support for calling C++ functions with non-const pointer references + +From now on, we disallow calling C++ functions with non-const pointer references (`T*&`) from Python. +These allow pointer rebinding, which cannot be represented safely in Python and could previously lead to confusing behavior. +A `TypeError` is now raised. Typical ROOT usage is unaffected. + +In the rare case where you want to call such a function, please change the C++ interface or - if the interface is outside your control - write a wrapper function that avoids non-const pointer references as arguments. + +For example, a function with the signature `bool setPtr(MyClass *&)` could be wrapped by a function that augments the return value with the updated pointer: +``` +ROOT.gInterpreter.Declare(""" + std::pair setPtrWrapper(MyClass *ptr) { + bool out = setPtr(ptr); + return {out, ptr}; + } +""") +``` +Then, call it from Python as follows: +```Python +# Use tuple unpacking for convenience +_, ptr = cppyy.gbl.setPtrWrapper(ptr) +``` + ### UHI #### Backwards incompatible changes - `TH1.values()` now returns a **read-only** NumPy array by default. Previously it returned a writable array that allowed modifying histogram contents implicitly. @@ -430,10 +470,18 @@ The version of the following packages has been updated: - cppzeromq: 4.10.0 - fftw3: 3.3.10 + - freetype: 2.14.3 - gsl: 2.8 - gtest: 1.17.0 + - giflib: 5.2.2 + - libjpeg-turbo 3.1.3 is now used as a replacement for libjpeg + - libpng: 1.6.4 - libzeromq: 4.3.5 - - xrootd: 5.9.1 + - lz4: 1.10.0 + - lzma: 5.8.2 + - xrootd: 5.9.2 + - zlib: 1.3.2 + - zstd: 1.5.7 ## Items addressed for this release diff --git a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h index 0a4e4b3f6ab31..05bd9f13937a8 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h +++ b/bindings/pyroot/cppyy/CPyCppyy/include/CPyCppyy/API.h @@ -174,6 +174,10 @@ CPYCPPYY_EXTERN void* CallVoidP(Cppyy::TCppMethod_t, Cppyy::TCppObject_t, CallCo //- C++ access to cppyy objects --------------------------------------------- +// Get C++ Instance (python object proxy) name. +// Sets a TypeError and returns an empty string if the pyobject is not a CPPInstance. +CPYCPPYY_EXTERN std::string Instance_GetScopedFinalName(PyObject* pyobject); + // C++ Instance (python object proxy) to void* conversion CPYCPPYY_EXTERN void* Instance_AsVoidPtr(PyObject* pyobject); @@ -199,6 +203,11 @@ CPYCPPYY_EXTERN bool Instance_IsLively(PyObject* pyobject); CPYCPPYY_EXTERN bool Overload_Check(PyObject* pyobject); CPYCPPYY_EXTERN bool Overload_CheckExact(PyObject* pyobject); +// Sets the __reduce__ method for the CPPInstance class, which is by default not +// implemented by cppyy but might make sense to implement by frameworks that +// support IO of arbitrary C++ objects, like ROOT. +CPYCPPYY_EXTERN void Instance_SetReduceMethod(PyCFunction reduceMethod); + //- access to the python interpreter ---------------------------------------- diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/API.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/API.cxx index ad57cd9c559e5..0438e69f69c0e 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/API.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/API.cxx @@ -103,6 +103,18 @@ static bool Initialize() //- C++ access to cppyy objects --------------------------------------------- +std::string CPyCppyy::Instance_GetScopedFinalName(PyObject* pyobject) +{ + if (!Instance_Check(pyobject)) { + PyErr_SetString(PyExc_TypeError, "Instance_GetScopedFinalName : object is not a C++ instance"); + return ""; + } + + Cppyy::TCppType_t pyobjectClass = ((CPPInstance *)pyobject)->ObjectIsA(); + return Cppyy::GetScopedFinalName(pyobjectClass); +} + +//----------------------------------------------------------------------------- void* CPyCppyy::Instance_AsVoidPtr(PyObject* pyobject) { // Extract the object pointer held by the CPPInstance pyobject. @@ -254,6 +266,11 @@ bool CPyCppyy::Overload_CheckExact(PyObject* pyobject) return CPPOverload_CheckExact(pyobject); } +//----------------------------------------------------------------------------- +void CPyCppyy::Instance_SetReduceMethod(PyCFunction reduceMethod) +{ + CPPInstance::ReduceMethod() = reduceMethod; +} //- access to the python interpreter ---------------------------------------- bool CPyCppyy::Import(const std::string& mod_name) @@ -338,117 +355,58 @@ void CPyCppyy::ExecScript(const std::string& name, const std::vector("argv")); // borrowed - if (!oldargv) // e.g. apache + PyObject* oldargv = PySys_GetObject("argv"); // borrowed + if (oldargv) { + PyObject* copy = PyList_GetSlice(oldargv, 0, PyList_Size(oldargv)); + oldargv = copy; // now owned + } else { PyErr_Clear(); - else { - PyObject* l = PyList_New(PyList_GET_SIZE(oldargv)); - for (int i = 0; i < PyList_GET_SIZE(oldargv); ++i) { - PyObject* item = PyList_GET_ITEM(oldargv, i); - Py_INCREF(item); - PyList_SET_ITEM(l, i, item); // steals ref - } - oldargv = l; } -// create and set (add program name) the new command line - int argc = args.size() + 1; -#if PY_VERSION_HEX < 0x03000000 -// This is a legacy implementation for Python 2 - const char** argv = new const char*[argc]; - for (int i = 1; i < argc; ++i) argv[i] = args[i-1].c_str(); - argv[0] = Py_GetProgramName(); - PySys_SetArgv(argc, const_cast(argv)); - delete [] argv; -#else -// This is a common code block for Python 3. We prefer using objects to -// automatize memory management and not introduce even more preprocessor -// branching for deletion at the end of the method. -// -// FUTURE IMPROVEMENT ONCE OLD PYTHON VERSIONS ARE NOT SUPPORTED BY CPPYY: -// Right now we use C++ objects to automatize memory management. One could use -// RAAI and the Python memory allocation API (PEP 445) once some old Python -// version is deprecated in CPPYY. That new feature is available since version -// 3.4 and the preprocessor branching to also support that would be so -// complicated to make the code unreadable. - std::vector argv2; - argv2.reserve(argc); - argv2.emplace_back(name.c_str(), &name[name.size()]); - - for (int i = 1; i < argc; ++i) { - auto iarg = args[i - 1].c_str(); - argv2.emplace_back(iarg, &iarg[strlen(iarg)]); - } - -#if PY_VERSION_HEX < 0x03080000 -// Before version 3.8, the code is one simple line - wchar_t *argv2_arr[argc]; - for (int i = 0; i < argc; ++i) { - argv2_arr[i] = const_cast(argv2[i].c_str()); - } - PySys_SetArgv(argc, argv2_arr); +// build new argv + const int argc = (int)args.size() + 1; + std::vector wargv(argc); -#else -// Here we comply to "PEP 587 – Python Initialization Configuration" to avoid -// deprecation warnings at compile time. - class PyConfigHelperRAAI { - public: - PyConfigHelperRAAI(const std::vector &argv2) - { - PyConfig_InitPythonConfig(&fConfig); - fConfig.parse_argv = 1; - UpdateArgv(argv2); - InitFromConfig(); - } - ~PyConfigHelperRAAI() { PyConfig_Clear(&fConfig); } - - private: - void InitFromConfig() { Py_InitializeFromConfig(&fConfig); }; - void UpdateArgv(const std::vector &argv2) - { - auto WideStringListAppendHelper = [](PyWideStringList *wslist, const wchar_t *wcstr) { - PyStatus append_status = PyWideStringList_Append(wslist, wcstr); - if (PyStatus_IsError(append_status)) { - std::wcerr << "Error: could not append element " << wcstr << " to arglist - " << append_status.err_msg - << std::endl; - } - }; - -#if PY_VERSION_HEX < 0x30d00f0 - WideStringListAppendHelper(&fConfig.argv, Py_GetProgramName()); -#else - PyObject* progname = PySys_GetObject("executable"); // borrowed - wchar_t buf[4096]; - Py_ssize_t sz = CPyCppyy_PyUnicode_AsWideChar(progname, buf, 4095); - if (0 < sz) - WideStringListAppendHelper(&fConfig.argv, buf); -#endif - for (const auto &iarg : argv2) { - WideStringListAppendHelper(&fConfig.argv, iarg.c_str()); - } - } - PyConfig fConfig; - }; + wargv[0] = Py_DecodeLocale(name.c_str(), nullptr); - PyConfigHelperRAAI pych(argv2); + for (int i = 1; i < argc; ++i) { + wargv[i] = Py_DecodeLocale(args[i - 1].c_str(), nullptr); + } -#endif // of the else branch of PY_VERSION_HEX < 0x03080000 -#endif // of the else branch of PY_VERSION_HEX < 0x03000000 +// set sys.argv + PyObject* sysmod = PyImport_ImportModule("sys"); // new reference + if (sysmod) { + PyObject* argv_obj = PyList_New(argc); + for (int i = 0; i < argc; ++i) { + PyList_SET_ITEM(argv_obj, i, PyUnicode_FromWideChar(wargv[i], -1)); + } + PyObject_SetAttrString(sysmod, "argv", argv_obj); + Py_DECREF(argv_obj); + Py_DECREF(sysmod); + } else { + PyErr_Print(); + } // actual script execution PyObject* gbl = PyDict_Copy(gMainDict); PyObject* result = // PyRun_FileEx closes fp (b/c of last argument "1") PyRun_FileEx(fp, const_cast(name.c_str()), Py_file_input, gbl, gbl, 1); + if (!result) PyErr_Print(); + Py_XDECREF(result); Py_DECREF(gbl); // restore original command line if (oldargv) { - PySys_SetObject(const_cast("argv"), oldargv); + PySys_SetObject("argv", oldargv); Py_DECREF(oldargv); } + +// free memory from Py_DecodeLocale + for (auto ptr : wargv) + PyMem_RawFree(ptr); } //----------------------------------------------------------------------------- diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx index 301e9d39d260e..57095383c3e7b 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.cxx @@ -400,19 +400,19 @@ static PySequenceMethods op_as_sequence = { 0, // sq_inplace_repeat }; -std::function &CPPInstance::ReduceMethod() { - static std::function reducer; +PyCFunction &CPPInstance::ReduceMethod() { + static PyCFunction reducer = nullptr; return reducer; } -PyObject *op_reduce(PyObject *self, PyObject * /*args*/) +PyObject *op_reduce(PyObject *self, PyObject * args) { auto &reducer = CPPInstance::ReduceMethod(); if (!reducer) { PyErr_SetString(PyExc_NotImplementedError, ""); return nullptr; } - return reducer(self); + return reducer(self, args); } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h index fcb0ad3d5d11a..3635543d178fc 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPInstance.h @@ -15,7 +15,6 @@ #include "CallContext.h" // for Parameter // Standard -#include #include #include @@ -87,7 +86,7 @@ class CPPInstance { // implementation of the __reduce__ method: doesn't wrap any function by // default but can be re-assigned by libraries that add C++ object // serialization support, like ROOT - static std::function &ReduceMethod(); + static PyCFunction &ReduceMethod(); private: void CreateExtension(); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyy.h b/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyy.h index e3244bdcfe74b..84b193d7646a7 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyy.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyy.h @@ -276,14 +276,6 @@ inline Py_ssize_t PyNumber_AsSsize_t(PyObject* obj, PyObject*) { #define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False #endif -#if PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03010000 -#define CPyCppyy_PyBuffer PyBuffer_Release -#else -inline void CPyCppyy_PyBuffer_Release(PyObject* /* unused */, Py_buffer* view) { - PyBuffer_Release(view); -} -#endif - // vector call support #if PY_VERSION_HEX >= 0x03090000 #define CPyCppyy_PyCFunction_Call PyObject_Call diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx index e4b53a2081637..5c5d374254c11 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPyCppyyModule.cxx @@ -439,7 +439,8 @@ static PyObject* SetCppLazyLookup(PyObject*, PyObject* args) #else // As of py3.11, there is no longer a lookup function pointer in the dict object // to replace. Since this feature is not widely advertised, it's simply dropped - PyErr_Warn(PyExc_RuntimeWarning, (char*)"lazy lookup is no longer supported"); + if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"lazy lookup is no longer supported", 1) < 0) + return nullptr; (void)args; // avoid warning about unused parameter #endif diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx index 56a569d8f0f26..cf7d27adc84db 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Converters.cxx @@ -29,6 +29,9 @@ #include #include #include +#if __cplusplus >= 202002L +#include +#endif // codecvt does not exist for gcc4.8.5 and is in principle deprecated; it is // only used in py2 for char -> wchar_t conversion for std::wstring; if not @@ -441,8 +444,21 @@ static inline PY_LONG_LONG CPyCppyy_PyLong_AsStrictLongLong(PyObject* pyobject) static inline bool CArraySetArg( PyObject* pyobject, CPyCppyy::Parameter& para, char tc, int size, bool check=true) { +// Case of LowLevelView. In general, they also implement the buffer protocol, +// but for views around nullptr or C-style arrays without size info the buffer +// protocol implementation is incomplete and PyObject_GetBuffer will fail. + if (CPyCppyy::LowLevelView_Check(pyobject)) { + auto llview = ((CPyCppyy::LowLevelView*)pyobject); + if (llview->fBufInfo.itemsize != size || !strchr(llview->fBufInfo.format, tc)) { + PyErr_Format(PyExc_TypeError, + "could not convert argument to buffer or nullptr"); + return false; + } + + para.fValue.fVoidp = llview->get_buf(); + } // general case of loading a C array pointer (void* + type code) as function argument - if (pyobject == CPyCppyy::gNullPtrObject || pyobject == CPyCppyy::gDefaultObject) + else if (pyobject == CPyCppyy::gNullPtrObject || pyobject == CPyCppyy::gDefaultObject) para.fValue.fVoidp = nullptr; else { Py_ssize_t buflen = CPyCppyy::Utility::GetBuffer(pyobject, tc, size, para.fValue.fVoidp, check); @@ -1301,7 +1317,8 @@ bool CPyCppyy::CStringConverter::SetArg( // verify (too long string will cause truncation, no crash) if (fMaxSize != std::string::npos && fMaxSize < fBuffer.size()) - PyErr_Warn(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)"); + if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)", 1) < 0) + return false; if (!ctxt->fPyContext) { // use internal buffer as workaround @@ -1346,7 +1363,8 @@ bool CPyCppyy::CStringConverter::ToMemory(PyObject* value, void* address, PyObje // verify (too long string will cause truncation, no crash) if (fMaxSize != std::string::npos && fMaxSize < (std::string::size_type)len) - PyErr_Warn(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)"); + if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for char array (truncated)", 1) < 0) + return false; // if address is available, and it wasn't set by this converter, assume a byte-wise copy; // otherwise assume a pointer copy (this relies on the converter to be used for properties, @@ -1423,7 +1441,8 @@ bool CPyCppyy::WCStringConverter::ToMemory(PyObject* value, void* address, PyObj // verify (too long string will cause truncation, no crash) if (fMaxSize != std::wstring::npos && fMaxSize < (std::wstring::size_type)len) - PyErr_Warn(PyExc_RuntimeWarning, (char*)"string too long for wchar_t array (truncated)"); + if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for wchar_t array (truncated)", 1) < 0) + return false; Py_ssize_t res = -1; if (fMaxSize != std::wstring::npos) @@ -1482,7 +1501,10 @@ bool CPyCppyy::name##Converter::ToMemory(PyObject* value, void* address, PyObjec \ /* verify (too long string will cause truncation, no crash) */ \ if (fMaxSize != std::wstring::npos && maxbytes < len) { \ - PyErr_Warn(PyExc_RuntimeWarning, (char*)"string too long for "#type" array (truncated)");\ + if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)"string too long for "#type" array (truncated)", 1) < 0) { \ + Py_DECREF(bstr); \ + return false; \ + } \ len = maxbytes; \ } \ \ @@ -1645,6 +1667,78 @@ bool CPyCppyy::VoidArrayConverter::ToMemory(PyObject* value, void* address, PyOb return true; } +#if __cplusplus >= 202002L + +namespace CPyCppyy { + +class StdSpanConverter : public InstanceConverter { +public: + StdSpanConverter(std::string const &typeName, Cppyy::TCppType_t klass, bool keepControl = false) + : InstanceConverter{klass, keepControl}, fTypeName{typeName} + { + } + + ~StdSpanConverter() + { + if (fHasBuffer) { + PyBuffer_Release(&fBufinfo); + } + } + + bool SetArg(PyObject *, Parameter &, CallContext * = nullptr) override; + bool HasState() override { return true; } + +private: + std::string fTypeName; + std::span fBuffer; + bool fHasBuffer = false; + Py_buffer fBufinfo; +}; + +} // namespace CPyCppyy + +//---------------------------------------------------------------------------- +bool CPyCppyy::StdSpanConverter::SetArg(PyObject *pyobject, Parameter ¶, CallContext *ctxt) +{ + auto typecodeFound = Utility::TypecodeMap().find(fTypeName); + +// attempt to get buffer if the C++ type maps to a buffer type + if (typecodeFound == Utility::TypecodeMap().end() || !PyObject_CheckBuffer(pyobject)) { + // Fall back to regular InstanceConverter + return this->InstanceConverter::SetArg(pyobject, para, ctxt); + } + + Py_ssize_t buflen = 0; + char typecode = typecodeFound->second; + memset(&fBufinfo, 0, sizeof(Py_buffer)); + + if (PyObject_GetBuffer(pyobject, &fBufinfo, PyBUF_FORMAT) == 0) { + if (!strchr(fBufinfo.format, typecode)) { + PyErr_Format(PyExc_TypeError, + "buffer has incompatible type: expected '%c' for C++ type '%s', but got format '%s'", typecode, + fTypeName.c_str(), fBufinfo.format ? fBufinfo.format : ""); + PyBuffer_Release(&fBufinfo); + return false; + } + buflen = Utility::GetBuffer(pyobject, typecode, 1, para.fValue.fVoidp, false); + } + +// ok if buffer exists (can't perform any useful size checks) + if (para.fValue.fVoidp && buflen != 0) { + // We assume the layout for any std::span is the same, and just use + // std::span as a placeholder. Not elegant, but works. + fBuffer = std::span{(std::size_t *)para.fValue.fVoidp, static_cast(buflen)}; + fHasBuffer = true; + para.fValue.fVoidp = &fBuffer; + para.fTypeCode = 'V'; + return true; + } + + return false; +} + +#endif // __cplusplus >= 202002L + namespace { // Copy a buffer to memory address with an array converter. @@ -3187,11 +3281,25 @@ bool CPyCppyy::InitializerListConverter::SetArg( #endif } +namespace CPyCppyy { + +// raising converter to take out overloads +class NotImplementedConverter : public Converter { +public: + NotImplementedConverter(PyObject *errorType, std::string const &message) : fErrorType{errorType}, fMessage{message} {} + bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; +private: + PyObject *fErrorType; + std::string fMessage; +}; + +} // namespace CPyCppyy + //---------------------------------------------------------------------------- bool CPyCppyy::NotImplementedConverter::SetArg(PyObject*, Parameter&, CallContext*) { // raise a NotImplemented exception to take a method out of overload resolution - PyErr_SetString(PyExc_NotImplementedError, "this method cannot (yet) be called"); + PyErr_SetString(fErrorType, fMessage.c_str()); return false; } @@ -3257,6 +3365,14 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim const std::string& cpd = TypeManip::compound(resolvedType); std::string realType = TypeManip::clean_type(resolvedType, false, true); +// mutable pointer references (T*&) are incompatible with Python's object model + if (cpd == "*&") { + return new NotImplementedConverter{PyExc_TypeError, + "argument type '" + resolvedType + "' is not supported: non-const references to pointers (T*&) allow a" + " function to replace the pointer itself. Python cannot represent this safely. Consider changing the" + " C++ API to return the new pointer or use a wrapper"}; + } + // accept unqualified type (as python does not know about qualifiers) h = gConvFactories.find((isConst ? "const " : "") + realType + cpd); if (h != gConvFactories.end()) @@ -3342,6 +3458,30 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim } } +#if __cplusplus >= 202002L +//-- special case: std::span + pos = resolvedType.find("span<"); + if (pos == 0 /* no std:: */ || pos == 5 /* with std:: */ || + pos == 6 /* const no std:: */ || pos == 11 /* const with std:: */ ) { + + auto pos1 = realType.find('<'); + auto pos21 = realType.find(','); // for the case there are more template args + auto pos22 = realType.find('>'); + auto len = std::min(pos21 - pos1, pos22 - pos1) - 1; + std::string value_type = realType.substr(pos1+1, len); + + // strip leading "const " + const std::string cprefix = "const "; + if (value_type.compare(0, cprefix.size(), cprefix) == 0) { + value_type = value_type.substr(cprefix.size()); + } + + std::string span_type = "std::span<" + value_type + ">"; + + return new StdSpanConverter{value_type, Cppyy::GetScope(span_type)}; + } +#endif + // converters for known C++ classes and default (void*) Converter* result = nullptr; if (Cppyy::TCppScope_t klass = Cppyy::GetScope(realType)) { @@ -3382,7 +3522,7 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim if (h != gConvFactories.end()) return (h->second)(dims); // else, unhandled moves - result = new NotImplementedConverter(); + result = new NotImplementedConverter{PyExc_NotImplementedError, "this method cannot (yet) be called"}; } if (!result && h != gConvFactories.end()) @@ -3395,7 +3535,8 @@ CPyCppyy::Converter* CPyCppyy::CreateConverter(const std::string& fullType, cdim else if (!cpd.empty()) result = new VoidArrayConverter(); // "user knows best" else - result = new NotImplementedConverter(); // fails on use + // fails on use + result = new NotImplementedConverter{PyExc_NotImplementedError, "this method cannot (yet) be called"}; } return result; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/DeclareConverters.h b/bindings/pyroot/cppyy/CPyCppyy/src/DeclareConverters.h index a390822d811ef..3f8ed377d3bd5 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/DeclareConverters.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/DeclareConverters.h @@ -481,13 +481,6 @@ class InitializerListConverter : public InstanceConverter { size_t fValueSize; }; - -// raising converter to take out overloads -class NotImplementedConverter : public Converter { -public: - bool SetArg(PyObject*, Parameter&, CallContext* = nullptr) override; -}; - } // unnamed namespace } // namespace CPyCppyy diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/DeclareExecutors.h b/bindings/pyroot/cppyy/CPyCppyy/src/DeclareExecutors.h index 888f63be96e63..ebb62c8f73d22 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/DeclareExecutors.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/DeclareExecutors.h @@ -76,6 +76,7 @@ CPPYY_ARRAY_DECL_EXEC(LLong); CPPYY_ARRAY_DECL_EXEC(ULLong); CPPYY_ARRAY_DECL_EXEC(Float); CPPYY_ARRAY_DECL_EXEC(Double); +CPPYY_ARRAY_DECL_EXEC(LDouble); CPPYY_ARRAY_DECL_EXEC(ComplexF); CPPYY_ARRAY_DECL_EXEC(ComplexD); CPPYY_ARRAY_DECL_EXEC(ComplexI); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx index b5f8eb38aed00..63bd3b9e7dc0e 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Dispatcher.cxx @@ -203,7 +203,8 @@ bool CPyCppyy::InsertDispatcher(CPPScope* klass, PyObject* bases, PyObject* dct, if (!Cppyy::HasVirtualDestructor(basetype)) { const std::string& bname = Cppyy::GetScopedFinalName(basetype); - PyErr_Warn(PyExc_RuntimeWarning, (char*)("class \""+bname+"\" has no virtual destructor").c_str()); + if (PyErr_WarnEx(PyExc_RuntimeWarning, (char*)("class \""+bname+"\" has no virtual destructor").c_str(), 1) < 0) + return false; } base_infos.emplace_back( diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Executors.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Executors.cxx index fa0aefa0931fa..8a3648fad8576 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Executors.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Executors.cxx @@ -523,6 +523,7 @@ CPPYY_IMPL_ARRAY_EXEC(LLong, long long, ) CPPYY_IMPL_ARRAY_EXEC(ULLong, unsigned long long, ) CPPYY_IMPL_ARRAY_EXEC(Float, float, ) CPPYY_IMPL_ARRAY_EXEC(Double, double, ) +CPPYY_IMPL_ARRAY_EXEC(LDouble, long double, ) CPPYY_IMPL_ARRAY_EXEC(ComplexF, std::complex, ) CPPYY_IMPL_ARRAY_EXEC(ComplexD, std::complex, ) CPPYY_IMPL_ARRAY_EXEC(ComplexI, std::complex, ) @@ -1037,6 +1038,7 @@ struct InitExecFactories_t { gf["unsigned long long ptr"] = (ef_t)+[](cdims_t d) { return new ULLongArrayExecutor{d}; }; gf["float ptr"] = (ef_t)+[](cdims_t d) { return new FloatArrayExecutor{d}; }; gf["double ptr"] = (ef_t)+[](cdims_t d) { return new DoubleArrayExecutor{d}; }; + gf["long double ptr"] = (ef_t)+[](cdims_t d) { return new LDoubleArrayExecutor{d}; }; gf["std::complex ptr"] = (ef_t)+[](cdims_t d) { return new ComplexFArrayExecutor{d}; }; gf["std::complex ptr"] = (ef_t)+[](cdims_t d) { return new ComplexDArrayExecutor{d}; }; gf["std::complex ptr"] = (ef_t)+[](cdims_t d) { return new ComplexIArrayExecutor{d}; }; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.cxx index 5a4314b1002c2..c71df38b71223 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/LowLevelViews.cxx @@ -565,7 +565,7 @@ static int ll_ass_sub(CPyCppyy::LowLevelView* self, PyObject* key, PyObject* val // rvalue must be an exporter if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) < 0) { - if (src.obj) CPyCppyy_PyBuffer_Release(value, &src); + if (src.obj) PyBuffer_Release(&src); return ret; } @@ -581,7 +581,7 @@ static int ll_ass_sub(CPyCppyy::LowLevelView* self, PyObject* key, PyObject* val dest.len = dest.shape[0] * dest.itemsize; ret = copy_single(&dest, &src); - CPyCppyy_PyBuffer_Release(value, &src); + PyBuffer_Release(&src); return ret; } diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.cxx index 6f2a172fe2985..cec92cbeff02d 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Pythonize.cxx @@ -1871,7 +1871,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) std::ostringstream initdef; initdef << "namespace __cppyy_internal {\n" - << "void init_" << rname << "(" << name << "*& self"; + << "void init_" << rname << "(" << name << "** self"; bool codegen_ok = true; std::vector arg_types, arg_names, arg_defaults; arg_types.reserve(ndata); arg_names.reserve(ndata); arg_defaults.reserve(ndata); @@ -1909,7 +1909,7 @@ bool CPyCppyy::Pythonize(PyObject* pyclass, const std::string& name) initdef << ", " << arg_types[i] << " " << arg_names[i]; if (defaults_ok) initdef << " = " << arg_defaults[i]; } - initdef << ") {\n self = new " << name << "{"; + initdef << ") {\n *self = new " << name << "{"; for (std::vector::size_type i = 0; i < arg_names.size(); ++i) { if (i != 0) initdef << ", "; initdef << arg_names[i]; diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/TemplateProxy.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/TemplateProxy.cxx index 9a9664c0d9a23..12982ddb7e2db 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/TemplateProxy.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/TemplateProxy.cxx @@ -102,7 +102,7 @@ PyObject* TemplateProxy::Instantiate(const std::string& fname, std::string ptrdef; if (PyObject_GetBuffer(itemi, &bufinfo, PyBUF_FORMAT) == 0) { for (int j = 0; j < bufinfo.ndim; ++j) ptrdef += "*"; - CPyCppyy_PyBuffer_Release(itemi, &bufinfo); + PyBuffer_Release(&bufinfo); } else { ptrdef += "*"; PyErr_Clear(); diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Utility.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/Utility.cxx index 11f02dca8e234..7c3e209f31cd8 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Utility.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Utility.cxx @@ -889,6 +889,33 @@ bool CPyCppyy::Utility::InitProxy(PyObject* module, PyTypeObject* pytype, const return true; } +//---------------------------------------------------------------------------- +std::map const &CPyCppyy::Utility::TypecodeMap() +{ + // See https://docs.python.org/3/library/array.html#array.array + static std::map typecodeMap{ + {"char", 'b'}, + {"unsigned char", 'B'}, +#if PY_VERSION_HEX < 0x03100000 + {"wchar_t", 'u'}, +#endif +#if PY_VERSION_HEX >= 0x030d0000 + {"Py_UCS4", 'w'}, +#endif + {"short", 'h'}, + {"unsigned short", 'H'}, + {"int", 'i'}, + {"unsigned int", 'I'}, + {"long", 'l'}, + {"unsigned long", 'L'}, + {"long long", 'q'}, + {"unsigned long long", 'Q'}, + {"float", 'f'}, + {"double", 'd'} + }; + return typecodeMap; +} + //---------------------------------------------------------------------------- Py_ssize_t CPyCppyy::Utility::GetBuffer(PyObject* pyobject, char tc, int size, void*& buf, bool check) { @@ -928,7 +955,7 @@ Py_ssize_t CPyCppyy::Utility::GetBuffer(PyObject* pyobject, char tc, int size, v if (check && bufinfo.itemsize != size) { PyErr_Format(PyExc_TypeError, "buffer itemsize (%ld) does not match expected size (%d)", bufinfo.itemsize, size); - CPyCppyy_PyBuffer_Release(pyobject, &bufinfo); + PyBuffer_Release(&bufinfo); return 0; } @@ -937,17 +964,17 @@ Py_ssize_t CPyCppyy::Utility::GetBuffer(PyObject* pyobject, char tc, int size, v buflen = bufinfo.len/bufinfo.itemsize; else if (buf && bufinfo.ndim == 1) buflen = bufinfo.shape ? bufinfo.shape[0] : bufinfo.len/bufinfo.itemsize; - CPyCppyy_PyBuffer_Release(pyobject, &bufinfo); + PyBuffer_Release(&bufinfo); if (buflen) return buflen; } else { // have buf, but format mismatch: bail out now, otherwise the old // code will return based on itemsize match - CPyCppyy_PyBuffer_Release(pyobject, &bufinfo); + PyBuffer_Release(&bufinfo); return 0; } } else if (bufinfo.obj) - CPyCppyy_PyBuffer_Release(pyobject, &bufinfo); + PyBuffer_Release(&bufinfo); PyErr_Clear(); } @@ -972,7 +999,7 @@ Py_ssize_t CPyCppyy::Utility::GetBuffer(PyObject* pyobject, char tc, int size, v (*(bufprocs->bf_getbuffer))(pyobject, &bufinfo, PyBUF_WRITABLE); buf = (char*)bufinfo.buf; Py_ssize_t buflen = bufinfo.len; - CPyCppyy_PyBuffer_Release(pyobject, &bufinfo); + PyBuffer_Release(&bufinfo); #endif if (buf && check == true) { diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/Utility.h b/bindings/pyroot/cppyy/CPyCppyy/src/Utility.h index 68bee557aaa24..087dac2d456f8 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/Utility.h +++ b/bindings/pyroot/cppyy/CPyCppyy/src/Utility.h @@ -62,6 +62,8 @@ PyObject* FuncPtr2StdFunction(const std::string& retType, const std::string& sig // initialize proxy type objects bool InitProxy(PyObject* module, PyTypeObject* pytype, const char* name); +std::map const &TypecodeMap(); + // retrieve the memory buffer from pyobject, return buflength, tc (optional) is python // array.array type code, size is type size, buf will point to buffer, and if check is // true, some heuristics will be applied to check buffer compatibility with the type diff --git a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx index d0abf7ca35362..896fe55805126 100644 --- a/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx +++ b/bindings/pyroot/cppyy/cppyy-backend/clingwrapper/src/clingwrapper.cxx @@ -1703,7 +1703,7 @@ ptrdiff_t Cppyy::GetBaseOffset(TCppType_t derived, TCppType_t base, std::ostringstream msg; msg << "failed offset calculation between " << cb->GetName() << " and " << cd->GetName(); // TODO: propagate this warning to caller w/o use of Python C-API - // PyErr_Warn(PyExc_RuntimeWarning, const_cast(msg.str().c_str())); + // PyErr_WarnEx(PyExc_RuntimeWarning, const_cast(msg.str().c_str()), 1); std::cerr << "Warning: " << msg.str() << '\n'; } diff --git a/bindings/pyroot/cppyy/cppyy/test/templates.h b/bindings/pyroot/cppyy/cppyy/test/templates.h index 3dc96686617ee..b3c38d2514e42 100644 --- a/bindings/pyroot/cppyy/cppyy/test/templates.h +++ b/bindings/pyroot/cppyy/cppyy/test/templates.h @@ -528,7 +528,7 @@ struct Test {}; using testptr = Test *; template -bool testfun(T const &x) +bool testfun(T x) { return !(bool)x; } diff --git a/bindings/pyroot/cppyy/cppyy/test/test_advancedcpp.py b/bindings/pyroot/cppyy/cppyy/test/test_advancedcpp.py index 0b6ba190a79c0..03307937c8ab1 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_advancedcpp.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_advancedcpp.py @@ -445,10 +445,10 @@ def test09_opaque_pointer_passing(self): #assert o == cppyy.bind_object(cobj, o.__class__) #assert o == cppyy.bind_object(cobj, "some_concrete_class") assert cppyy.addressof(o) == cppyy.addressof(cppyy.bind_object(addr, some_concrete_class)) - assert o == cppyy.bind_object(addr, some_concrete_class) - assert o == cppyy.bind_object(addr, type(o)) - assert o == cppyy.bind_object(addr, o.__class__) - assert o == cppyy.bind_object(addr, "some_concrete_class") + assert o is cppyy.bind_object(addr, some_concrete_class) + assert o is cppyy.bind_object(addr, type(o)) + assert o is cppyy.bind_object(addr, o.__class__) + assert o is cppyy.bind_object(addr, "some_concrete_class") raises(TypeError, cppyy.bind_object, addr, "does_not_exist") raises(TypeError, cppyy.bind_object, addr, 1) @@ -531,13 +531,13 @@ def test12_actual_type(self): b = base_class() d = derived_class() - assert b == b.cycle(b) + assert b is b.cycle(b) assert id(b) == id(b.cycle(b)) - assert b == d.cycle(b) + assert b is d.cycle(b) assert id(b) == id(d.cycle(b)) - assert d == b.cycle(d) + assert d is b.cycle(d) assert id(d) == id(b.cycle(d)) - assert d == d.cycle(d) + assert d is d.cycle(d) assert id(d) == id(d.cycle(d)) assert isinstance(b.cycle(b), base_class) diff --git a/bindings/pyroot/cppyy/cppyy/test/test_crossinheritance.py b/bindings/pyroot/cppyy/cppyy/test/test_crossinheritance.py index caf4996d16c1e..e3699888d0e9c 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_crossinheritance.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_crossinheritance.py @@ -448,6 +448,12 @@ class MyClass4 { # as the C++ side now carries the type of the dispatcher, not the type of # the direct base class with warnings.catch_warnings(record=True) as w: + # ensure warnings are not turned into errors, even if we run python -W error + # The reason why we don't turn warnings into errors instead and just + # catch the exception is that the error would be changed into a + # more uninformative "TypeError: no python-side overrides supported ()" + warnings.simplefilter("default") + class MyPyDerived1(VD.MyClass1): pass # TODO: verify warning is given assert len(w) == 1 @@ -1139,7 +1145,7 @@ def __init__(self): def test26_no_default_ctor(self): """Make sure no default ctor is created if not viable""" - import cppyy, warnings + import cppyy cppyy.cppdef("""namespace no_default_ctor { struct NoDefCtor1 { @@ -1158,7 +1164,11 @@ class NoDefCtor3 { virtual ~NoDefCtor3() {} }; - class Simple {}; }""") + class Simple { + public: + virtual ~Simple() {} + }; + }""") ns = cppyy.gbl.no_default_ctor @@ -1170,18 +1180,16 @@ def __init__(self): with raises(TypeError): PyDerived() - with warnings.catch_warnings(record=True) as w: - class PyDerived(cppyy.multi(kls, ns.Simple)): - def __init__(self): - super(PyDerived, self).__init__() + class PyDerived(cppyy.multi(kls, ns.Simple)): + def __init__(self): + super(PyDerived, self).__init__() with raises(TypeError): PyDerived() - with warnings.catch_warnings(record=True) as w: - class PyDerived(cppyy.multi(ns.Simple, kls)): - def __init__(self): - super(PyDerived, self).__init__() + class PyDerived(cppyy.multi(ns.Simple, kls)): + def __init__(self): + super(PyDerived, self).__init__() with raises(TypeError): PyDerived() diff --git a/bindings/pyroot/cppyy/cppyy/test/test_datatypes.py b/bindings/pyroot/cppyy/cppyy/test/test_datatypes.py index 1404a5d99148d..e66e36529ece9 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_datatypes.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_datatypes.py @@ -644,7 +644,7 @@ def test09_global_ptr(self): d = gbl.get_global_pod() assert gbl.is_global_pod(d) - assert c == d + assert c is d assert id(c) == id(d) e = gbl.CppyyTestPod() @@ -2442,5 +2442,32 @@ class JetFit: public virtual JetFitTag { for i in gbl.make(): assert i.name == "NAME" + def test53_long_double_iterator(self): + """Test covering ROOT GitHub issue + https://github.com/root-project/root/issues/21732 + """ + + import cppyy + + std = cppyy.gbl.std + + N = 5 + A1 = std.array["int", N] + A2 = std.array["long double", N] + + a1 = A1() + a2 = A2() + + for i in range(len(a1)): + a1[i] = i + for i in range(len(a2)): + a2[i] = i + + for i, v in enumerate(a1): + assert v == a1[i] + + for i, v in enumerate(a2): + assert v == a2[i] + if __name__ == "__main__": exit(pytest.main(args=['-v', '-ra', __file__])) diff --git a/bindings/pyroot/cppyy/cppyy/test/test_fragile.py b/bindings/pyroot/cppyy/cppyy/test/test_fragile.py index 2181a6e18068a..b1a07477aa394 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_fragile.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_fragile.py @@ -604,16 +604,18 @@ def test25_cppdef_error_reporting(self): int add42(int i) { return i + 42; } }""") - with warnings.catch_warnings(record=True) as w: + # isolate the warning configuration + with warnings.catch_warnings(): + warnings.simplefilter("error") # turn warnings into errors + # missing return statement - cppyy.cppdef("""\ - namespace fragile { - double add42d(double d) { d + 42.; return d; } - }""") + with pytest.raises(SyntaxWarning) as exc: + cppyy.cppdef("""\ + namespace fragile { + double add42d(double d) { d + 42.; return d; } + }""") - assert len(w) == 1 - assert issubclass(w[-1].category, SyntaxWarning) - assert "return" in str(w[-1].message) + assert "return" in str(exc.value) # mix of error and warning with raises(SyntaxError): diff --git a/bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py b/bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py index 71c1ed63e939a..fa07805b29598 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_lowlevel.py @@ -513,6 +513,24 @@ def test15_templated_arrays_gmpxx(self): g = cppyy.gbl assert g.test15_templated_arrays_gmpxx.vector.value_type[g.std.vector[g.mpz_class]] + def test16_empy_llview_as_argument(self): + """Verify that empty LowLevelViews from nullptr can be used for C++ + function calls. This covers the problem that was reported in + https://github.com/root-project/root/issues/20687. + """ + import cppyy + + cppyy.cppdef(""" + int *getCStyleArray() { return nullptr; } + void takeCStyleArray(int *arr) {} + """) + + ll_view = cppyy.gbl.getCStyleArray() + + assert(len(ll_view) == 0) + + cppyy.gbl.takeCStyleArray(ll_view) + class TestMULTIDIMARRAYS: def setup_class(cls): diff --git a/bindings/pyroot/cppyy/cppyy/test/test_overloads.py b/bindings/pyroot/cppyy/cppyy/test/test_overloads.py index c33049d08da42..4993e8f7be2c8 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_overloads.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_overloads.py @@ -448,5 +448,24 @@ class Test14Functor { assert cppyy.gbl.test14_baz(functor) == 2 # should resolve to baz(std::function) + def test15_disallow_mutable_pointer_references(self): + """Verify that mutable pointer references (T*&) are not allowed as arguments. + """ + + import cppyy + + cppyy.cppdef(""" + struct MyClass { + int val = 0; + }; + + void changePtr(MyClass *& ptr) {} + """) + + ptr = cppyy.gbl.MyClass() + + raises(TypeError, cppyy.gbl.changePtr, ptr) + + if __name__ == "__main__": exit(pytest.main(args=['-sv', '-ra', __file__])) diff --git a/bindings/pyroot/cppyy/cppyy/test/test_regression.py b/bindings/pyroot/cppyy/cppyy/test/test_regression.py index 81c6a74fd0eda..a3cc699609df8 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_regression.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_regression.py @@ -253,7 +253,7 @@ def test11_cobject_addressing(self): a = cppyy.gbl.CObjA() co = cppyy.ll.as_cobject(a) - assert a == cppyy.bind_object(co, 'CObjA') + assert a is cppyy.bind_object(co, 'CObjA') assert a.m_int == 42 assert cppyy.bind_object(co, 'CObjA').m_int == 42 @@ -1114,9 +1114,13 @@ def test38_char16_arrays(self): len(ai.name) == 6 assert ai.name[:len(s)] == s - with warnings.catch_warnings(record=True) as w: - ai.name = u'hellowd' - assert 'too long' in str(w[-1].message) + # isolate the warning configuration + with warnings.catch_warnings(): + warnings.simplefilter("error") # turn warnings into errors + + with pytest.raises(RuntimeWarning) as exc: + ai.name = u'hellowd' + assert 'too long' in str(exc.value) # vector of objects va = cppyy.gbl.std.vector[ns.AxisInformation](N) diff --git a/bindings/pyroot/cppyy/cppyy/test/test_stltypes.py b/bindings/pyroot/cppyy/cppyy/test/test_stltypes.py index 77761e2c7d6b2..3c9ac4c8859dd 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_stltypes.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_stltypes.py @@ -1982,7 +1982,7 @@ def test04_tuple_lifeline(self): assert s1.fInt == 42 assert s2.fInt == 42 - @mark.xfail(strict=True, condition=IS_WINDOWS, reason="The wrong values are read back from the tuple!") + @mark.xfail(run=False, condition=IS_WINDOWS, reason="The wrong values are read back from the tuple!") def test05_tuple_assignment_operator(self): """Check that using std::tuple<>::operator= works. This used to fail because ROOT uses a different type to represent @@ -2216,6 +2216,75 @@ def test01_span_iterators(self): # internally. assert [b for b in s] == l1 + def test02_span_argument_conversions(self): + """ + Test conversion of various Python objects to std::span arguments. + + Covers: + 1) Python proxy spans + 2) NumPy arrays + 3) array.array + 4) Type mismatch errors + 5) std::vector implicit conversion + 6) const std::span behavior + """ + import cppyy + import numpy as np + import array + import pytest + + cppyy.cppdef(""" + #include + #include + + template + size_t sum_span(std::span s) { + size_t total = 0; + for (size_t i = 0; i < s.size(); ++i) + total += (size_t)s[i]; + return total; + } + + template + size_t sum_span_const(std::span s) { + size_t total = 0; + for (size_t i = 0; i < s.size(); ++i) + total += (size_t)s[i]; + return total; + } + """) + + data = [1., 2., 3.] + expected = sum(data) + + # 1) Python proxy span + v = cppyy.gbl.std.vector["double"](data) + s = cppyy.gbl.std.span["double"](v) + assert cppyy.gbl.sum_span["double"](s) == expected + assert cppyy.gbl.sum_span_const["double"](s) == expected + + # 2) NumPy array + np_arr = np.array(data, dtype=np.float64) + assert cppyy.gbl.sum_span["double"](np_arr) == expected + assert cppyy.gbl.sum_span_const["double"](np_arr) == expected + + # 3) array.array + arr = array.array('d', data) + assert cppyy.gbl.sum_span["double"](arr) == expected + assert cppyy.gbl.sum_span_const["double"](arr) == expected + + # 4) Type mismatch → should raise TypeError + np_double = np.array([1.0, 2.0, 3.0], dtype=np.float32) + with pytest.raises(TypeError): + cppyy.gbl.sum_span["double"](np_double) + + # 5) std::vector implicit conversion + v2 = cppyy.gbl.std.vector["double"](data) + assert cppyy.gbl.sum_span["double"](v2) == expected + assert cppyy.gbl.sum_span_const["double"](v2) == expected + + # 6) const span behaves the same (already checked above, but explicit case) + assert cppyy.gbl.sum_span_const["double"](np_arr) == expected if __name__ == "__main__": exit(pytest.main(args=['-v', '-ra', __file__])) diff --git a/bindings/pyroot/cppyy/cppyy/test/test_templates.py b/bindings/pyroot/cppyy/cppyy/test/test_templates.py index 7c2ea9bdfb53a..f5d7904e0b9d0 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_templates.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_templates.py @@ -1133,9 +1133,7 @@ def test33_using_template_argument(self): assert ns.testfun["testptr"](cppyy.bind_object(cppyy.nullptr, ns.Test)) - # TODO: raises TypeError; the problem is that the type is resolved - # from UsingPtr::Test*const& to UsingPtr::Test*& (ie. `const` is lost) - # assert ns.testfun["UsingPtr::testptr"](cppyy.nullptr) + assert ns.testfun["UsingPtr::testptr"](cppyy.nullptr) assert ns.testptr.__name__ == "Test" assert ns.testptr.__cpp_name__ == "UsingPtr::Test*" diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdataframe.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdataframe.py index 5a970b3009800..4d56d6c2c7dd8 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdataframe.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdataframe.py @@ -549,6 +549,7 @@ def _MakeNumpyDataFrame(np_dict): using the keys as column names and the numpy arrays as data. """ import ROOT + from ROOT.libROOTPythonizations import PyObjRefCounterAsStdAny if not isinstance(np_dict, dict): raise RuntimeError("Object not convertible: Python object is not a dictionary.") @@ -558,42 +559,10 @@ def _MakeNumpyDataFrame(np_dict): args = (_make_name_rvec_pair(key, value) for key, value in np_dict.items()) - # How we keep the NumPy arrays around as long as the RDataSource is alive: - # - # 1. Create a new dict with references to the NumPy arrays and take - # ownership of it on the C++ side (Py_INCREF). We use a copy of the - # original dict, because otherwise the caller of _MakeNumpyDataFrame - # can invalidate our cache by mutating the np_dict after the call. - # - # 2. The C++ side gets a deleter std::function, calling Py_DECREF when the - # RDataSource is destroyed. - - def _ensure_deleter_declared(): - # If the function is already known to ROOT, skip declaring again - try: - ROOT.__ROOT_Internal.MakePyDeleter - return - except AttributeError: - pass - - ROOT.gInterpreter.Declare( - r""" - #include - - namespace __ROOT_Internal { - - inline std::function MakePyDeleter(std::uintptr_t ptr) { - PyObject *obj = reinterpret_cast(ptr); - Py_INCREF(obj); - return [obj](){ Py_DECREF(obj); }; - } - - } // namespace __ROOT_Internal - """ - ) - - _ensure_deleter_declared() + # To keep the NumPy arrays around as long as the RDataSource is alive, + # create a new dict with references to the NumPy arrays, and pass a + # reference count to the C++ side via std::any. We use a copy of the + # original dict, because otherwise the caller of _MakeNumpyDataFrame can + # invalidate our cache by mutating the np_dict after the call. - np_dict_copy = dict(**np_dict) - key = id(np_dict_copy) - return ROOT.Internal.RDF.MakeRVecDataFrame(ROOT.__ROOT_Internal.MakePyDeleter(key), *args) + return ROOT.Internal.RDF.MakeRVecDataFrame(PyObjRefCounterAsStdAny(dict(**np_dict)), *args) diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_rooabspdf.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_rooabspdf.py index ec8a6ccf80b36..be5991080c176 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_rooabspdf.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_rooabspdf.py @@ -12,38 +12,7 @@ from ._rooabsreal import RooAbsReal -from ._utils import _kwargs_to_roocmdargs, cpp_signature - - -def _pack_cmd_args(*args, **kwargs): - # Pack command arguments passed into a RooLinkedList. - - import ROOT - - # If the second argument is already a RooLinkedList, do nothing - if len(kwargs) == 0 and len(args) == 1 and isinstance(args[0], ROOT.RooLinkedList): - return args[0] - - # Transform keyword arguments to RooCmdArgs - args, kwargs = _kwargs_to_roocmdargs(*args, **kwargs) - - # All keyword arguments should be transformed now - assert len(kwargs) == 0 - - # Put RooCmdArgs in a RooLinkedList - cmd_list = ROOT.RooLinkedList() - for cmd in args: - if not isinstance(cmd, ROOT.RooCmdArg): - raise TypeError("This function only takes RooFit command arguments.") - cmd_list.Add(cmd) - - # The RooLinkedList passed to functions like fitTo() is expected to be - # non-owning. To make sure that the RooCmdArgs live long enough, we attach - # them as an attribute of the output list, such that the Python reference - # counter doesn't hit zero. - cmd_list._owning_pylist = args - - return cmd_list +from ._utils import _kwargs_to_roocmdargs, cpp_signature, _pack_cmd_args class RooAbsPdf(RooAbsReal): diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_rooabsreal.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_rooabsreal.py index 7017f66e0d21a..46ebfeb4f5467 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_rooabsreal.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_rooabsreal.py @@ -12,7 +12,7 @@ ################################################################################ -from ._utils import _kwargs_to_roocmdargs, cpp_signature +from ._utils import _kwargs_to_roocmdargs, cpp_signature, _pack_cmd_args class RooAbsReal(object): @@ -110,8 +110,7 @@ def createChi2(self, *args, **kwargs): """ import ROOT - args, kwargs = _kwargs_to_roocmdargs(*args, **kwargs) - out = self._createChi2(*args, **kwargs) + out = self._createChi2["RooLinkedList const&"](args[0], _pack_cmd_args(*args[1:], **kwargs)) ROOT.SetOwnership(out, True) return out @@ -126,8 +125,7 @@ def chi2FitTo(self, *args, **kwargs): """ import ROOT - args, kwargs = _kwargs_to_roocmdargs(*args, **kwargs) - out = self._chi2FitTo(*args, **kwargs) + out = self._chi2FitTo["RooLinkedList const&"](args[0], _pack_cmd_args(*args[1:], **kwargs)) ROOT.SetOwnership(out, True) return out diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_utils.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_utils.py index 96a8212a1764e..775eae4da1498 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_utils.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_roofit/_utils.py @@ -53,6 +53,37 @@ def getter(k, v): return args, {} +def _pack_cmd_args(*args, **kwargs): + # Pack command arguments passed into a RooLinkedList. + + import ROOT + + # If the second argument is already a RooLinkedList, do nothing + if len(kwargs) == 0 and len(args) == 1 and isinstance(args[0], ROOT.RooLinkedList): + return args[0] + + # Transform keyword arguments to RooCmdArgs + args, kwargs = _kwargs_to_roocmdargs(*args, **kwargs) + + # All keyword arguments should be transformed now + assert len(kwargs) == 0 + + # Put RooCmdArgs in a RooLinkedList + cmd_list = ROOT.RooLinkedList() + for cmd in args: + if not isinstance(cmd, ROOT.RooCmdArg): + raise TypeError("This function only takes RooFit command arguments.") + cmd_list.Add(cmd) + + # The RooLinkedList passed to functions like fitTo() is expected to be + # non-owning. To make sure that the RooCmdArgs live long enough, we attach + # them as an attribute of the output list, such that the Python reference + # counter doesn't hit zero. + cmd_list._owning_pylist = args + + return cmd_list + + def _dict_to_flat_map(arg_dict, allowed_val_dict): """ Helper function to convert python dict to std::map. diff --git a/bindings/pyroot/pythonizations/src/CPPInstancePyz.cxx b/bindings/pyroot/pythonizations/src/CPPInstancePyz.cxx index 021e0834776b3..639bff19a4069 100644 --- a/bindings/pyroot/pythonizations/src/CPPInstancePyz.cxx +++ b/bindings/pyroot/pythonizations/src/CPPInstancePyz.cxx @@ -9,18 +9,14 @@ * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ +#include "PythonLimitedAPI.h" + // Bindings #include "CPyCppyy/API.h" -#include "../../cppyy/CPyCppyy/src/CPyCppyy.h" -#include "../../cppyy/CPyCppyy/src/CPPInstance.h" -#include "../../cppyy/CPyCppyy/src/CustomPyTypes.h" - #include "PyROOTPythonize.h" #include "TBufferFile.h" -using namespace CPyCppyy; - namespace PyROOT { extern PyObject *gRootModule; } @@ -40,18 +36,18 @@ PyObject *PyROOT::CPPInstanceExpand(PyObject * /*self*/, PyObject *args) PyObject *pybuf = 0, *pyname = 0; if (!PyArg_ParseTuple(args, "O!O!:__expand__", &PyBytes_Type, &pybuf, &PyBytes_Type, &pyname)) return 0; - const char *clname = PyBytes_AS_STRING(pyname); + const char *clname = PyBytes_AsString(pyname); // TBuffer and its derived classes can't write themselves, but can be created // directly from the buffer, so handle them in a special case void *newObj = 0; if (strcmp(clname, "TBufferFile") == 0) { TBufferFile *buf = new TBufferFile(TBuffer::kWrite); - buf->WriteFastArray(PyBytes_AS_STRING(pybuf), PyBytes_GET_SIZE(pybuf)); + buf->WriteFastArray(PyBytes_AsString(pybuf), PyBytes_Size(pybuf)); newObj = buf; } else { // use the PyString macro's to by-pass error checking; do not adopt the buffer, // as the local TBufferFile can go out of scope (there is no copying) - TBufferFile buf(TBuffer::kRead, PyBytes_GET_SIZE(pybuf), PyBytes_AS_STRING(pybuf), kFALSE); + TBufferFile buf(TBuffer::kRead, PyBytes_Size(pybuf), PyBytes_AsString(pybuf), kFALSE); newObj = buf.ReadObjectAny(0); } PyObject *result = CPyCppyy::Instance_FromVoidPtr(newObj, clname, /*python_owns=*/true); @@ -62,7 +58,7 @@ PyObject *PyROOT::CPPInstanceExpand(PyObject * /*self*/, PyObject *args) /// Turn the object proxy instance into a character stream and return for /// pickle, together with the callable object that can restore the stream /// into the object proxy instance. -PyObject *op_reduce(PyObject *self) +PyObject *op_reduce(PyObject *self, PyObject * /*args*/) { // keep a borrowed reference around to the callable function for expanding; // because it is borrowed, it means that there can be no pickling during the @@ -71,13 +67,11 @@ PyObject *op_reduce(PyObject *self) // TBuffer and its derived classes can't write themselves, but can be created // directly from the buffer, so handle them in a special case - static Cppyy::TCppType_t s_bfClass = Cppyy::GetScope("TBufferFile"); - TBufferFile *buff = 0; - Cppyy::TCppType_t selfClass = ((CPPInstance *)self)->ObjectIsA(); - if (selfClass == s_bfClass) { + TBufferFile *buff = nullptr; + std::string className = CPyCppyy::Instance_GetScopedFinalName(self); + if (className == "TBufferFile") { buff = (TBufferFile *)CPyCppyy::Instance_AsVoidPtr(self); } else { - auto className = Cppyy::GetScopedFinalName(selfClass); if (className.find("__cppyy_internal::Dispatcher") == 0) { PyErr_Format(PyExc_IOError, "generic streaming of Python objects whose class derives from a C++ class is not supported. " @@ -92,7 +86,7 @@ PyObject *op_reduce(PyObject *self) // to delete if (s_buff.WriteObjectAny(CPyCppyy::Instance_AsVoidPtr(self), TClass::GetClass(className.c_str())) != 1) { PyErr_Format(PyExc_IOError, "could not stream object of type %s", - Cppyy::GetScopedFinalName(selfClass).c_str()); + className.c_str()); return 0; } buff = &s_buff; @@ -101,13 +95,13 @@ PyObject *op_reduce(PyObject *self) // the buffer contents; use a string for the class name, used when casting // on reading back in (see CPPInstanceExpand defined above) PyObject *res2 = PyTuple_New(2); - PyTuple_SET_ITEM(res2, 0, PyBytes_FromStringAndSize(buff->Buffer(), buff->Length())); - PyTuple_SET_ITEM(res2, 1, PyBytes_FromString(Cppyy::GetScopedFinalName(selfClass).c_str())); + PyTuple_SetItem(res2, 0, PyBytes_FromStringAndSize(buff->Buffer(), buff->Length())); + PyTuple_SetItem(res2, 1, PyBytes_FromString(className.c_str())); PyObject *result = PyTuple_New(2); Py_INCREF(s_expand); - PyTuple_SET_ITEM(result, 0, s_expand); - PyTuple_SET_ITEM(result, 1, res2); + PyTuple_SetItem(result, 0, s_expand); + PyTuple_SetItem(result, 1, res2); return result; } @@ -122,6 +116,6 @@ PyObject *op_reduce(PyObject *self) /// so that it can be injected in CPPInstance PyObject *PyROOT::AddCPPInstancePickling(PyObject * /*self*/, PyObject * /*args*/) { - CPPInstance::ReduceMethod() = op_reduce; + CPyCppyy::Instance_SetReduceMethod((PyCFunction)op_reduce); Py_RETURN_NONE; } diff --git a/bindings/pyroot/pythonizations/src/GenericPyz.cxx b/bindings/pyroot/pythonizations/src/GenericPyz.cxx index 3e5ace48241d5..a1295a9461873 100644 --- a/bindings/pyroot/pythonizations/src/GenericPyz.cxx +++ b/bindings/pyroot/pythonizations/src/GenericPyz.cxx @@ -14,7 +14,6 @@ #include "CPyCppyy/API.h" #include "../../cppyy/CPyCppyy/src/CPyCppyy.h" -#include "../../cppyy/CPyCppyy/src/CPPInstance.h" #include "../../cppyy/CPyCppyy/src/Utility.h" #include "PyROOTPythonize.h" @@ -25,15 +24,6 @@ #include -namespace { - -std::string GetScopedFinalNameFromPyObject(const PyObject *pyobj) -{ - return Cppyy::GetScopedFinalName(((CPyCppyy::CPPInstance *)pyobj)->ObjectIsA()); -} - -} // namespace - using namespace CPyCppyy; // We take as unique identifier the declId of the class to @@ -75,7 +65,7 @@ PyObject *ClingPrintValue(PyObject *self, PyObject * /* args */) gInterpreter->Declare(printerCode.c_str()); } - const std::string className = GetScopedFinalNameFromPyObject(self); + const std::string className = CPyCppyy::Instance_GetScopedFinalName(self); std::string printResult; diff --git a/bindings/pyroot/pythonizations/src/IOHandler.cxx b/bindings/pyroot/pythonizations/src/IOHandler.cxx index 13348c0744857..e59b60f2a6491 100644 --- a/bindings/pyroot/pythonizations/src/IOHandler.cxx +++ b/bindings/pyroot/pythonizations/src/IOHandler.cxx @@ -8,7 +8,7 @@ * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ -#include +#include "PythonLimitedAPI.h" #include #ifdef _MSC_VER // Visual Studio diff --git a/bindings/pyroot/pythonizations/src/PyROOTModule.cxx b/bindings/pyroot/pythonizations/src/PyROOTModule.cxx index dff7f6f09ad5f..d6ba1aa297b6d 100644 --- a/bindings/pyroot/pythonizations/src/PyROOTModule.cxx +++ b/bindings/pyroot/pythonizations/src/PyROOTModule.cxx @@ -15,8 +15,6 @@ // Cppyy #include "CPyCppyy/API.h" -#include "../../cppyy/CPyCppyy/src/CallContext.h" -#include "../../cppyy/CPyCppyy/src/ProxyWrappers.h" // ROOT #include "TInterpreter.h" @@ -42,9 +40,21 @@ PyObject *RegisterConverterAlias(PyObject * /*self*/, PyObject *args) PyObject *name = nullptr; PyObject *target = nullptr; - PyArg_ParseTuple(args, "UU:RegisterConverterAlias", &name, &target); + if (!PyArg_ParseTuple(args, "UU:RegisterConverterAlias", &name, &target)) { + return nullptr; + } - CPyCppyy::RegisterConverterAlias(PyUnicode_AsUTF8(name), PyUnicode_AsUTF8(target)); + const char *nameStr = PyUnicode_AsUTF8AndSize(name, nullptr); + if (!nameStr) { + return nullptr; + } + + const char *targetStr = PyUnicode_AsUTF8AndSize(target, nullptr); + if (!targetStr) { + return nullptr; + } + + CPyCppyy::RegisterConverterAlias(nameStr, targetStr); Py_RETURN_NONE; } @@ -54,9 +64,21 @@ PyObject *RegisterExecutorAlias(PyObject * /*self*/, PyObject *args) PyObject *name = nullptr; PyObject *target = nullptr; - PyArg_ParseTuple(args, "UU:RegisterExecutorAlias", &name, &target); + if (!PyArg_ParseTuple(args, "UU:RegisterExecutorAlias", &name, &target)) { + return nullptr; + } - CPyCppyy::RegisterExecutorAlias(PyUnicode_AsUTF8(name), PyUnicode_AsUTF8(target)); + const char *nameStr = PyUnicode_AsUTF8AndSize(name, nullptr); + if (!nameStr) { + return nullptr; + } + + const char *targetStr = PyUnicode_AsUTF8AndSize(target, nullptr); + if (!targetStr) { + return nullptr; + } + + CPyCppyy::RegisterExecutorAlias(nameStr, targetStr); Py_RETURN_NONE; } @@ -75,7 +97,7 @@ class PyObjRefCounter final { void Reset(PyObject *object) { if (fObject) { - Py_DECREF(fObject); + Py_DecRef(fObject); fObject = nullptr; } if (object) { @@ -122,50 +144,83 @@ PyObject *PyObjRefCounterAsStdAny(PyObject * /*self*/, PyObject *args) /*python_owns=*/true); } +// Helper function to get the pointer to a buffer (heavily simplified copy of +// CPyCppyy::Utility::GetBuffer). +void GetBuffer(PyObject *pyobject, void *&buf) +{ + buf = nullptr; + + // Exclude text-like objects (policy decision) + if (PyBytes_Check(pyobject) || PyUnicode_Check(pyobject)) + return; + + // Fast path: bytearray + if (PyByteArray_CheckExact(pyobject)) { + buf = PyByteArray_AsString(pyobject); + return; + } + + // Buffer protocol + if (PyObject_CheckBuffer(pyobject)) { + // Avoid potential issues with empty sequences + if (PySequence_Check(pyobject) && PySequence_Size(pyobject) == 0) + return; + + Py_buffer view; + if (PyObject_GetBuffer(pyobject, &view, PyBUF_SIMPLE) == 0) { + if (view.buf && view.len > 0) + buf = view.buf; + PyBuffer_Release(&view); + } else { + PyErr_Clear(); + } + } +} + } // namespace PyROOT // Methods offered by the interface static PyMethodDef gPyROOTMethods[] = { - {(char *)"AddCPPInstancePickling", (PyCFunction)PyROOT::AddCPPInstancePickling, METH_NOARGS, - (char *)"Add a custom pickling mechanism for Cppyy Python proxy objects"}, - {(char *)"GetBranchAttr", (PyCFunction)PyROOT::GetBranchAttr, METH_VARARGS, - (char *)"Allow to access branches as tree attributes"}, - {(char *)"AddTClassDynamicCastPyz", (PyCFunction)PyROOT::AddTClassDynamicCastPyz, METH_VARARGS, - (char *)"Cast the void* returned by TClass::DynamicCast to the right type"}, - {(char *)"BranchPyz", (PyCFunction)PyROOT::BranchPyz, METH_VARARGS, - (char *)"Fully enable the use of TTree::Branch from Python"}, - {(char *)"AddPrettyPrintingPyz", (PyCFunction)PyROOT::AddPrettyPrintingPyz, METH_VARARGS, - (char *)"Add pretty printing pythonization"}, - {(char *)"InitApplication", (PyCFunction)PyROOT::RPyROOTApplication::InitApplication, METH_VARARGS, - (char *)"Initialize interactive ROOT use from Python"}, - {(char *)"InstallGUIEventInputHook", (PyCFunction)PyROOT::RPyROOTApplication::InstallGUIEventInputHook, METH_NOARGS, - (char *)"Install an input hook to process GUI events"}, - {(char *)"_CPPInstance__expand__", (PyCFunction)PyROOT::CPPInstanceExpand, METH_VARARGS, - (char *)"Deserialize a pickled object"}, - {(char *)"JupyROOTExecutor", (PyCFunction)JupyROOTExecutor, METH_VARARGS, (char *)"Create JupyROOTExecutor"}, - {(char *)"JupyROOTDeclarer", (PyCFunction)JupyROOTDeclarer, METH_VARARGS, (char *)"Create JupyROOTDeclarer"}, - {(char *)"JupyROOTExecutorHandler_Clear", (PyCFunction)JupyROOTExecutorHandler_Clear, METH_NOARGS, - (char *)"Clear JupyROOTExecutorHandler"}, - {(char *)"JupyROOTExecutorHandler_Ctor", (PyCFunction)JupyROOTExecutorHandler_Ctor, METH_NOARGS, - (char *)"Create JupyROOTExecutorHandler"}, - {(char *)"JupyROOTExecutorHandler_Poll", (PyCFunction)JupyROOTExecutorHandler_Poll, METH_NOARGS, - (char *)"Poll JupyROOTExecutorHandler"}, - {(char *)"JupyROOTExecutorHandler_EndCapture", (PyCFunction)JupyROOTExecutorHandler_EndCapture, METH_NOARGS, - (char *)"End capture JupyROOTExecutorHandler"}, - {(char *)"JupyROOTExecutorHandler_InitCapture", (PyCFunction)JupyROOTExecutorHandler_InitCapture, METH_NOARGS, - (char *)"Init capture JupyROOTExecutorHandler"}, - {(char *)"JupyROOTExecutorHandler_GetStdout", (PyCFunction)JupyROOTExecutorHandler_GetStdout, METH_NOARGS, - (char *)"Get stdout JupyROOTExecutorHandler"}, - {(char *)"JupyROOTExecutorHandler_GetStderr", (PyCFunction)JupyROOTExecutorHandler_GetStderr, METH_NOARGS, - (char *)"Get stderr JupyROOTExecutorHandler"}, - {(char *)"JupyROOTExecutorHandler_Dtor", (PyCFunction)JupyROOTExecutorHandler_Dtor, METH_NOARGS, - (char *)"Destruct JupyROOTExecutorHandler"}, - {(char *)"CPyCppyyRegisterConverterAlias", (PyCFunction)PyROOT::RegisterConverterAlias, METH_VARARGS, - (char *)"Register a custom converter that is a reference to an existing converter"}, - {(char *)"CPyCppyyRegisterExecutorAlias", (PyCFunction)PyROOT::RegisterExecutorAlias, METH_VARARGS, - (char *)"Register a custom executor that is a reference to an existing executor"}, - {(char *)"PyObjRefCounterAsStdAny", (PyCFunction)PyROOT::PyObjRefCounterAsStdAny, METH_VARARGS, - (char *)"Wrap a reference count to any Python object in a std::any for resource management in C++"}, + {"AddCPPInstancePickling", (PyCFunction)PyROOT::AddCPPInstancePickling, METH_NOARGS, + "Add a custom pickling mechanism for Cppyy Python proxy objects"}, + {"GetBranchAttr", (PyCFunction)PyROOT::GetBranchAttr, METH_VARARGS, + "Allow to access branches as tree attributes"}, + {"AddTClassDynamicCastPyz", (PyCFunction)PyROOT::AddTClassDynamicCastPyz, METH_VARARGS, + "Cast the void* returned by TClass::DynamicCast to the right type"}, + {"BranchPyz", (PyCFunction)PyROOT::BranchPyz, METH_VARARGS, + "Fully enable the use of TTree::Branch from Python"}, + {"AddPrettyPrintingPyz", (PyCFunction)PyROOT::AddPrettyPrintingPyz, METH_VARARGS, + "Add pretty printing pythonization"}, + {"InitApplication", (PyCFunction)PyROOT::RPyROOTApplication::InitApplication, METH_VARARGS, + "Initialize interactive ROOT use from Python"}, + {"InstallGUIEventInputHook", (PyCFunction)PyROOT::RPyROOTApplication::InstallGUIEventInputHook, METH_NOARGS, + "Install an input hook to process GUI events"}, + {"_CPPInstance__expand__", (PyCFunction)PyROOT::CPPInstanceExpand, METH_VARARGS, + "Deserialize a pickled object"}, + {"JupyROOTExecutor", (PyCFunction)JupyROOTExecutor, METH_VARARGS, "Create JupyROOTExecutor"}, + {"JupyROOTDeclarer", (PyCFunction)JupyROOTDeclarer, METH_VARARGS, "Create JupyROOTDeclarer"}, + {"JupyROOTExecutorHandler_Clear", (PyCFunction)JupyROOTExecutorHandler_Clear, METH_NOARGS, + "Clear JupyROOTExecutorHandler"}, + {"JupyROOTExecutorHandler_Ctor", (PyCFunction)JupyROOTExecutorHandler_Ctor, METH_NOARGS, + "Create JupyROOTExecutorHandler"}, + {"JupyROOTExecutorHandler_Poll", (PyCFunction)JupyROOTExecutorHandler_Poll, METH_NOARGS, + "Poll JupyROOTExecutorHandler"}, + {"JupyROOTExecutorHandler_EndCapture", (PyCFunction)JupyROOTExecutorHandler_EndCapture, METH_NOARGS, + "End capture JupyROOTExecutorHandler"}, + {"JupyROOTExecutorHandler_InitCapture", (PyCFunction)JupyROOTExecutorHandler_InitCapture, METH_NOARGS, + "Init capture JupyROOTExecutorHandler"}, + {"JupyROOTExecutorHandler_GetStdout", (PyCFunction)JupyROOTExecutorHandler_GetStdout, METH_NOARGS, + "Get stdout JupyROOTExecutorHandler"}, + {"JupyROOTExecutorHandler_GetStderr", (PyCFunction)JupyROOTExecutorHandler_GetStderr, METH_NOARGS, + "Get stderr JupyROOTExecutorHandler"}, + {"JupyROOTExecutorHandler_Dtor", (PyCFunction)JupyROOTExecutorHandler_Dtor, METH_NOARGS, + "Destruct JupyROOTExecutorHandler"}, + {"CPyCppyyRegisterConverterAlias", (PyCFunction)PyROOT::RegisterConverterAlias, METH_VARARGS, + "Register a custom converter that is a reference to an existing converter"}, + {"CPyCppyyRegisterExecutorAlias", (PyCFunction)PyROOT::RegisterExecutorAlias, METH_VARARGS, + "Register a custom executor that is a reference to an existing executor"}, + {"PyObjRefCounterAsStdAny", (PyCFunction)PyROOT::PyObjRefCounterAsStdAny, METH_VARARGS, + "Wrap a reference count to any Python object in a std::any for resource management in C++"}, {NULL, NULL, 0, NULL}}; struct module_state { diff --git a/bindings/pyroot/pythonizations/src/PyROOTPythonize.h b/bindings/pyroot/pythonizations/src/PyROOTPythonize.h index 3034f851ba9d0..460e2844caad2 100644 --- a/bindings/pyroot/pythonizations/src/PyROOTPythonize.h +++ b/bindings/pyroot/pythonizations/src/PyROOTPythonize.h @@ -12,7 +12,7 @@ #ifndef PYROOT_PYTHONIZE_H #define PYROOT_PYTHONIZE_H -#include "Python.h" +#include "PythonLimitedAPI.h" namespace PyROOT { diff --git a/bindings/pyroot/pythonizations/src/PythonLimitedAPI.h b/bindings/pyroot/pythonizations/src/PythonLimitedAPI.h new file mode 100644 index 0000000000000..39ff5c9a4972f --- /dev/null +++ b/bindings/pyroot/pythonizations/src/PythonLimitedAPI.h @@ -0,0 +1,20 @@ +#ifndef ROOT_PythonLimitedAPI_h +#define ROOT_PythonLimitedAPI_h + +// Use what is in the limited API since Python 3.11 if we're building with at +// least Python 3.11. The reason why we can't go back to 3.10 is that that at +// that point, the new buffer interface was not part of the limited API yet. +#if PY_VERSION_HEX >= 0x030B0000 + +// On Windows we can't use the stable ABI yet: it requires linking against a +// different libpython, so as long as we don't build all translation units in +// the ROOT Pythonization library with the stable ABI we should not use it. +#ifndef _WIN32 +#define Py_LIMITED_API 0x030B0000 +#endif + +#endif + +#include + +#endif diff --git a/bindings/pyroot/pythonizations/src/RPyROOTApplication.cxx b/bindings/pyroot/pythonizations/src/RPyROOTApplication.cxx index 2a07320e974e6..b4fcb5e0982a5 100644 --- a/bindings/pyroot/pythonizations/src/RPyROOTApplication.cxx +++ b/bindings/pyroot/pythonizations/src/RPyROOTApplication.cxx @@ -10,7 +10,7 @@ *************************************************************************/ // Bindings -#include "Python.h" +#include "PythonLimitedAPI.h" #include "RPyROOTApplication.h" // ROOT @@ -51,18 +51,24 @@ bool PyROOT::RPyROOTApplication::CreateApplication(int ignoreCmdLineOpts) // Retrieve sys.argv list from Python PyObject *argl = PySys_GetObject("argv"); - if (argl && 0 < PyList_Size(argl)) - argc = (int)PyList_GET_SIZE(argl); + if (argl) { + Py_ssize_t size = PyList_Size(argl); + if (size > 0) + argc = static_cast(size); + } argv = new char *[argc]; + for (int i = 1; i < argc; ++i) { - char *argi = const_cast(PyUnicode_AsUTF8(PyList_GET_ITEM(argl, i))); + PyObject *item = PyList_GetItem(argl, i); + const char *argi = PyUnicode_AsUTF8AndSize(item, nullptr); + if (strcmp(argi, "-") == 0 || strcmp(argi, "--") == 0) { // Stop collecting options, the remaining are for the Python script argc = i; // includes program name break; } - argv[i] = argi; + argv[i] = const_cast(argi); } } @@ -139,7 +145,7 @@ void PyROOT::RPyROOTApplication::InitROOTMessageCallback() /// \param[in] args [0] Boolean that tells whether to ignore the command line options. PyObject *PyROOT::RPyROOTApplication::InitApplication(PyObject * /*self*/, PyObject *args) { - int argc = PyTuple_GET_SIZE(args); + int argc = PyTuple_Size(args); if (argc == 1) { PyObject *ignoreCmdLineOpts = PyTuple_GetItem(args, 0); diff --git a/bindings/pyroot/pythonizations/src/TClassPyz.cxx b/bindings/pyroot/pythonizations/src/TClassPyz.cxx index 426fabf378826..b732aecbc12a4 100644 --- a/bindings/pyroot/pythonizations/src/TClassPyz.cxx +++ b/bindings/pyroot/pythonizations/src/TClassPyz.cxx @@ -13,7 +13,6 @@ #include "CPyCppyy/API.h" #include "../../cppyy/CPyCppyy/src/CPyCppyy.h" -#include "../../cppyy/CPyCppyy/src/CPPInstance.h" #include "../../cppyy/CPyCppyy/src/Utility.h" #include "PyROOTPythonize.h" @@ -23,6 +22,10 @@ using namespace CPyCppyy; +namespace PyROOT{ +void GetBuffer(PyObject *pyobject, void *&buf); +} + // Cast the void* returned by TClass::DynamicCast to the right type PyObject *TClassDynamicCastPyz(PyObject *self, PyObject *args) { @@ -30,9 +33,16 @@ PyObject *TClassDynamicCastPyz(PyObject *self, PyObject *args) PyObject *pyclass = nullptr; PyObject *pyobject = nullptr; int up = 1; - if (!PyArg_ParseTuple(args, "O!O|i:DynamicCast", &CPPInstance_Type, &pyclass, &pyobject, &up)) + if (!PyArg_ParseTuple(args, "OO|i:DynamicCast", &pyclass, &pyobject, &up)) return nullptr; + if (!CPyCppyy::Instance_Check(pyclass)) { + PyErr_Format(PyExc_TypeError, + "DynamicCast argument 1 must be a cppyy instance, got '%.200s'", + Py_TYPE(pyclass)->tp_name); + return nullptr; + } + // Perform actual cast - calls default implementation of DynamicCast TClass *cl1 = (TClass *)CPyCppyy::Instance_AsVoidPtr(self); TClass *cl2 = (TClass *)CPyCppyy::Instance_AsVoidPtr(pyclass); @@ -44,13 +54,12 @@ PyObject *TClassDynamicCastPyz(PyObject *self, PyObject *args) } else if (PyInt_Check(pyobject) || PyLong_Check(pyobject)) { address = (void *)PyLong_AsLongLong(pyobject); } else { - Utility::GetBuffer(pyobject, '*', 1, address, false); + PyROOT::GetBuffer(pyobject, address); } // Now use binding to return a usable class. Upcast: result is a base. // Downcast: result is a derived. - Cppyy::TCppType_t cpptype = ((CPyCppyy::CPPInstance *)(up ? pyclass : self))->ObjectIsA(); - TClass *tcl = TClass::GetClass(Cppyy::GetScopedFinalName(cpptype).c_str()); + TClass *tcl = TClass::GetClass(CPyCppyy::Instance_GetScopedFinalName(up ? pyclass : self).c_str()); TClass *klass = (TClass *)tcl->DynamicCast(TClass::Class(), up ? CPyCppyy::Instance_AsVoidPtr(pyclass) : cl1); return CPyCppyy::Instance_FromVoidPtr(address, klass->GetName()); diff --git a/bindings/pyroot/pythonizations/src/TPyDispatcher.cxx b/bindings/pyroot/pythonizations/src/TPyDispatcher.cxx index bdb01d04eafa4..fa5ba950528f9 100644 --- a/bindings/pyroot/pythonizations/src/TPyDispatcher.cxx +++ b/bindings/pyroot/pythonizations/src/TPyDispatcher.cxx @@ -54,7 +54,7 @@ TPyDispatcher &TPyDispatcher::operator=(const TPyDispatcher &other) if (this != &other) { this->TObject::operator=(other); - Py_XDECREF(fCallable); + Py_DecRef(fCallable); Py_XINCREF(other.fCallable); fCallable = other.fCallable; } @@ -67,7 +67,7 @@ TPyDispatcher &TPyDispatcher::operator=(const TPyDispatcher &other) TPyDispatcher::~TPyDispatcher() { - Py_XDECREF(fCallable); + Py_DecRef(fCallable); } //- public members ----------------------------------------------------------- @@ -93,13 +93,13 @@ PyObject *TPyDispatcher::DispatchVA(const char *format, ...) if (!PyTuple_Check(args)) { // if only one arg ... PyObject *t = PyTuple_New(1); - PyTuple_SET_ITEM(t, 0, args); + PyTuple_SetItem(t, 0, args); args = t; } } PyObject *result = PyObject_CallObject(fCallable, args); - Py_XDECREF(args); + Py_DecRef(args); if (!result) { PyErr_Print(); @@ -136,27 +136,27 @@ PyObject *TPyDispatcher::DispatchVA1(const char *clname, void *obj, const char * if (!PyTuple_Check(args)) { // if only one arg ... PyObject *t = PyTuple_New(2); - PyTuple_SET_ITEM(t, 0, pyobj); - PyTuple_SET_ITEM(t, 1, args); + PyTuple_SetItem(t, 0, pyobj); + PyTuple_SetItem(t, 1, args); args = t; } else { - PyObject *t = PyTuple_New(PyTuple_GET_SIZE(args) + 1); - PyTuple_SET_ITEM(t, 0, pyobj); - for (int i = 0; i < PyTuple_GET_SIZE(args); i++) { - PyObject *item = PyTuple_GET_ITEM(args, i); - Py_INCREF(item); - PyTuple_SET_ITEM(t, i + 1, item); + PyObject *t = PyTuple_New(PyTuple_Size(args) + 1); + PyTuple_SetItem(t, 0, pyobj); + for (int i = 0; i < PyTuple_Size(args); i++) { + PyObject *item = PyTuple_GetItem(args, i); + Py_IncRef(item); + PyTuple_SetItem(t, i + 1, item); } - Py_DECREF(args); + Py_DecRef(args); args = t; } } else { args = PyTuple_New(1); - PyTuple_SET_ITEM(args, 0, pyobj); + PyTuple_SetItem(args, 0, pyobj); } PyObject *result = PyObject_CallObject(fCallable, args); - Py_XDECREF(args); + Py_DecRef(args); if (!result) { PyErr_Print(); @@ -171,12 +171,12 @@ PyObject *TPyDispatcher::DispatchVA1(const char *clname, void *obj, const char * PyObject *TPyDispatcher::Dispatch(TPad *selpad, TObject *selected, Int_t event) { PyObject *args = PyTuple_New(3); - PyTuple_SET_ITEM(args, 0, CPyCppyy::Instance_FromVoidPtr(selpad, "TPad")); - PyTuple_SET_ITEM(args, 1, CPyCppyy::Instance_FromVoidPtr(selected, "TObject")); - PyTuple_SET_ITEM(args, 2, PyLong_FromLong(event)); + PyTuple_SetItem(args, 0, CPyCppyy::Instance_FromVoidPtr(selpad, "TPad")); + PyTuple_SetItem(args, 1, CPyCppyy::Instance_FromVoidPtr(selected, "TObject")); + PyTuple_SetItem(args, 2, PyLong_FromLong(event)); PyObject *result = PyObject_CallObject(fCallable, args); - Py_XDECREF(args); + Py_DecRef(args); if (!result) { PyErr_Print(); @@ -191,13 +191,13 @@ PyObject *TPyDispatcher::Dispatch(TPad *selpad, TObject *selected, Int_t event) PyObject *TPyDispatcher::Dispatch(Int_t event, Int_t x, Int_t y, TObject *selected) { PyObject *args = PyTuple_New(4); - PyTuple_SET_ITEM(args, 0, PyLong_FromLong(event)); - PyTuple_SET_ITEM(args, 1, PyLong_FromLong(x)); - PyTuple_SET_ITEM(args, 2, PyLong_FromLong(y)); - PyTuple_SET_ITEM(args, 3, CPyCppyy::Instance_FromVoidPtr(selected, "TObject")); + PyTuple_SetItem(args, 0, PyLong_FromLong(event)); + PyTuple_SetItem(args, 1, PyLong_FromLong(x)); + PyTuple_SetItem(args, 2, PyLong_FromLong(y)); + PyTuple_SetItem(args, 3, CPyCppyy::Instance_FromVoidPtr(selected, "TObject")); PyObject *result = PyObject_CallObject(fCallable, args); - Py_XDECREF(args); + Py_DecRef(args); if (!result) { PyErr_Print(); @@ -212,12 +212,12 @@ PyObject *TPyDispatcher::Dispatch(Int_t event, Int_t x, Int_t y, TObject *select PyObject *TPyDispatcher::Dispatch(TVirtualPad *pad, TObject *obj, Int_t event) { PyObject *args = PyTuple_New(3); - PyTuple_SET_ITEM(args, 0, CPyCppyy::Instance_FromVoidPtr(pad, "TVirtualPad")); - PyTuple_SET_ITEM(args, 1, CPyCppyy::Instance_FromVoidPtr(obj, "TObject")); - PyTuple_SET_ITEM(args, 2, PyLong_FromLong(event)); + PyTuple_SetItem(args, 0, CPyCppyy::Instance_FromVoidPtr(pad, "TVirtualPad")); + PyTuple_SetItem(args, 1, CPyCppyy::Instance_FromVoidPtr(obj, "TObject")); + PyTuple_SetItem(args, 2, PyLong_FromLong(event)); PyObject *result = PyObject_CallObject(fCallable, args); - Py_XDECREF(args); + Py_DecRef(args); if (!result) { PyErr_Print(); @@ -232,11 +232,11 @@ PyObject *TPyDispatcher::Dispatch(TVirtualPad *pad, TObject *obj, Int_t event) PyObject *TPyDispatcher::Dispatch(TGListTreeItem *item, TDNDData *data) { PyObject *args = PyTuple_New(2); - PyTuple_SET_ITEM(args, 0, CPyCppyy::Instance_FromVoidPtr(item, "TGListTreeItem")); - PyTuple_SET_ITEM(args, 1, CPyCppyy::Instance_FromVoidPtr(data, "TDNDData")); + PyTuple_SetItem(args, 0, CPyCppyy::Instance_FromVoidPtr(item, "TGListTreeItem")); + PyTuple_SetItem(args, 1, CPyCppyy::Instance_FromVoidPtr(data, "TDNDData")); PyObject *result = PyObject_CallObject(fCallable, args); - Py_XDECREF(args); + Py_DecRef(args); if (!result) { PyErr_Print(); @@ -251,11 +251,11 @@ PyObject *TPyDispatcher::Dispatch(TGListTreeItem *item, TDNDData *data) PyObject *TPyDispatcher::Dispatch(const char *name, const TList *attr) { PyObject *args = PyTuple_New(2); - PyTuple_SET_ITEM(args, 0, PyBytes_FromString(name)); - PyTuple_SET_ITEM(args, 1, CPyCppyy::Instance_FromVoidPtr((void *)attr, "TList")); + PyTuple_SetItem(args, 0, PyBytes_FromString(name)); + PyTuple_SetItem(args, 1, CPyCppyy::Instance_FromVoidPtr((void *)attr, "TList")); PyObject *result = PyObject_CallObject(fCallable, args); - Py_XDECREF(args); + Py_DecRef(args); if (!result) { PyErr_Print(); diff --git a/bindings/pyroot/pythonizations/src/TTreePyz.cxx b/bindings/pyroot/pythonizations/src/TTreePyz.cxx index fa96c3f672d7c..e93cb4bb1f898 100644 --- a/bindings/pyroot/pythonizations/src/TTreePyz.cxx +++ b/bindings/pyroot/pythonizations/src/TTreePyz.cxx @@ -13,7 +13,6 @@ #include "../../cppyy/CPyCppyy/src/CPyCppyy.h" #include "../../cppyy/CPyCppyy/src/CPPInstance.h" #include "../../cppyy/CPyCppyy/src/ProxyWrappers.h" -#include "../../cppyy/CPyCppyy/src/Utility.h" #include "../../cppyy/CPyCppyy/src/Dimensions.h" #include "CPyCppyy/API.h" @@ -38,15 +37,19 @@ namespace { // Get the TClass of the C++ object proxied by pyobj -TClass *GetTClass(const PyObject *pyobj) +TClass *GetTClass(PyObject *pyobj) { - return TClass::GetClass(Cppyy::GetScopedFinalName(((CPyCppyy::CPPInstance *)pyobj)->ObjectIsA()).c_str()); + return TClass::GetClass(CPyCppyy::Instance_GetScopedFinalName(pyobj).c_str()); } } // namespace using namespace CPyCppyy; +namespace PyROOT{ +void GetBuffer(PyObject *pyobject, void *&buf); +} + static TBranch *SearchForBranch(TTree *tree, const char *name) { TBranch *branch = tree->GetBranch(name); @@ -180,9 +183,9 @@ PyObject *PyROOT::GetBranchAttr(PyObject * /*self*/, PyObject *args) PyArg_ParseTuple(args, "OU:GetBranchAttr", &self, &pyname); - const char *name_possibly_alias = PyUnicode_AsUTF8(pyname); + const char *name_possibly_alias = PyUnicode_AsUTF8AndSize(pyname, nullptr); if (!name_possibly_alias) - return 0; + return nullptr; // get hold of actual tree auto tree = (TTree *)GetTClass(self)->DynamicCast(TTree::Class(), CPyCppyy::Instance_AsVoidPtr(self)); @@ -205,8 +208,8 @@ PyObject *PyROOT::GetBranchAttr(PyObject * /*self*/, PyObject *args) const auto [finalAddressVoidPtr, finalTypeName] = ResolveBranch(tree, name, branch); if (!finalTypeName.empty()) { PyObject *outTuple = PyTuple_New(2); - PyTuple_SET_ITEM(outTuple, 0, PyLong_FromLongLong((intptr_t)finalAddressVoidPtr)); - PyTuple_SET_ITEM(outTuple, 1, CPyCppyy_PyText_FromString((finalTypeName + "*").c_str())); + PyTuple_SetItem(outTuple, 0, PyLong_FromLongLong((intptr_t)finalAddressVoidPtr)); + PyTuple_SetItem(outTuple, 1, CPyCppyy_PyText_FromString((finalTypeName + "*").c_str())); return outTuple; } } @@ -217,8 +220,8 @@ PyObject *PyROOT::GetBranchAttr(PyObject * /*self*/, PyObject *args) auto wrapper = WrapLeaf(leaf); if (wrapper != nullptr) { PyObject *outTuple = PyTuple_New(2); - PyTuple_SET_ITEM(outTuple, 0, wrapper); - PyTuple_SET_ITEM(outTuple, 1, CPyCppyy_PyText_FromString("")); + PyTuple_SetItem(outTuple, 0, wrapper); + PyTuple_SetItem(outTuple, 1, CPyCppyy_PyText_FromString("")); return outTuple; } } @@ -248,17 +251,25 @@ PyObject *TryBranchLeafListOverload(int argc, PyObject *args) } void *buf = nullptr; - if (CPPInstance_Check(address)) + if (CPyCppyy::Instance_Check(address)) buf = CPyCppyy::Instance_AsVoidPtr(address); else - Utility::GetBuffer(address, '*', 1, buf, false); + PyROOT::GetBuffer(address, buf); if (buf) { TBranch *branch = nullptr; + const char *nameString = PyUnicode_AsUTF8AndSize(name, nullptr); + if (!nameString) { + return nullptr; + } + const char *leaflistString = PyUnicode_AsUTF8AndSize(leaflist, nullptr); + if (!leaflistString) { + return nullptr; + } if (argc == 5) { - branch = tree->Branch(PyUnicode_AsUTF8(name), buf, PyUnicode_AsUTF8(leaflist), PyInt_AS_LONG(bufsize)); + branch = tree->Branch(nameString, buf, leaflistString, PyInt_AsLong(bufsize)); } else { - branch = tree->Branch(PyUnicode_AsUTF8(name), buf, PyUnicode_AsUTF8(leaflist)); + branch = tree->Branch(nameString, buf, leaflistString); } return BindCppObject(branch, Cppyy::GetScope("TBranch")); @@ -302,10 +313,17 @@ PyObject *TryBranchPtrToPtrOverloads(int argc, PyObject *args) return nullptr; } - std::string klName = clName ? PyUnicode_AsUTF8(clName) : ""; + std::string klName; + if (clName) { + const char *clNameString = PyUnicode_AsUTF8AndSize(clName, nullptr); + if (!clNameString) { + return nullptr; + } + klName = clNameString; + } void *buf = nullptr; - if (CPPInstance_Check(address)) { + if (CPyCppyy::Instance_Check(address)) { if (((CPPInstance *)address)->fFlags & CPPInstance::kIsReference) buf = (void *)((CPPInstance *)address)->fObject; else @@ -316,18 +334,25 @@ PyObject *TryBranchPtrToPtrOverloads(int argc, PyObject *args) argc += 1; } } else { - Utility::GetBuffer(address, '*', 1, buf, false); + PyROOT::GetBuffer(address, buf); } if (buf && !klName.empty()) { TBranch *branch = nullptr; + const char *nameString = nullptr; + if (argc == 4 || argc == 5 || argc == 6) { + nameString = PyUnicode_AsUTF8AndSize(name, nullptr); + if (!nameString) { + return nullptr; + } + } if (argc == 4) { - branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf); + branch = tree->Branch(nameString, klName.c_str(), buf); } else if (argc == 5) { - branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf, PyInt_AS_LONG(bufsize)); + branch = tree->Branch(nameString, klName.c_str(), buf, PyInt_AsLong(bufsize)); } else if (argc == 6) { - branch = tree->Branch(PyUnicode_AsUTF8(name), klName.c_str(), buf, PyInt_AS_LONG(bufsize), - PyInt_AS_LONG(splitlevel)); + branch = tree->Branch(nameString, klName.c_str(), buf, PyInt_AsLong(bufsize), + PyInt_AsLong(splitlevel)); } return BindCppObject(branch, Cppyy::GetScope("TBranch")); @@ -359,7 +384,7 @@ PyObject *TryBranchPtrToPtrOverloads(int argc, PyObject *args) /// - ( const char*, T**, Int_t = 32000, Int_t = 99 ) PyObject *PyROOT::BranchPyz(PyObject * /* self */, PyObject *args) { - int argc = PyTuple_GET_SIZE(args); + int argc = PyTuple_Size(args); if (argc >= 3) { // We count the TTree proxy object too auto branch = TryBranchLeafListOverload(argc, args); diff --git a/bindings/pyroot/pythonizations/test/generate_keras_functional.py b/bindings/pyroot/pythonizations/test/generate_keras_functional.py index 11f7bdefda00e..35fd5e6260128 100644 --- a/bindings/pyroot/pythonizations/test/generate_keras_functional.py +++ b/bindings/pyroot/pythonizations/test/generate_keras_functional.py @@ -1,6 +1,9 @@ +import warnings + def generate_keras_functional(dst_dir): import numpy as np + import keras from keras import layers, models from parser_test_function import is_channels_first_supported @@ -16,8 +19,14 @@ def train_and_save(model, name): model.compile(optimizer='adam', loss='mean_squared_error', metrics=['mae']) model.summary() - model.fit(x_train, y_train, epochs=1, verbose=0) - model.save(f"{dst_dir}/Functional_{name}_test.keras") + if len(model.trainable_weights) > 0: + model.fit(x_train, y_train, epochs=1, verbose=0) + + with warnings.catch_warnings(): + # Some object inside TensorFlow/Keras has an outdated __array__ implementation + warnings.filterwarnings("ignore", category=DeprecationWarning, message=".*__array__.*copy keyword.*") + model.save(f"{dst_dir}/Functional_{name}_test.keras") + print("generated and saved functional model",name) @@ -211,7 +220,11 @@ def train_and_save(model, name): sub = layers.Subtract()([d1, d2]) mul = layers.Multiply()([d1, d2]) merged = layers.Concatenate()([add, sub, mul]) - merged = layers.LeakyReLU(alpha=0.1)(merged) + # `alpha` was renamed to `negative_slope` in Keras 3 + if keras.__version__ >= "3.0": + merged = layers.LeakyReLU(negative_slope=0.1)(merged) + else: + merged = layers.LeakyReLU(alpha=0.1)(merged) out = layers.Dense(4, activation="softmax")(merged) model = models.Model([inp1, inp2], out) train_and_save(model, "Layer_Combination_3") diff --git a/bindings/pyroot/pythonizations/test/generate_keras_sequential.py b/bindings/pyroot/pythonizations/test/generate_keras_sequential.py index 40b6c645b1fd4..47d10968b8ca5 100644 --- a/bindings/pyroot/pythonizations/test/generate_keras_sequential.py +++ b/bindings/pyroot/pythonizations/test/generate_keras_sequential.py @@ -1,5 +1,8 @@ +import warnings + def generate_keras_sequential(dst_dir): + import keras import numpy as np from keras import layers, models from parser_test_function import is_channels_first_supported @@ -9,10 +12,15 @@ def train_and_save(model, name): x_train = np.random.rand(32, *model.input_shape[1:]) y_train = np.random.rand(32, *model.output_shape[1:]) model.compile(optimizer='adam', loss='mean_squared_error', metrics=['mae']) - model.fit(x_train, y_train, epochs=1, verbose=0) + if len(model.trainable_weights) > 0: + model.fit(x_train, y_train, epochs=1, verbose=0) model.summary() print("fitting sequential model",name) - model.save(f"{dst_dir}/Sequential_{name}_test.keras") + + with warnings.catch_warnings(): + # Some object inside TensorFlow/Keras has an outdated __array__ implementation + warnings.filterwarnings("ignore", category=DeprecationWarning, message=".*__array__.*copy keyword.*") + model.save(f"{dst_dir}/Sequential_{name}_test.keras") # Binary Ops: Add, Subtract, Multiply are not typical in Sequential - skipping those @@ -183,6 +191,9 @@ def train_and_save(model, name): ]) train_and_save(modelA, "Layer_Combination_1") + # `alpha` was renamed to `negative_slope` in Keras 3 + negative_slope_key = "negative_slope" if keras.__version__ >= "3.0" else "alpha" + modelB = models.Sequential([ layers.Input(shape=(32,32,3)), layers.Conv2D(8, (3,3), padding='valid', data_format='channels_last', activation='relu'), @@ -193,7 +204,7 @@ def train_and_save(model, name): layers.Permute((2, 1)), layers.Flatten(), layers.Dense(32), - layers.LeakyReLU(alpha=0.1), + layers.LeakyReLU(**{negative_slope_key : 0.1}), layers.Dense(10, activation='softmax'), ]) train_and_save(modelB, "Layer_Combination_2") @@ -210,4 +221,4 @@ def train_and_save(model, name): layers.Dense(8, activation='swish'), layers.Dense(3, activation='softmax'), ]) - train_and_save(modelC, "Layer_Combination_3") \ No newline at end of file + train_and_save(modelC, "Layer_Combination_3") diff --git a/bindings/pyroot/pythonizations/test/ml_dataloader.py b/bindings/pyroot/pythonizations/test/ml_dataloader.py index 40ffa0197c8b9..398b32df15d12 100644 --- a/bindings/pyroot/pythonizations/test/ml_dataloader.py +++ b/bindings/pyroot/pythonizations/test/ml_dataloader.py @@ -4725,7 +4725,7 @@ def test14_big_data_replacement_true(self): # max samling strategy to guarantee duplicate sampled entires max_sampling_ratio = entries_in_rdf_minor / entries_in_rdf_major - sampling_ratio = round(uniform(0.01, max_sampling_ratio), 2) + sampling_ratio = round(uniform(0.01, max_sampling_ratio - 0.01), 2) error_message = f"\n Batch size: {batch_size}\ Number of major entries: {entries_in_rdf_major} \ diff --git a/bindings/pyroot/pythonizations/test/parser_test_function.py b/bindings/pyroot/pythonizations/test/parser_test_function.py index eaa4a0ed5fb2f..9232362ed45ba 100644 --- a/bindings/pyroot/pythonizations/test/parser_test_function.py +++ b/bindings/pyroot/pythonizations/test/parser_test_function.py @@ -2,10 +2,6 @@ ''' The test file contains two types of functions: - is_accurate: - - This function checks whether the inference results from SOFIE and Keras are accurate within a specified - tolerance. Since the inference result from Keras is not flattened, the function flattens both tensors before - performing the comparison. generate_and_test_inference: - This function accepts the following inputs: @@ -29,7 +25,7 @@ shape from the model object. - Convert the inference results to NumPy arrays: The SOFIE result is of type vector, and the Keras result is a TensorFlow tensor. Both are converted to - NumPy arrays before being passed to the is_accurate function for comparison. + NumPy arrays before being passed to the np.testing.assert_allclose function for comparison. ''' def is_channels_first_supported() : @@ -42,16 +38,6 @@ def is_channels_first_supported() : return True -def is_accurate(tensor_a, tensor_b, tolerance=1e-2): - tensor_a = tensor_a.flatten() - tensor_b = tensor_b.flatten() - for i in range(len(tensor_a)): - difference = abs(tensor_a[i] - tensor_b[i]) - if difference > tolerance: - print(tensor_a[i], tensor_b[i]) - return False - return True - def generate_and_test_inference(model_file_path: str, generated_header_file_dir: str = None, batch_size=1): import keras @@ -81,7 +67,6 @@ def generate_and_test_inference(model_file_path: str, generated_header_file_dir: sofie_model_namespace = getattr(ROOT, "TMVA_SOFIE_" + model_name) inference_session = sofie_model_namespace.Session(generated_header_file_path.removesuffix(".hxx") + ".dat") keras_model = keras.models.load_model(model_file_path) - keras_model.load_weights(model_file_path) input_tensors = [] for model_input in keras_model.inputs: @@ -91,11 +76,17 @@ def generate_and_test_inference(model_file_path: str, generated_header_file_dir: sofie_inference_result = inference_session.infer(*input_tensors) sofie_output_tensor_shape = list(rmodel.GetTensorShape(rmodel.GetOutputTensorNames()[0])) # get output shape # from SOFIE - keras_inference_result = keras_model(input_tensors) + # Keras explicitly forbids input tensor lists of size 1 + if len(keras_model.inputs) == 1: + keras_inference_result = keras_model(input_tensors[0]) + else: + keras_inference_result = keras_model(input_tensors) if sofie_output_tensor_shape != list(keras_inference_result.shape): raise AssertionError("Output tensor dimensions from SOFIE and Keras do not match") - sofie_inference_result = np.asarray(sofie_inference_result) - keras_inference_result = np.asarray(keras_inference_result) - is_inference_accurate = is_accurate(sofie_inference_result, keras_inference_result) - if not is_inference_accurate: - raise AssertionError("Inference results from SOFIE and Keras do not match") \ No newline at end of file + + np.testing.assert_allclose( + np.asarray(sofie_inference_result).flatten(), + np.asarray(keras_inference_result).flatten(), + atol=1e-2, + rtol=0. # explicitly disable relative tolerance (NumPy uses |a - b| <= atol + rtol * |b|) + ) diff --git a/bindings/tpython/CMakeLists.txt b/bindings/tpython/CMakeLists.txt index cf6374818a673..c48b3250ffba8 100644 --- a/bindings/tpython/CMakeLists.txt +++ b/bindings/tpython/CMakeLists.txt @@ -30,4 +30,15 @@ ROOT_STANDARD_LIBRARY_PACKAGE(ROOTTPython Python3::Python ) +# Use what is in the limited API since Python 3.10, if we build with a lower +# Python version, we don't bother using the limited API. +if (Python3_VERSION VERSION_GREATER_EQUAL "3.10") + # On Windows we can't use the stable ABI yet: it requires linking against a + # different libpython, so as long as we don't build all translation units in + # the ROOT Pythonization library with the stable ABI we should not use it. + if(NOT MSVC) + target_compile_options(ROOTTPython PRIVATE -DPy_LIMITED_API=0x030A0000) + endif() +endif() + ROOT_ADD_TEST_SUBDIRECTORY(test) diff --git a/bindings/tpython/src/TPyArg.cxx b/bindings/tpython/src/TPyArg.cxx index 09ed4f81b2faf..efb0a468fe605 100644 --- a/bindings/tpython/src/TPyArg.cxx +++ b/bindings/tpython/src/TPyArg.cxx @@ -10,7 +10,7 @@ // *************************************************************************/ // Bindings -#include "Python.h" +#include #include "TPyArg.h" @@ -42,7 +42,7 @@ void TPyArg::CallConstructor(PyObject *&pyself, PyObject *pyclass, const std::ve int nArgs = args.size(); PyObject *pyargs = PyTuple_New(nArgs); for (int i = 0; i < nArgs; ++i) - PyTuple_SET_ITEM(pyargs, i, (PyObject *)args[i]); + PyTuple_SetItem(pyargs, i, (PyObject *)args[i]); pyself = PyObject_Call(pyclass, pyargs, NULL); Py_DecRef(pyargs); } @@ -65,7 +65,7 @@ PyObject *TPyArg::CallMethod(PyObject *pymeth, const std::vector &args) int nArgs = args.size(); PyObject *pyargs = PyTuple_New(nArgs); for (int i = 0; i < nArgs; ++i) - PyTuple_SET_ITEM(pyargs, i, (PyObject *)args[i]); + PyTuple_SetItem(pyargs, i, (PyObject *)args[i]); PyObject *result = PyObject_Call(pymeth, pyargs, NULL); Py_DecRef(pyargs); return result; diff --git a/bindings/tpython/src/TPyClassGenerator.cxx b/bindings/tpython/src/TPyClassGenerator.cxx index 622820b6305ea..f95163a07e127 100644 --- a/bindings/tpython/src/TPyClassGenerator.cxx +++ b/bindings/tpython/src/TPyClassGenerator.cxx @@ -9,7 +9,7 @@ // * For the list of contributors see $ROOTSYS/README/CREDITS. * // *************************************************************************/ -#include "Python.h" +#include #include "TPyClassGenerator.h" #include "TPyReturn.h" @@ -26,14 +26,56 @@ #include namespace { - class PyGILRAII { - PyGILState_STATE m_GILState; - public: - PyGILRAII() : m_GILState(PyGILState_Ensure()) { } - ~PyGILRAII() { PyGILState_Release(m_GILState); } - }; + +class PyGILRAII { + PyGILState_STATE m_GILState; + +public: + PyGILRAII() : m_GILState(PyGILState_Ensure()) {} + ~PyGILRAII() { PyGILState_Release(m_GILState); } +}; + +#if (defined(Py_LIMITED_API) || PY_VERSION_HEX < 0x30d00f0) + +// Implementation of PyObject_GetOptionalAttr and +// PyObject_GetOptionalAttrString from +// https://github.com/python/pythoncapi-compat/, since the function is not part +// of the limited API. + +inline int PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) +{ + *result = PyObject_GetAttr(obj, attr_name); + if (*result != NULL) { + return 1; + } + if (!PyErr_Occurred()) { + return 0; + } + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + return 0; + } + return -1; } +inline int PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) +{ + PyObject *name_obj; + int rc; + name_obj = PyUnicode_FromString(attr_name); + if (name_obj == NULL) { + *result = NULL; + return -1; + } + rc = PyObject_GetOptionalAttr(obj, name_obj, result); + Py_DECREF(name_obj); + return rc; +} + +#endif + +} // namespace + //- public members ----------------------------------------------------------- TClass *TPyClassGenerator::GetClass(const char *name, Bool_t load) { @@ -75,26 +117,25 @@ TClass *TPyClassGenerator::GetClass(const char *name, Bool_t load, Bool_t silent PyObject *dct = PyModule_GetDict(mod); keys = PyDict_Keys(dct); - for (int i = 0; i < PyList_GET_SIZE(keys); ++i) { - PyObject *key = PyList_GET_ITEM(keys, i); - Py_IncRef(key); - + for (int i = 0; i < PyList_Size(keys); ++i) { + // borrowed references + PyObject *key = PyList_GetItem(keys, i); PyObject *attr = PyDict_GetItem(dct, key); - Py_IncRef(attr); // TODO: refactor the code below with the class method code if (PyCallable_Check(attr) && !(PyType_Check(attr) || PyObject_HasAttr(attr, bases))) { - std::string func_name = PyUnicode_AsUTF8(key); + const char *func_name = PyUnicode_AsUTF8AndSize(key, nullptr); + if (!func_name) { + Py_DecRef(keys); + Py_DecRef(bases); + return nullptr; // propagate possible error + } // figure out number of variables required -#if PY_VERSION_HEX < 0x30d00f0 - PyObject *func_code = PyObject_GetAttrString(attr, (char *)"func_code"); -#else PyObject *func_code = nullptr; PyObject_GetOptionalAttrString(attr, (char *)"func_code", &func_code); -#endif PyObject *var_names = func_code ? PyObject_GetAttrString(func_code, (char *)"co_varnames") : NULL; - int nVars = var_names ? PyTuple_GET_SIZE(var_names) : 0 /* TODO: probably large number, all default? */; + int nVars = var_names ? PyTuple_Size(var_names) : 0 /* TODO: probably large number, all default? */; if (nVars < 0) nVars = 0; Py_DecRef(var_names); @@ -116,9 +157,6 @@ TClass *TPyClassGenerator::GetClass(const char *name, Bool_t load, Bool_t silent // call dispatch (method or class pointer hard-wired) nsCode << " return TPyReturn(TPyArg::CallMethod((PyObject*)" << std::showbase << (uintptr_t)attr << ", v)); }\n"; } - - Py_DecRef(attr); - Py_DecRef(key); } Py_DecRef(keys); @@ -187,14 +225,18 @@ TClass *TPyClassGenerator::GetClass(const char *name, Bool_t load, Bool_t silent // loop over and add member functions Bool_t hasConstructor = kFALSE, hasDestructor = kFALSE; - for (int i = 0; i < PyList_GET_SIZE(attrs); ++i) { - PyObject *label = PyList_GET_ITEM(attrs, i); + for (int i = 0; i < PyList_Size(attrs); ++i) { + PyObject *label = PyList_GetItem(attrs, i); Py_IncRef(label); PyObject *attr = PyObject_GetAttr(pyclass, label); // collect only member functions (i.e. callable elements in __dict__) if (PyCallable_Check(attr)) { - std::string mtName = PyUnicode_AsUTF8(label); + const char *mtNameCStr = PyUnicode_AsUTF8AndSize(label, nullptr); + if(!mtNameCStr) { + return nullptr; + } + std::string mtName = mtNameCStr; if (mtName == "__del__") { hasDestructor = kTRUE; @@ -213,7 +255,7 @@ TClass *TPyClassGenerator::GetClass(const char *name, Bool_t load, Bool_t silent PyErr_Clear(); // happens for slots; default to 0 arguments int nVars = - var_names ? PyTuple_GET_SIZE(var_names) - 1 /* self */ : 0 /* TODO: probably large number, all default? */; + var_names ? PyTuple_Size(var_names) - 1 /* self */ : 0 /* TODO: probably large number, all default? */; if (nVars < 0) nVars = 0; Py_DecRef(var_names); diff --git a/bindings/tpython/src/TPyReturn.cxx b/bindings/tpython/src/TPyReturn.cxx index a569a669cbb85..b4dd5016a930f 100644 --- a/bindings/tpython/src/TPyReturn.cxx +++ b/bindings/tpython/src/TPyReturn.cxx @@ -9,6 +9,8 @@ // * For the list of contributors see $ROOTSYS/README/CREDITS. * // *************************************************************************/ +#include + // Bindings #include "CPyCppyy/API.h" #include "TPyReturn.h" @@ -131,7 +133,7 @@ TPyReturn::operator const char *() const if (fPyObject == Py_None) // for void returns return 0; - const char *s = PyUnicode_AsUTF8(fPyObject); + const char *s = PyUnicode_AsUTF8AndSize(fPyObject, nullptr); if (PyErr_Occurred()) { PyErr_Print(); return 0; diff --git a/bindings/tpython/src/TPython.cxx b/bindings/tpython/src/TPython.cxx index da5da424e7fac..9c858c9e56e79 100644 --- a/bindings/tpython/src/TPython.cxx +++ b/bindings/tpython/src/TPython.cxx @@ -9,6 +9,8 @@ // * For the list of contributors see $ROOTSYS/README/CREDITS. * // *************************************************************************/ +#include + // Bindings // CPyCppyy.h must be go first, since it includes Python.h, which must be // included before any standard header @@ -134,43 +136,8 @@ Bool_t TPython::Initialize() return true; if (!Py_IsInitialized()) { - wchar_t rootStr[] = L"root"; - wchar_t *argv[] = {rootStr}; - int argc = sizeof(argv) / sizeof(argv[0]); -#if PY_VERSION_HEX < 0x030b0000 - Py_Initialize(); -#else - PyStatus status; - PyConfig config; - - PyConfig_InitPythonConfig(&config); - - status = PyConfig_SetArgv(&config, argc, argv); - if (PyStatus_Exception(status)) { - PyConfig_Clear(&config); - std::cerr << "Error when setting command line arguments." << std::endl; - return false; - } - - status = Py_InitializeFromConfig(&config); - if (PyStatus_Exception(status)) { - PyConfig_Clear(&config); - std::cerr << "Error when initializing Python." << std::endl; - return false; - } - PyConfig_Clear(&config); -#endif - - // try again to see if the interpreter is initialized - if (!Py_IsInitialized()) { - // give up ... - std::cerr << "Error: python has not been intialized; returning." << std::endl; - return false; - } - -#if PY_VERSION_HEX < 0x030b0000 - PySys_SetArgv(argc, argv); -#endif + // Trigger the Python initialization indirectly via CPyCppyy + CPyCppyy::Scope_Check(nullptr); mainThreadState = PyEval_SaveThread(); } @@ -180,17 +147,22 @@ Bool_t TPython::Initialize() PyGILRAII gilRaii; // force loading of the ROOT module - const int ret = PyRun_SimpleString("import ROOT"); - if (ret != 0) { - std::cerr << "Error: import ROOT failed, check your PYTHONPATH environmental variable." << std::endl; + PyObject* rootModule = PyImport_ImportModule("ROOT"); + if (!rootModule) { + PyErr_Print(); return false; } + // to trigger the lazy initialization of the C++ runtime - if (PyRun_SimpleString("ROOT.gInterpreter") != 0) { - std::cerr << "Error: initializing ROOT Python module failed." << std::endl; + PyObject* interpreterAttr = PyObject_GetAttrString(rootModule, "gInterpreter"); + if (!interpreterAttr) { + PyErr_Print(); + Py_DecRef(rootModule); return false; } + Py_DecRef(interpreterAttr); + if (!gMainDict) { // retrieve the main dictionary @@ -200,6 +172,15 @@ Bool_t TPython::Initialize() // alive. The gMainDict is only used in Exec(), ExecScript(), and Eval(), // which should not be called after __main__ is garbage collected anyway. } + + // Inject ROOT into __main__ + if (PyDict_SetItemString(gMainDict, "ROOT", rootModule) != 0) { + PyErr_Print(); + Py_DecRef(rootModule); + return false; + } + + Py_DecRef(rootModule); } // python side class construction, managed by ROOT @@ -400,16 +381,7 @@ Bool_t TPython::Exec(const char *cmd, std::any *result, std::string const &resul } // execute the command - PyObjectRef pyObjectResult{ - PyRun_String(command.str().c_str(), Py_file_input, gMainDict, gMainDict)}; - - // test for error - if (pyObjectResult) { - return true; - } - - PyErr_Print(); - return false; + return CPyCppyy::Exec(command.str()); } //////////////////////////////////////////////////////////////////////////////// @@ -452,7 +424,7 @@ void TPython::Prompt() PyGILRAII gilRaii; // enter i/o interactive mode - PyRun_InteractiveLoop(stdin, "\0"); + CPyCppyy::Prompt(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/builtins/cfitsio/CMakeLists.txt b/builtins/cfitsio/CMakeLists.txt index 607c8106a4eb9..e12c260d15940 100644 --- a/builtins/cfitsio/CMakeLists.txt +++ b/builtins/cfitsio/CMakeLists.txt @@ -17,7 +17,7 @@ if(WIN32 AND NOT CMAKE_GENERATOR MATCHES Ninja) if(winrtdebug) set(CFITSIO_BUILD_COMMAND_FLAGS "--config Debug") else() - set(CFITSIO_BUILD_COMMAND_FLAGS "--config Release") + set(CFITSIO_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") endif() endif() diff --git a/builtins/freetype/CMakeLists.txt b/builtins/freetype/CMakeLists.txt new file mode 100644 index 0000000000000..be5033bfc4e14 --- /dev/null +++ b/builtins/freetype/CMakeLists.txt @@ -0,0 +1,74 @@ +# Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. +# All rights reserved. +# +# For the licensing terms see $ROOTSYS/LICENSE. +# For the list of contributors see $ROOTSYS/README/CREDITS. + +# **PLEASE UPDATE ALSO THE FOLLOWING LINE WHEN UPDATING THE VERSION** +# 22 Mar 2026, https://github.com/freetype/freetype/releases/tag/VER-2-14-3 +set(FREETYPE_VERSION 2.14.3) +set(FREETYPE_HASH "e61b31ab26358b946e767ed7eb7f4bb2e507da1cfefeb7a8861ace7fd5c899a1") + +set(FREETYPE_PREFIX ${CMAKE_BINARY_DIR}/builtins/FREETYPE-prefix) + +# The CMake of FreeType will call the static library differently if the CMAKE_BUILD_TYPE is Debug, bu adding a +# "d" to the name of the file. This is unusual if compared to the behaviour of other ROOT builtins, where the name +# changes only between Linux/macOS and Windows. +if(MSVC) + if(winrtdebug) + set(FREETYPE_POSTFIX d) + endif() + if(NOT CMAKE_GENERATOR MATCHES Ninja) + if(winrtdebug) + set(ROOT_FREETYPE_BUILD_COMMAND_FLAGS "--config Debug") + else() + set(ROOT_FREETYPE_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") + endif() + endif() +else() + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(FREETYPE_POSTFIX d) + endif() +endif() +set(FREETYPE_LIBRARY ${FREETYPE_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}freetype${FREETYPE_POSTFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) + +ExternalProject_Add( + BUILTIN_FREETYPE + URL https://lcgpackages.web.cern.ch/tarFiles/sources/freetype-${FREETYPE_VERSION}.tar.gz + URL_HASH SHA256=${FREETYPE_HASH} + PREFIX ${FREETYPE_PREFIX} + CMAKE_ARGS -G ${CMAKE_GENERATOR} + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=${FREETYPE_PREFIX} + -DCMAKE_INSTALL_LIBDIR=lib + -DFT_DISABLE_BZIP2=TRUE + -DFT_DISABLE_BROTLI=TRUE + -DFT_DISABLE_PNG=TRUE + -DFT_DISABLE_HARFBUZZ=TRUE + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_C_VISIBILITY_PRESET=hidden + -DZLIB_LIBRARY=${ZLIB_LIBRARIES} + -DZLIB_INCLUDE_DIR=${ZLIB_INCLUDE_DIRS} + BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_FREETYPE_BUILD_COMMAND_FLAGS} + INSTALL_COMMAND ${CMAKE_COMMAND} --install . ${ROOT_FREETYPE_BUILD_COMMAND_FLAGS} + LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 + BUILD_IN_SOURCE 0 + BUILD_BYPRODUCTS ${FREETYPE_LIBRARY} + TIMEOUT 600 +) + +add_library(Freetype::Freetype IMPORTED STATIC GLOBAL) + +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(FREETYPE_INCLUDE_DIRS ${FREETYPE_PREFIX}/include/freetype2 PARENT_SCOPE) +set(FREETYPE_LIBRARIES ${FREETYPE_LIBRARY} PARENT_SCOPE) +set(Freetype_FOUND TRUE PARENT_SCOPE) +set(Freetype_VERSION ${FREETYPE_VERSION} PARENT_SCOPE) + +if(builtin_zlib) + add_dependencies(BUILTIN_FREETYPE BUILTIN_ZLIB) +endif() diff --git a/builtins/libgif/CMakeLists.txt b/builtins/libgif/CMakeLists.txt index eed00c1cf53bf..17a83d3729b41 100644 --- a/builtins/libgif/CMakeLists.txt +++ b/builtins/libgif/CMakeLists.txt @@ -19,7 +19,7 @@ if(WIN32 AND NOT CMAKE_GENERATOR MATCHES Ninja) if(winrtdebug) set(ROOT_LIBGIF_BUILD_COMMAND_FLAGS "--config Debug") else() - set(ROOT_LIBGIF_BUILD_COMMAND_FLAGS "--config Release") + set(ROOT_LIBGIF_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") endif() endif() @@ -35,6 +35,7 @@ ExternalProject_Add( -DCMAKE_INSTALL_PREFIX= -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_C_VISIBILITY_PRESET=hidden BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_LIBGIF_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_LIBGIF_BUILD_COMMAND_FLAGS} --target install LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 @@ -48,3 +49,10 @@ set_target_properties(GIF::GIF PROPERTIES IMPORTED_LOCATION ${ROOT_LIBGIF_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ROOT_LIBGIF_PREFIX}/include ) + +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(GIF_INCLUDE_DIRS ${ROOT_LIBGIF_PREFIX}/include PARENT_SCOPE) +set(GIF_LIBRARIES ${ROOT_LIBGIF_LIBRARY} PARENT_SCOPE) +set(GIF_FOUND TRUE PARENT_SCOPE) +set(GIF_VERSION ${ROOT_LIBGIF_VERSION} PARENT_SCOPE) diff --git a/builtins/libgif/libgif_add_cmakelists.patch b/builtins/libgif/libgif_add_cmakelists.patch index 2073443027b3b..2f8c58583e973 100644 --- a/builtins/libgif/libgif_add_cmakelists.patch +++ b/builtins/libgif/libgif_add_cmakelists.patch @@ -1,7 +1,7 @@ --- /dev/null 2026-03-12 08:23:04 +++ CMakeLists.txt 2026-03-12 08:20:11 @@ -0,0 +1,29 @@ -+cmake_minimum_required(VERSION 3.23) ++cmake_minimum_required(VERSION 3.22) +project(LibUngif VERSION 1.0) # The version does not matter here + +add_library(gif-static STATIC diff --git a/builtins/libjpeg/CMakeLists.txt b/builtins/libjpeg/CMakeLists.txt index 472c72cea3b7e..d87c7c8c80dfa 100644 --- a/builtins/libjpeg/CMakeLists.txt +++ b/builtins/libjpeg/CMakeLists.txt @@ -22,7 +22,7 @@ if(WIN32 AND NOT CMAKE_GENERATOR MATCHES Ninja) if(winrtdebug) set(ROOT_LIBJPEG_BUILD_COMMAND_FLAGS "--config Debug") else() - set(ROOT_LIBJPEG_BUILD_COMMAND_FLAGS "--config Release") + set(ROOT_LIBJPEG_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") endif() endif() @@ -38,6 +38,7 @@ ExternalProject_Add( -DCMAKE_INSTALL_LIBDIR=/lib -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_C_VISIBILITY_PRESET=hidden -DENABLE_SHARED=OFF -DWITH_TURBOJPEG=OFF -DWITH_SIMD=OFF @@ -56,3 +57,10 @@ set_target_properties(JPEG::JPEG PROPERTIES IMPORTED_LOCATION ${ROOT_LIBJPEG_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ROOT_LIBJPEG_PREFIX}/include ) + +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(JPEG_INCLUDE_DIRS ${${ROOT_LIBJPEG_PREFIX}/include} PARENT_SCOPE) +set(JPEG_LIBRARIES ${ROOT_LIBJPEG_LIBRARY} PARENT_SCOPE) +set(JPEG_FOUND TRUE PARENT_SCOPE) +set(JPEG_VERSION ${ROOT_LIBJPEG_VERSION} PARENT_SCOPE) diff --git a/builtins/libpng/CMakeLists.txt b/builtins/libpng/CMakeLists.txt index 942a40f039356..36220e9564ba1 100644 --- a/builtins/libpng/CMakeLists.txt +++ b/builtins/libpng/CMakeLists.txt @@ -13,17 +13,32 @@ set(ROOT_LIBPNG_PREFIX ${CMAKE_BINARY_DIR}/builtins/LIBPNG-prefix) # Here we need two cases because the library has two different names on Linux/macOS and Windows if(MSVC) - set(ROOT_LIBPNG_LIBRARY ${ROOT_LIBPNG_PREFIX}/lib/libpng16_static${CMAKE_STATIC_LIBRARY_SUFFIX}) + if(winrtdebug) + set(LIBPNG_POSTFIX d) + endif() + if(NOT CMAKE_GENERATOR MATCHES Ninja) + if(winrtdebug) + set(ROOT_LIBPNG_BUILD_COMMAND_FLAGS "--config Debug") + else() + set(ROOT_LIBPNG_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") + endif() + endif() + set(ROOT_LIBPNG_LIBRARY ${ROOT_LIBPNG_PREFIX}/lib/libpng16_static${LIBPNG_POSTFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) else() set(ROOT_LIBPNG_LIBRARY ${ROOT_LIBPNG_PREFIX}/lib/libpng${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() -if(WIN32 AND NOT CMAKE_GENERATOR MATCHES Ninja) - if(winrtdebug) - set(ROOT_LIBPNG_BUILD_COMMAND_FLAGS "--config Debug") - else() - set(ROOT_LIBPNG_BUILD_COMMAND_FLAGS "--config Release") - endif() +# If zlib is not builtin, the string will be empty, nothing special to be +# done: we rely on libpng CMake to find zlib. +# If zlib is builtin, we need to direct the builtin libpng to it. +# For that, the libpng recommended way (see their CMakeLists) +# is to use the CMake ZLIB_ROOT variable, which has nothing to do with ROOT +# despite the name: it's a standard CMake convention to direct the search of +# the find macro. +# Therefore we set it to the prefix path of the builtin zlib in ROOT's +# build directory. +if(builtin_zlib) + set(ZLIB_ROOT_OPTION "-DZLIB_ROOT=${ROOT_ZLIB_PREFIX}") endif() ExternalProject_Add( @@ -38,10 +53,12 @@ ExternalProject_Add( -DCMAKE_INSTALL_LIBDIR=/lib -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_C_VISIBILITY_PRESET=hidden -DPNG_SHARED=OFF -DPNG_STATIC=ON + -DZLIB_LIBRARY=${ZLIB_LIBRARIES} -DZLIB_INCLUDE_DIR=${ZLIB_INCLUDE_DIRS} - -DZLIB_LIBRARY=$ + ${ZLIB_ROOT_OPTION} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_LIBPNG_BUILD_COMMAND_FLAGS} INSTALL_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_LIBPNG_BUILD_COMMAND_FLAGS} --target install LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 @@ -55,3 +72,14 @@ set_target_properties(PNG::PNG PROPERTIES IMPORTED_LOCATION ${ROOT_LIBPNG_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ROOT_LIBPNG_PREFIX}/include ) + +if(builtin_zlib) + add_dependencies(BUILTIN_LIBPNG BUILTIN_ZLIB) +endif() + +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(PNG_INCLUDE_DIRS ${ROOT_LIBPNG_PREFIX}/include PARENT_SCOPE) +set(PNG_LIBRARIES ${ROOT_LIBPNG_LIBRARY} PARENT_SCOPE) +set(PNG_FOUND TRUE PARENT_SCOPE) +set(PNG_VERSION ${ROOT_LIBPNG_VERSION} PARENT_SCOPE) diff --git a/builtins/lz4/CMakeLists.txt b/builtins/lz4/CMakeLists.txt index d7747968ccade..2fc837f6501c1 100644 --- a/builtins/lz4/CMakeLists.txt +++ b/builtins/lz4/CMakeLists.txt @@ -24,6 +24,13 @@ if(MSVC) -DCMAKE_CXXFLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DCMAKE_CXXFLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE} -DCMAKE_CXXFLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}) + if(NOT CMAKE_GENERATOR MATCHES Ninja) + if(winrtdebug) + set(ROOT_LZ4_BUILD_COMMAND_FLAGS "--config Debug") + else() + set(ROOT_LZ4_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") + endif() + endif() endif() ExternalProject_Add( @@ -33,6 +40,7 @@ ExternalProject_Add( PREFIX ${ROOT_LZ4_PREFIX} SOURCE_SUBDIR "build/cmake" CMAKE_ARGS -G ${CMAKE_GENERATOR} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX= -DCMAKE_INSTALL_LIBDIR=/lib -DCMAKE_POSITION_INDEPENDENT_CODE=ON @@ -41,8 +49,8 @@ ExternalProject_Add( -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON ${lz4_extra_cmake_args} - BUILD_COMMAND ${CMAKE_COMMAND} --build "/" --config $ - INSTALL_COMMAND ${CMAKE_COMMAND} --install "/" --config $ + BUILD_COMMAND ${CMAKE_COMMAND} --build "/" ${ROOT_LZ4_BUILD_COMMAND_FLAGS} + INSTALL_COMMAND ${CMAKE_COMMAND} --install "/" ${ROOT_LZ4_BUILD_COMMAND_FLAGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${ROOT_LZ4_LIBRARY} @@ -57,3 +65,10 @@ add_library(LibLZ4 STATIC IMPORTED GLOBAL) add_library(LZ4::LZ4 ALIAS LibLZ4) target_include_directories(LibLZ4 INTERFACE ${LIBLZ4_INCLUDE_DIR}) set_target_properties(LibLZ4 PROPERTIES IMPORTED_LOCATION ${ROOT_LZ4_LIBRARY}) + +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(LZ4_INCLUDE_DIRS ${LIBLZ4_INCLUDE_DIR} PARENT_SCOPE) +set(LZ4_LIBRARIES ${ROOT_LZ4_LIBRARY} PARENT_SCOPE) +set(LZ4_FOUND TRUE PARENT_SCOPE) +set(LZ4_VERSION ${ROOT_LZ4_VERSION} PARENT_SCOPE) diff --git a/builtins/lzma/CMakeLists.txt b/builtins/lzma/CMakeLists.txt index dbefca08d2e2b..0c10ec273c6a3 100644 --- a/builtins/lzma/CMakeLists.txt +++ b/builtins/lzma/CMakeLists.txt @@ -21,6 +21,13 @@ if(MSVC) -DCMAKE_CXXFLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DCMAKE_CXXFLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE} -DCMAKE_CXXFLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}) + if(NOT CMAKE_GENERATOR MATCHES Ninja) + if(winrtdebug) + set(ROOT_LZMA_BUILD_COMMAND_FLAGS "--config Debug") + else() + set(ROOT_LZMA_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") + endif() + endif() else() if(CMAKE_OSX_SYSROOT) set(liblzma_flags "-isysroot\ ${CMAKE_OSX_SYSROOT}") @@ -37,10 +44,12 @@ ExternalProject_Add( URL_HASH SHA256=${ROOT_LZMA_HASH} PREFIX ${ROOT_LZMA_PREFIX} CMAKE_ARGS -G ${CMAKE_GENERATOR} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX= -DCMAKE_INSTALL_LIBDIR=/lib -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_C_VISIBILITY_PRESET=hidden -DBUILD_SHARED_LIBS=OFF -DENABLE_SCRIPTS=OFF -DXZ_TOOL=OFF @@ -48,8 +57,8 @@ ExternalProject_Add( -DLZMADEC=OFF -DLZMAINFO=OFF ${lzma_extra_cmake_args} - BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $ --target liblzma - INSTALL_COMMAND ${CMAKE_COMMAND} --install . --config $ --component liblzma_Development + BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_LZMA_BUILD_COMMAND_FLAGS} --target liblzma + INSTALL_COMMAND ${CMAKE_COMMAND} --install . ${ROOT_LZMA_BUILD_COMMAND_FLAGS} --component liblzma_Development LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${LIBLZMA_LIBRARIES} @@ -64,3 +73,10 @@ add_library(LibLZMA STATIC IMPORTED GLOBAL) add_library(LibLZMA::LibLZMA ALIAS LibLZMA) target_include_directories(LibLZMA INTERFACE ${LIBLZMA_INCLUDE_DIR}) set_target_properties(LibLZMA PROPERTIES IMPORTED_LOCATION ${LIBLZMA_LIBRARIES}) + +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(LIBLZMA_INCLUDE_DIRS ${LibLZMA_INCLUDE_DIR} PARENT_SCOPE) +set(LIBLZMA_LIBRARIES ${LIBLZMA_LIBRARIES} PARENT_SCOPE) +set(LibLZMA_FOUND TRUE PARENT_SCOPE) +set(LibLZMA_VERSION ${ROOT_LZMA_VERSION} PARENT_SCOPE) diff --git a/builtins/xrootd/CMakeLists.txt b/builtins/xrootd/CMakeLists.txt index 00abd0d93cb77..17c176f7ef61d 100644 --- a/builtins/xrootd/CMakeLists.txt +++ b/builtins/xrootd/CMakeLists.txt @@ -6,7 +6,7 @@ include(ExternalProject) -set(XROOTD_VERSION "5.9.1") +set(XROOTD_VERSION "5.9.2") set(XROOTD_PREFIX ${CMAKE_BINARY_DIR}/XROOTD-prefix) message(STATUS "Downloading and building XROOTD version ${XROOTD_VERSION}") @@ -27,7 +27,7 @@ endif() ExternalProject_Add( BUILTIN_XROOTD URL http://lcgpackages.web.cern.ch/lcgpackages/tarFiles/sources/xrootd-${XROOTD_VERSION}.tar.gz - URL_HASH SHA256=39946509a50e790ab3fcc77ba0f4c9b66abef221262756aa8bb2494f00a0e321 + URL_HASH SHA256=e29edb755d5f728eff0c74f7bd8cec35c954239ea747975eebd9c1e2bd61edb5 INSTALL_DIR ${XROOTD_PREFIX} CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH:STRING=${OPENSSL_PREFIX} diff --git a/builtins/zlib/CMakeLists.txt b/builtins/zlib/CMakeLists.txt index aa5755adb5cf3..182e1242b1594 100644 --- a/builtins/zlib/CMakeLists.txt +++ b/builtins/zlib/CMakeLists.txt @@ -1,140 +1,76 @@ -# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. +# Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. # All rights reserved. # # For the licensing terms see $ROOTSYS/LICENSE. # For the list of contributors see $ROOTSYS/README/CREDITS. -project(ZLIB C) - -set(ZLIB_PUBLIC_HEADERS - zconf.h - zlib.h -) - -set(ZLIB_PRIVATE_HEADERS - crc32.h - deflate.h - gzguts.h - inffast.h - inffixed.h - inflate.h - inftrees.h - trees.h - zutil.h -) - -set(ZLIBCF_PRIVATE_HEADERS - crc32.h - deflate_cf.h - gzguts.h - inffast.h - inffixed.h - inflate.h - inftrees.h - trees.h - zutil.h -) - -set(ZLIB_SOURCES - adler32.c - compress.c - crc32.c - deflate.c - gzclose.c - gzlib.c - gzread.c - gzwrite.c - inflate.c - infback.c - inftrees.c - inffast.c - trees.c - uncompr.c - zutil.c -) - -set(ZLIBCF_SOURCES - adler32_cf.c - compress.c - crc32_cf.c - deflate_cf.c - gzclose.c - gzlib.c - gzread.c - gzwrite.c - inflate.c - infback.c - inftrees.c - inffast.c - trees_cf.c - uncompr.c - zutil.c -) - -unset(ZLIB_FOUND CACHE) -unset(ZLIB_FOUND PARENT_SCOPE) -set(ZLIB_FOUND TRUE CACHE BOOL "" FORCE) - -file(STRINGS zlib.h ZLIB_H REGEX "^#define ZLIB_VERSION \"[^\"]*\"$") -string(REGEX REPLACE "^.*ZLIB_VERSION \"([0-9]+).*$" "\\1" ZLIB_VERSION_MAJOR "${ZLIB_H}") -string(REGEX REPLACE "^.*ZLIB_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" ZLIB_VERSION_MINOR "${ZLIB_H}") -string(REGEX REPLACE "^.*ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" ZLIB_VERSION_PATCH "${ZLIB_H}") -set(ZLIB_VERSION_STRING "${ZLIB_VERSION_MAJOR}.${ZLIB_VERSION_MINOR}.${ZLIB_VERSION_PATCH}") - -set(ZLIB_VERSION_TWEAK "") -if("${ZLIB_H}" MATCHES "ZLIB_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+)") - set(ZLIB_VERSION_TWEAK "${CMAKE_MATCH_1}") - string(APPEND ZLIB_VERSION_STRING ".${ZLIB_VERSION_TWEAK}") -endif() - -set(ZLIB_VERSION ${ZLIB_VERSION_STRING} CACHE INTERNAL "") -set(ZLIB_VERSION_STRING ${ZLIB_VERSION_STRING} CACHE INTERNAL "") - -set(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") -set(ZLIB_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") - -if((CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64|X86_64|aarch64") AND (CMAKE_SYSTEM_NAME MATCHES "Linux")) - # Calling helper to avoid using old unsupported binutils (e.g. with SL6) - # macro is returning extra ${ROOT_DEFINITIONS} used after in ZLIB-CF - root_check_assembler() - # Calling helper to avoid using old unsupported binutils (e.g. with Centos7 - # and native gcc compiler 4.8.5) - # Macros are returning bools SSE_SUPPORT & AVX2_SUPPORT - root_check_sse41() - root_check_avx2() - if(SSE_SUPPORT OR AVX2_SUPPORT) - set(ZLIB_CF TRUE CACHE INTERNAL "") - endif() -endif() - -if(ZLIB_CF) - add_library(ZLIB STATIC ${ZLIB_PUBLIC_HEADERS} ${ZLIBCF_PRIVATE_HEADERS} ${ZLIBCF_SOURCES}) +# **PLEASE UPDATE ALSO THE FOLLOWING LINE WHEN UPDATING THE VERSION** +# 17 Feb 2026, https://github.com/madler/zlib/releases/tag/v1.3.2 +set(ROOT_ZLIB_VERSION 1.3.2) +set(ROOT_ZLIB_HASH "bb329a0a2cd0274d05519d61c667c062e06990d72e125ee2dfa8de64f0119d16") + +set(ROOT_ZLIB_PREFIX ${CMAKE_BINARY_DIR}/builtins/ZLIB-prefix PARENT_SCOPE) +set(ROOT_ZLIB_PREFIX ${CMAKE_BINARY_DIR}/builtins/ZLIB-prefix) + +# Here we need two cases because the library has two different names on Linux/macOS and Windows +if(MSVC) + if(winrtdebug) + set(ZLIB_POSTFIX d) + endif() + if(NOT CMAKE_GENERATOR MATCHES Ninja) + if(winrtdebug) + set(ROOT_ZLIB_BUILD_COMMAND_FLAGS "--config Debug") + else() + set(ROOT_ZLIB_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") + endif() + endif() + set(ROOT_ZLIB_LIBRARY ${ROOT_ZLIB_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}zs${ZLIB_POSTFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}) + # In the MSVC case, we forward the default C++ configuration flags because + # they are changed by ROOT in SetUpWindows.cmake. + set(zlib_extra_cmake_args + -DCMAKE_CXXFLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} + -DCMAKE_CXXFLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE} + -DCMAKE_CXXFLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}) else() - add_library(ZLIB STATIC ${ZLIB_PUBLIC_HEADERS} ${ZLIB_PRIVATE_HEADERS} ${ZLIB_SOURCES}) + set(ROOT_ZLIB_LIBRARY ${ROOT_ZLIB_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}z${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() -set_target_properties(ZLIB PROPERTIES C_VISIBILITY_PRESET hidden POSITION_INDEPENDENT_CODE ON) -target_include_directories(ZLIB INTERFACE $) +ExternalProject_Add( + BUILTIN_ZLIB + URL https://lcgpackages.web.cern.ch/lcgpackages/tarFiles/sources/zlib-${ROOT_ZLIB_VERSION}.tar.gz + URL_HASH SHA256=${ROOT_ZLIB_HASH} + PREFIX ${ROOT_ZLIB_PREFIX} + CMAKE_ARGS -G ${CMAKE_GENERATOR} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX= + -DCMAKE_INSTALL_LIBDIR=/lib + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DCMAKE_CXX_VISIBILITY_PRESET=hidden + -DCMAKE_C_VISIBILITY_PRESET=hidden + -DZLIB_BUILD_TESTING=OFF + -DZLIB_BUILD_SHARED=OFF + ${zlib_extra_cmake_args} + BUILD_COMMAND ${CMAKE_COMMAND} --build . ${ROOT_ZLIB_BUILD_COMMAND_FLAGS} + INSTALL_COMMAND ${CMAKE_COMMAND} --install . ${ROOT_ZLIB_BUILD_COMMAND_FLAGS} + LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 LOG_TEST 1 + BUILD_BYPRODUCTS ${ROOT_ZLIB_LIBRARY} + TIMEOUT 600 +) -if((CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64|X86_64") AND (CMAKE_SYSTEM_NAME MATCHES "Linux")) - target_compile_options(ZLIB PRIVATE -Wno-unused-function -O3 -mpclmul -Wno-attribute-alias ${ROOT_DEFINITIONS}) -else() - if(NOT MSVC) - target_compile_options(ZLIB PRIVATE -O3) - endif() -endif() +set(ZLIB_INCLUDE_DIR ${ROOT_ZLIB_PREFIX}/include) +file(MAKE_DIRECTORY ${ZLIB_INCLUDE_DIR}) +set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS ZLIB::ZLIB) +add_library(ZLIB STATIC IMPORTED GLOBAL) add_library(ZLIB::ZLIB ALIAS ZLIB) +target_include_directories(ZLIB INTERFACE ${ZLIB_INCLUDE_DIR}) +set_target_properties(ZLIB PROPERTIES IMPORTED_LOCATION ${ROOT_ZLIB_LIBRARY}) -set(ZLIB_LIBRARY $ CACHE INTERNAL FORCE "") -set(ZLIB_LIBRARIES ZLIB::ZLIB CACHE INTERNAL "") - -if(DEFINED ZLIB_LIBRARY_DEBUG) - set(ZLIB_LIBRARY_DEBUG ${ZLIB_LIBRARY} CACHE INTERNAL "") -endif() +add_dependencies(ZLIB BUILTIN_ZLIB) -if(DEFINED ZLIB_LIBRARY_RELEASE) - set(ZLIB_LIBRARY_RELEASE ${ZLIB_LIBRARY} CACHE INTERNAL "") -endif() - -set_property(GLOBAL APPEND PROPERTY ROOT_BUILTIN_TARGETS ZLIB::ZLIB) +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(ZLIB_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} PARENT_SCOPE) +set(ZLIB_LIBRARIES ${ROOT_ZLIB_LIBRARY} PARENT_SCOPE) +set(ZLIB_FOUND TRUE PARENT_SCOPE) +set(ZLIB_VERSION ${ROOT_ZLIB_VERSION} PARENT_SCOPE) diff --git a/builtins/zlib/adler32.c b/builtins/zlib/adler32.c deleted file mode 100644 index eeadf98d089c9..0000000000000 --- a/builtins/zlib/adler32.c +++ /dev/null @@ -1,167 +0,0 @@ -/* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-2011 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#include "zutil.h" - -#define local static - -local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); - -#define BASE 65521 /* largest prime smaller than 65536 */ -#define NMAX 5552 -/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ - -#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} -#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); -#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); -#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); -#define DO16(buf) DO8(buf,0); DO8(buf,8); - -/* use NO_DIVIDE if your processor does not do division in hardware -- - try it both ways to see which is faster */ -#ifdef NO_DIVIDE -/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 - (thank you to John Reiser for pointing this out) */ -# define CHOP(a) \ - do { \ - unsigned long tmp = a >> 16; \ - a &= 0xffffUL; \ - a += (tmp << 4) - tmp; \ - } while (0) -# define MOD28(a) \ - do { \ - CHOP(a); \ - if (a >= BASE) a -= BASE; \ - } while (0) -# define MOD(a) \ - do { \ - CHOP(a); \ - MOD28(a); \ - } while (0) -# define MOD63(a) \ - do { /* this assumes a is not negative */ \ - z_off64_t tmp = a >> 32; \ - a &= 0xffffffffL; \ - a += (tmp << 8) - (tmp << 5) + tmp; \ - tmp = a >> 16; \ - a &= 0xffffL; \ - a += (tmp << 4) - tmp; \ - tmp = a >> 16; \ - a &= 0xffffL; \ - a += (tmp << 4) - tmp; \ - if (a >= BASE) a -= BASE; \ - } while (0) -#else -# define MOD(a) a %= BASE -# define MOD28(a) a %= BASE -# define MOD63(a) a %= BASE -#endif - -/* ========================================================================= */ -uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) -{ - unsigned long sum2; - unsigned n; - - /* split Adler-32 into component sums */ - sum2 = (adler >> 16) & 0xffff; - adler &= 0xffff; - - /* in case user likes doing a byte at a time, keep it fast */ - if (len == 1) { - adler += buf[0]; - if (adler >= BASE) - adler -= BASE; - sum2 += adler; - if (sum2 >= BASE) - sum2 -= BASE; - return adler | (sum2 << 16); - } - - /* initial Adler-32 value (deferred check for len == 1 speed) */ - if (buf == Z_NULL) - return 1L; - - /* in case short lengths are provided, keep it somewhat fast */ - if (len < 16) { - while (len--) { - adler += *buf++; - sum2 += adler; - } - if (adler >= BASE) - adler -= BASE; - MOD28(sum2); /* only added so many BASE's */ - return adler | (sum2 << 16); - } - - /* do length NMAX blocks -- requires just one modulo operation */ - while (len >= NMAX) { - len -= NMAX; - n = NMAX / 16; /* NMAX is divisible by 16 */ - do { - DO16(buf); /* 16 sums unrolled */ - buf += 16; - } while (--n); - MOD(adler); - MOD(sum2); - } - - /* do remaining bytes (less than NMAX, still just one modulo) */ - if (len) { /* avoid modulos if none remaining */ - while (len >= 16) { - len -= 16; - DO16(buf); - buf += 16; - } - while (len--) { - adler += *buf++; - sum2 += adler; - } - MOD(adler); - MOD(sum2); - } - - /* return recombined sums */ - return adler | (sum2 << 16); -} - -/* ========================================================================= */ -local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) -{ - unsigned long sum1; - unsigned long sum2; - unsigned rem; - - /* for negative len, return invalid adler32 as a clue for debugging */ - if (len2 < 0) - return 0xffffffffUL; - - /* the derivation of this formula is left as an exercise for the reader */ - MOD63(len2); /* assumes len2 >= 0 */ - rem = (unsigned)len2; - sum1 = adler1 & 0xffff; - sum2 = rem * sum1; - MOD(sum2); - sum1 += (adler2 & 0xffff) + BASE - 1; - sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; - if (sum1 >= BASE) sum1 -= BASE; - if (sum1 >= BASE) sum1 -= BASE; - if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); - if (sum2 >= BASE) sum2 -= BASE; - return sum1 | (sum2 << 16); -} - -/* ========================================================================= */ -uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) -{ - return adler32_combine_(adler1, adler2, len2); -} - -uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) -{ - return adler32_combine_(adler1, adler2, len2); -} diff --git a/builtins/zlib/adler32_cf.c b/builtins/zlib/adler32_cf.c deleted file mode 100644 index 7cbf8a030a941..0000000000000 --- a/builtins/zlib/adler32_cf.c +++ /dev/null @@ -1,467 +0,0 @@ -/* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-2011 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - - -#include "zutil.h" - -#if defined(_MSC_VER) - /* Microsoft C/C++-compatible compiler */ - #include -#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) - /* GCC-compatible compiler, targeting x86/x86-64 */ - #include -#elif defined(__GNUC__) && defined(__ARM_NEON__) - /* GCC-compatible compiler, targeting ARM with NEON */ - #include -#elif defined(__GNUC__) && defined(__IWMMXT__) - /* GCC-compatible compiler, targeting ARM with WMMX */ - #include -#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__)) - /* XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX */ - #include -#elif defined(__GNUC__) && defined(__SPE__) - /* GCC-compatible compiler, targeting PowerPC with SPE */ - #include -#elif defined(__clang__) && defined(__linux__) - #include -#endif - -#if defined (__x86_64__) && defined (__linux__) -#include "cpuid.h" -#endif - -// from cpuid.h -#ifndef bit_AVX2 -#define bit_AVX2 0x00000020 -#endif - -#ifndef bit_SSE4_2 -# define bit_SSE4_2 0x100000 -#endif - -static uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); - -#define BASE 65521 /* largest prime smaller than 65536 */ -#define NMAX 5552 -/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ - -/* - * As we are using _signed_ integer arithmetic for the SSE/AVX2 implementations, - * we consider the max as 2^31-1 - */ -#define NMAX_VEC 5552 - -#define NMAX_VEC2 5552 - -#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} -#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); -#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); -#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); -#define DO16(buf) DO8(buf,0); DO8(buf,8); - -/* use NO_DIVIDE if your processor does not do division in hardware -- - try it both ways to see which is faster */ -#ifdef NO_DIVIDE -/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 - (thank you to John Reiser for pointing this out) */ -# define CHOP(a) \ - do { \ - unsigned long tmp = a >> 16; \ - a &= 0xffffUL; \ - a += (tmp << 4) - tmp; \ - } while (0) -# define MOD28(a) \ - do { \ - CHOP(a); \ - if (a >= BASE) a -= BASE; \ - } while (0) -# define MOD(a) \ - do { \ - CHOP(a); \ - MOD28(a); \ - } while (0) -# define MOD63(a) \ - do { /* this assumes a is not negative */ \ - z_off64_t tmp = a >> 32; \ - a &= 0xffffffffL; \ - a += (tmp << 8) - (tmp << 5) + tmp; \ - tmp = a >> 16; \ - a &= 0xffffL; \ - a += (tmp << 4) - tmp; \ - tmp = a >> 16; \ - a &= 0xffffL; \ - a += (tmp << 4) - tmp; \ - if (a >= BASE) a -= BASE; \ - } while (0) -#else -# define MOD(a) a %= BASE -# define MOD28(a) a %= BASE -# define MOD63(a) a %= BASE -#endif - -/* ========================================================================= */ -uLong ZEXPORT adler32_default(uLong adler, const Bytef *buf, uInt len) -{ - unsigned long sum2; - unsigned n; - - /* split Adler-32 into component sums */ - sum2 = (adler >> 16) & 0xffff; - adler &= 0xffff; - - /* in case user likes doing a byte at a time, keep it fast */ - if (len == 1) { - adler += buf[0]; - if (adler >= BASE) - adler -= BASE; - sum2 += adler; - if (sum2 >= BASE) - sum2 -= BASE; - return adler | (sum2 << 16); - } - - /* initial Adler-32 value (deferred check for len == 1 speed) */ - if (buf == Z_NULL) - return 1L; - - /* in case short lengths are provided, keep it somewhat fast */ - if (len < 16) { - while (len--) { - adler += *buf++; - sum2 += adler; - } - if (adler >= BASE) - adler -= BASE; - MOD28(sum2); /* only added so many BASE's */ - return adler | (sum2 << 16); - } - - /* do length NMAX blocks -- requires just one modulo operation */ - while (len >= NMAX) { - len -= NMAX; - n = NMAX / 16; /* NMAX is divisible by 16 */ - do { - DO16(buf); /* 16 sums unrolled */ - buf += 16; - } while (--n); - MOD(adler); - MOD(sum2); - } - - /* do remaining bytes (less than NMAX, still just one modulo) */ - if (len) { /* avoid modulos if none remaining */ - while (len >= 16) { - len -= 16; - DO16(buf); - buf += 16; - } - while (len--) { - adler += *buf++; - sum2 += adler; - } - MOD(adler); - MOD(sum2); - } - - /* return recombined sums */ - return adler | (sum2 << 16); -} - -#if defined (__x86_64__) && defined (__linux__) && ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) || (__clang__)) - -#ifdef _MSC_VER - -/* MSC doesn't have __builtin_expect. Just ignore likely/unlikely and - hope the compiler optimizes for the best. -*/ -#define likely(x) (x) -#define unlikely(x) (x) - -#else - -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) - -#endif - -/* ========================================================================= */ - __attribute__ ((target ("sse4.2"))) -uLong ZEXPORT adler32_sse42(uLong adler, const Bytef *buf, uInt len) -{ - unsigned long sum2; - - /* split Adler-32 into component sums */ - sum2 = (adler >> 16) & 0xffff; - adler &= 0xffff; - - /* in case user likes doing a byte at a time, keep it fast */ - if (unlikely(len == 1)) { - adler += buf[0]; - if (adler >= BASE) - adler -= BASE; - sum2 += adler; - if (sum2 >= BASE) - sum2 -= BASE; - return adler | (sum2 << 16); - } - - /* initial Adler-32 value (deferred check for len == 1 speed) */ - if (unlikely(buf == Z_NULL)) - return 1L; - - /* in case short lengths are provided, keep it somewhat fast */ - if (unlikely(len < 16)) { - while (len--) { - adler += *buf++; - sum2 += adler; - } - if (adler >= BASE) - adler -= BASE; - MOD28(sum2); /* only added so many BASE's */ - return adler | (sum2 << 16); - } - - uint32_t __attribute__ ((aligned(16))) s1[4], s2[4]; - s1[0] = s1[1] = s1[2] = 0; s1[3] = adler; - s2[0] = s2[1] = s2[2] = 0; s2[3] = sum2; - char __attribute__ ((aligned(16))) dot1[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - __m128i dot1v = _mm_load_si128((__m128i*)dot1); - char __attribute__ ((aligned(16))) dot2[16] = {16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; - __m128i dot2v = _mm_load_si128((__m128i*)dot2); - short __attribute__ ((aligned(16))) dot3[8] = {1, 1, 1, 1, 1, 1, 1, 1}; - __m128i dot3v = _mm_load_si128((__m128i*)dot3); - // We will need to multiply by - //char __attribute__ ((aligned(16))) shift[4] = {0, 0, 0, 4}; //{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}; - char __attribute__ ((aligned(16))) shift[16] = {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - __m128i shiftv = _mm_load_si128((__m128i*)shift); - while (len >= 16) { - __m128i vs1 = _mm_load_si128((__m128i*)s1); - __m128i vs2 = _mm_load_si128((__m128i*)s2); - __m128i vs1_0 = vs1; - int k = (len < NMAX_VEC ? (int)len : NMAX_VEC); - k -= k % 16; - len -= k; - while (k >= 16) { - /* - vs1 = adler + sum(c[i]) - vs2 = sum2 + 16 vs1 + sum( (16-i+1) c[i] ) - - NOTE: 256-bit equivalents are: - _mm256_maddubs_epi16 <- operates on 32 bytes to 16 shorts - _mm256_madd_epi16 <- Sums 16 shorts to 8 int32_t. - We could rewrite the below to use 256-bit instructions instead of 128-bit. - */ - __m128i vbuf = _mm_loadu_si128((__m128i*)buf); - buf += 16; - k -= 16; - __m128i v_short_sum1 = _mm_maddubs_epi16(vbuf, dot1v); // multiply-add, resulting in 8 shorts. - __m128i vsum1 = _mm_madd_epi16(v_short_sum1, dot3v); // sum 8 shorts to 4 int32_t; - __m128i v_short_sum2 = _mm_maddubs_epi16(vbuf, dot2v); - vs1 = _mm_add_epi32(vsum1, vs1); - __m128i vsum2 = _mm_madd_epi16(v_short_sum2, dot3v); - vs1_0 = _mm_sll_epi32(vs1_0, shiftv); - vsum2 = _mm_add_epi32(vsum2, vs2); - vs2 = _mm_add_epi32(vsum2, vs1_0); - vs1_0 = vs1; - } - // At this point, we have partial sums stored in vs1 and vs2. There are AVX512 instructions that - // would allow us to sum these quickly (VP4DPWSSD). For now, just unpack and move on. - uint32_t __attribute__((aligned(16))) s1_unpack[4]; - uint32_t __attribute__((aligned(16))) s2_unpack[4]; - _mm_store_si128((__m128i*)s1_unpack, vs1); - _mm_store_si128((__m128i*)s2_unpack, vs2); - adler = (s1_unpack[0] % BASE) + (s1_unpack[1] % BASE) + (s1_unpack[2] % BASE) + (s1_unpack[3] % BASE); - MOD(adler); - s1[3] = adler; - sum2 = (s2_unpack[0] % BASE) + (s2_unpack[1] % BASE) + (s2_unpack[2] % BASE) + (s2_unpack[3] % BASE); - MOD(sum2); - s2[3] = sum2; - } - - while (len--) { - adler += *buf++; - sum2 += adler; - } - MOD(adler); - MOD(sum2); - - /* return recombined sums */ - return adler | (sum2 << 16); -} -#ifndef ROOT_NO_AVX2 -/* ========================================================================= */ -__attribute__ ((target ("avx2"))) -uLong ZEXPORT adler32_avx2(uLong adler, const Bytef *buf, uInt len) -{ - unsigned long sum2; - - /* split Adler-32 into component sums */ - sum2 = (adler >> 16) & 0xffff; - adler &= 0xffff; - - /* in case user likes doing a byte at a time, keep it fast */ - if (unlikely(len == 1)) { - adler += buf[0]; - if (adler >= BASE) - adler -= BASE; - sum2 += adler; - if (sum2 >= BASE) - sum2 -= BASE; - return adler | (sum2 << 16); - } - - /* initial Adler-32 value (deferred check for len == 1 speed) */ - if (unlikely(buf == Z_NULL)) - return 1L; - - /* in case short lengths are provided, keep it somewhat fast */ - if (unlikely(len < 32)) { - while (len--) { - adler += *buf++; - sum2 += adler; - } - if (adler >= BASE) - adler -= BASE; - MOD28(sum2); /* only added so many BASE's */ - return adler | (sum2 << 16); - } - - uint32_t __attribute__ ((aligned(32))) s1[8], s2[8]; - memset(s1, '\0', sizeof(uint32_t)*7); s1[7] = adler; // TODO: would a masked load be faster? - memset(s2, '\0', sizeof(uint32_t)*7); s2[7] = sum2; - char __attribute__ ((aligned(32))) dot1[32] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - __m256i dot1v = _mm256_load_si256((__m256i*)dot1); - char __attribute__ ((aligned(32))) dot2[32] = {32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; - __m256i dot2v = _mm256_load_si256((__m256i*)dot2); - short __attribute__ ((aligned(32))) dot3[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - __m256i dot3v = _mm256_load_si256((__m256i*)dot3); - // We will need to multiply by - char __attribute__ ((aligned(16))) shift[16] = {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - __m128i shiftv = _mm_load_si128((__m128i*)shift); - while (len >= 32) { - __m256i vs1 = _mm256_load_si256((__m256i*)s1); - __m256i vs2 = _mm256_load_si256((__m256i*)s2); - __m256i vs1_0 = vs1; - int k = (len < NMAX_VEC ? (int)len : NMAX_VEC); - k -= k % 32; - len -= k; - while (k >= 32) { - /* - vs1 = adler + sum(c[i]) - vs2 = sum2 + 16 vs1 + sum( (16-i+1) c[i] ) - */ - __m256i vbuf = _mm256_loadu_si256((__m256i*)buf); - buf += 32; - k -= 32; - __m256i v_short_sum1 = _mm256_maddubs_epi16(vbuf, dot1v); // multiply-add, resulting in 8 shorts. - __m256i vsum1 = _mm256_madd_epi16(v_short_sum1, dot3v); // sum 8 shorts to 4 int32_t; - __m256i v_short_sum2 = _mm256_maddubs_epi16(vbuf, dot2v); - vs1 = _mm256_add_epi32(vsum1, vs1); - __m256i vsum2 = _mm256_madd_epi16(v_short_sum2, dot3v); - vs1_0 = _mm256_sll_epi32(vs1_0, shiftv); - vsum2 = _mm256_add_epi32(vsum2, vs2); - vs2 = _mm256_add_epi32(vsum2, vs1_0); - vs1_0 = vs1; - } - // At this point, we have partial sums stored in vs1 and vs2. There are AVX512 instructions that - // would allow us to sum these quickly (VP4DPWSSD). For now, just unpack and move on. - uint32_t __attribute__((aligned(32))) s1_unpack[8]; - uint32_t __attribute__((aligned(32))) s2_unpack[8]; - _mm256_store_si256((__m256i*)s1_unpack, vs1); - _mm256_store_si256((__m256i*)s2_unpack, vs2); - adler = (s1_unpack[0] % BASE) + (s1_unpack[1] % BASE) + (s1_unpack[2] % BASE) + (s1_unpack[3] % BASE) + (s1_unpack[4] % BASE) + (s1_unpack[5] % BASE) + (s1_unpack[6] % BASE) + (s1_unpack[7] % BASE); - MOD(adler); - s1[7] = adler; - sum2 = (s2_unpack[0] % BASE) + (s2_unpack[1] % BASE) + (s2_unpack[2] % BASE) + (s2_unpack[3] % BASE) + (s2_unpack[4] % BASE) + (s2_unpack[5] % BASE) + (s2_unpack[6] % BASE) + (s2_unpack[7] % BASE); - MOD(sum2); - s2[7] = sum2; - } - - while (len--) { - adler += *buf++; - sum2 += adler; - } - MOD(adler); - MOD(sum2); - - /* return recombined sums */ - return adler | (sum2 << 16); -} -#endif - -void *resolve_adler32(void) -{ - unsigned int eax, ebx, ecx, edx; - signed char has_sse42 = 0; - - /* Collect CPU features */ - if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx)) - return adler32_default; - - has_sse42 = ((ecx & bit_SSE4_2) != 0); - if (__get_cpuid_max (0, NULL) < 7) - return adler32_default; - - __cpuid_count (7, 0, eax, ebx, ecx, edx); - /* Pick AVX2 version only if as supports AVX2 (not the case of Haswell)*/ -#if !defined(ROOT_NO_AVX2) - signed char has_avx2 = 0; - has_avx2 = ((ebx & bit_AVX2) != 0); - if (has_avx2) - return adler32_avx2; -#endif - /* Pick SSE4.2 version */ - if (has_sse42) - return adler32_sse42; - /* Fallback to default implementation */ - return adler32_default; -} - -uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) __attribute__ ((ifunc ("resolve_adler32"))); - -#else // x86_64 -uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len){ - return adler32_default(adler, buf, len); -} -#endif - -/* ========================================================================= */ -static uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) -{ - unsigned long sum1; - unsigned long sum2; - unsigned rem; - - /* for negative len, return invalid adler32 as a clue for debugging */ - if (len2 < 0) - return 0xffffffffUL; - - /* the derivation of this formula is left as an exercise for the reader */ - MOD63(len2); /* assumes len2 >= 0 */ - rem = (unsigned)len2; - sum1 = adler1 & 0xffff; - sum2 = rem * sum1; - MOD(sum2); - sum1 += (adler2 & 0xffff) + BASE - 1; - sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; - if (sum1 >= BASE) sum1 -= BASE; - if (sum1 >= BASE) sum1 -= BASE; - if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); - if (sum2 >= BASE) sum2 -= BASE; - return sum1 | (sum2 << 16); -} - -/* ========================================================================= */ -uLong adler32_combine(uLong adler1, uLong adler2, z_off_t len2) -{ - return adler32_combine_(adler1, adler2, len2); -} - -uLong adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) -{ - return adler32_combine_(adler1, adler2, len2); -} diff --git a/builtins/zlib/compress.c b/builtins/zlib/compress.c deleted file mode 100644 index 0aa12f0c3c669..0000000000000 --- a/builtins/zlib/compress.c +++ /dev/null @@ -1,70 +0,0 @@ -/* compress.c -- compress a memory buffer - * Copyright (C) 1995-2005 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#define ZLIB_INTERNAL -#include "zlib.h" - -/* =========================================================================== - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least 0.1% larger than sourceLen plus - 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ -int ZEXPORT compress2 (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level) -{ - z_stream stream; - int err; - - stream.next_in = (z_const Bytef *)source; - stream.avail_in = (uInt)sourceLen; -#ifdef MAXSEG_64K - /* Check for source > 64K on 16-bit machine: */ - if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; -#endif - stream.next_out = dest; - stream.avail_out = (uInt)*destLen; - if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; - - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - stream.opaque = (voidpf)0; - - err = deflateInit(&stream, level); - if (err != Z_OK) return err; - - err = deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - deflateEnd(&stream); - return err == Z_OK ? Z_BUF_ERROR : err; - } - *destLen = stream.total_out; - - err = deflateEnd(&stream); - return err; -} - -/* =========================================================================== - */ -int ZEXPORT compress (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen) -{ - return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); -} - -/* =========================================================================== - If the default memLevel or windowBits for deflateInit() is changed, then - this function needs to be updated. - */ -uLong ZEXPORT compressBound (uLong sourceLen) -{ - return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + - (sourceLen >> 25) + 13; -} diff --git a/builtins/zlib/crc32.c b/builtins/zlib/crc32.c deleted file mode 100644 index 2dcbf7a851708..0000000000000 --- a/builtins/zlib/crc32.c +++ /dev/null @@ -1,401 +0,0 @@ -/* crc32.c -- compute the CRC-32 of a data stream - * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - * - * Thanks to Rodney Brown for his contribution of faster - * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing - * tables for updating the shift register in one step with three exclusive-ors - * instead of four steps with four exclusive-ors. This results in about a - * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. - */ - -/* @(#) $Id$ */ - -/* - Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore - protection on the static variables used to control the first-use generation - of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should - first call get_crc_table() to initialize the tables before allowing more than - one thread to use crc32(). - - DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. - */ - -#ifdef MAKECRCH -# include -# ifndef DYNAMIC_CRC_TABLE -# define DYNAMIC_CRC_TABLE -# endif /* !DYNAMIC_CRC_TABLE */ -#endif /* MAKECRCH */ - -#include "zutil.h" /* for STDC and FAR definitions */ - -#define local static - -/* Definitions for doing the crc four data bytes at a time. */ -#if !defined(NOBYFOUR) && defined(Z_U4) -# define BYFOUR -#endif -#ifdef BYFOUR - local unsigned long crc32_little OF((unsigned long, - const unsigned char FAR *, unsigned)); - local unsigned long crc32_big OF((unsigned long, - const unsigned char FAR *, unsigned)); -# define TBLS 8 -#else -# define TBLS 1 -#endif /* BYFOUR */ - -/* Local functions for crc concatenation */ -local unsigned long gf2_matrix_times OF((unsigned long *mat, - unsigned long vec)); -local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); -local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); - - -#ifdef DYNAMIC_CRC_TABLE - -local volatile int crc_table_empty = 1; -local z_crc_t FAR crc_table[TBLS][256]; -local void make_crc_table OF((void)); -#ifdef MAKECRCH - local void write_table OF((FILE *, const z_crc_t FAR *)); -#endif /* MAKECRCH */ -/* - Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: - x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. - - Polynomials over GF(2) are represented in binary, one bit per coefficient, - with the lowest powers in the most significant bit. Then adding polynomials - is just exclusive-or, and multiplying a polynomial by x is a right shift by - one. If we call the above polynomial p, and represent a byte as the - polynomial q, also with the lowest power in the most significant bit (so the - byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, - where a mod b means the remainder after dividing a by b. - - This calculation is done using the shift-register method of multiplying and - taking the remainder. The register is initialized to zero, and for each - incoming bit, x^32 is added mod p to the register if the bit is a one (where - x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by - x (which is shifting right by one and adding x^32 mod p if the bit shifted - out is a one). We start with the highest power (least significant bit) of - q and repeat for all eight bits of q. - - The first table is simply the CRC of all possible eight bit values. This is - all the information needed to generate CRCs on data a byte at a time for all - combinations of CRC register values and incoming bytes. The remaining tables - allow for word-at-a-time CRC calculation for both big-endian and little- - endian machines, where a word is four bytes. -*/ -local void make_crc_table() -{ - z_crc_t c; - int n, k; - z_crc_t poly; /* polynomial exclusive-or pattern */ - /* terms of polynomial defining this crc (except x^32): */ - static volatile int first = 1; /* flag to limit concurrent making */ - static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; - - /* See if another task is already doing this (not thread-safe, but better - than nothing -- significantly reduces duration of vulnerability in - case the advice about DYNAMIC_CRC_TABLE is ignored) */ - if (first) { - first = 0; - - /* make exclusive-or pattern from polynomial (0xedb88320UL) */ - poly = 0; - for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) - poly |= (z_crc_t)1 << (31 - p[n]); - - /* generate a crc for every 8-bit value */ - for (n = 0; n < 256; n++) { - c = (z_crc_t)n; - for (k = 0; k < 8; k++) - c = c & 1 ? poly ^ (c >> 1) : c >> 1; - crc_table[0][n] = c; - } - -#ifdef BYFOUR - /* generate crc for each value followed by one, two, and three zeros, - and then the byte reversal of those as well as the first table */ - for (n = 0; n < 256; n++) { - c = crc_table[0][n]; - crc_table[4][n] = ZSWAP32(c); - for (k = 1; k < 4; k++) { - c = crc_table[0][c & 0xff] ^ (c >> 8); - crc_table[k][n] = c; - crc_table[k + 4][n] = ZSWAP32(c); - } - } -#endif /* BYFOUR */ - - crc_table_empty = 0; - } - else { /* not first */ - /* wait for the other guy to finish (not efficient, but rare) */ - while (crc_table_empty) - ; - } - -#ifdef MAKECRCH - /* write out CRC tables to crc32.h */ - { - FILE *out; - - out = fopen("crc32.h", "w"); - if (out == NULL) return; - fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); - fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); - fprintf(out, "local const z_crc_t FAR "); - fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); - write_table(out, crc_table[0]); -# ifdef BYFOUR - fprintf(out, "#ifdef BYFOUR\n"); - for (k = 1; k < 8; k++) { - fprintf(out, " },\n {\n"); - write_table(out, crc_table[k]); - } - fprintf(out, "#endif\n"); -# endif /* BYFOUR */ - fprintf(out, " }\n};\n"); - fclose(out); - } -#endif /* MAKECRCH */ -} - -#ifdef MAKECRCH -local void write_table(FILE *out, const z_crc_t FAR *table) -{ - int n; - - for (n = 0; n < 256; n++) - fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", - (unsigned long)(table[n]), - n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); -} -#endif /* MAKECRCH */ - -#else /* !DYNAMIC_CRC_TABLE */ -/* ======================================================================== - * Tables of CRC-32s of all single-byte values, made by make_crc_table(). - */ -#include "crc32.h" -#endif /* DYNAMIC_CRC_TABLE */ - -/* ========================================================================= - * This function can be used by asm versions of crc32() - */ -const z_crc_t FAR * ZEXPORT get_crc_table() -{ -#ifdef DYNAMIC_CRC_TABLE - if (crc_table_empty) - make_crc_table(); -#endif /* DYNAMIC_CRC_TABLE */ - return (const z_crc_t FAR *)crc_table; -} - -/* ========================================================================= */ -#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) -#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 - -/* ========================================================================= */ -unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, uInt len) -{ - if (buf == Z_NULL) return 0UL; - -#ifdef DYNAMIC_CRC_TABLE - if (crc_table_empty) - make_crc_table(); -#endif /* DYNAMIC_CRC_TABLE */ - -#ifdef BYFOUR - if (sizeof(void *) == sizeof(ptrdiff_t)) { - z_crc_t endian; - - endian = 1; - if (*((unsigned char *)(&endian))) - return crc32_little(crc, buf, len); - else - return crc32_big(crc, buf, len); - } -#endif /* BYFOUR */ - crc = crc ^ 0xffffffffUL; - while (len >= 8) { - DO8; - len -= 8; - } - if (len) do { - DO1; - } while (--len); - return crc ^ 0xffffffffUL; -} - -#ifdef BYFOUR - -/* ========================================================================= */ -#define DOLIT4 c ^= *buf4++; \ - c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ - crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] -#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 - -/* ========================================================================= */ -local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf, unsigned len) -{ - register z_crc_t c; - register const z_crc_t FAR *buf4; - - c = (z_crc_t)crc; - c = ~c; - while (len && ((ptrdiff_t)buf & 3)) { - c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); - len--; - } - - buf4 = (const z_crc_t FAR *)(const void FAR *)buf; - while (len >= 32) { - DOLIT32; - len -= 32; - } - while (len >= 4) { - DOLIT4; - len -= 4; - } - buf = (const unsigned char FAR *)buf4; - - if (len) do { - c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); - } while (--len); - c = ~c; - return (unsigned long)c; -} - -/* ========================================================================= */ -#define DOBIG4 c ^= *++buf4; \ - c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ - crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] -#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 - -/* ========================================================================= */ -local unsigned long crc32_big(unsigned long crc, const unsigned char FAR *buf, unsigned len) -{ - register z_crc_t c; - register const z_crc_t FAR *buf4; - - c = ZSWAP32((z_crc_t)crc); - c = ~c; - while (len && ((ptrdiff_t)buf & 3)) { - c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); - len--; - } - - buf4 = (const z_crc_t FAR *)(const void FAR *)buf; - buf4--; - while (len >= 32) { - DOBIG32; - len -= 32; - } - while (len >= 4) { - DOBIG4; - len -= 4; - } - buf4++; - buf = (const unsigned char FAR *)buf4; - - if (len) do { - c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); - } while (--len); - c = ~c; - return (unsigned long)(ZSWAP32(c)); -} - -#endif /* BYFOUR */ - -#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ - -/* ========================================================================= */ -local unsigned long gf2_matrix_times(unsigned long *mat, unsigned long vec) -{ - unsigned long sum; - - sum = 0; - while (vec) { - if (vec & 1) - sum ^= *mat; - vec >>= 1; - mat++; - } - return sum; -} - -/* ========================================================================= */ -local void gf2_matrix_square(unsigned long *square, unsigned long *mat) -{ - int n; - - for (n = 0; n < GF2_DIM; n++) - square[n] = gf2_matrix_times(mat, mat[n]); -} - -/* ========================================================================= */ -local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2) -{ - int n; - unsigned long row; - unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ - unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ - - /* degenerate case (also disallow negative lengths) */ - if (len2 <= 0) - return crc1; - - /* put operator for one zero bit in odd */ - odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ - row = 1; - for (n = 1; n < GF2_DIM; n++) { - odd[n] = row; - row <<= 1; - } - - /* put operator for two zero bits in even */ - gf2_matrix_square(even, odd); - - /* put operator for four zero bits in odd */ - gf2_matrix_square(odd, even); - - /* apply len2 zeros to crc1 (first square will put the operator for one - zero byte, eight zero bits, in even) */ - do { - /* apply zeros operator for this bit of len2 */ - gf2_matrix_square(even, odd); - if (len2 & 1) - crc1 = gf2_matrix_times(even, crc1); - len2 >>= 1; - - /* if no more bits set, then done */ - if (len2 == 0) - break; - - /* another iteration of the loop with odd and even swapped */ - gf2_matrix_square(odd, even); - if (len2 & 1) - crc1 = gf2_matrix_times(odd, crc1); - len2 >>= 1; - - /* if no more bits set, then done */ - } while (len2 != 0); - - /* return combined crc */ - crc1 ^= crc2; - return crc1; -} - -/* ========================================================================= */ -uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) -{ - return crc32_combine_(crc1, crc2, len2); -} - -uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) -{ - return crc32_combine_(crc1, crc2, len2); -} diff --git a/builtins/zlib/crc32.h b/builtins/zlib/crc32.h deleted file mode 100644 index 9e0c778102514..0000000000000 --- a/builtins/zlib/crc32.h +++ /dev/null @@ -1,441 +0,0 @@ -/* crc32.h -- tables for rapid CRC calculation - * Generated automatically by crc32.c - */ - -local const z_crc_t FAR crc_table[TBLS][256] = -{ - { - 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, - 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, - 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, - 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, - 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, - 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, - 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, - 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, - 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, - 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, - 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, - 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, - 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, - 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, - 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, - 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, - 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, - 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, - 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, - 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, - 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, - 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, - 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, - 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, - 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, - 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, - 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, - 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, - 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, - 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, - 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, - 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, - 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, - 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, - 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, - 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, - 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, - 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, - 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, - 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, - 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, - 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, - 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, - 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, - 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, - 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, - 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, - 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, - 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, - 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, - 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, - 0x2d02ef8dUL -#ifdef BYFOUR - }, - { - 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, - 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, - 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, - 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, - 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, - 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, - 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, - 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, - 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, - 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, - 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, - 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, - 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, - 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, - 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, - 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, - 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, - 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, - 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, - 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, - 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, - 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, - 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, - 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, - 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, - 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, - 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, - 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, - 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, - 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, - 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, - 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, - 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, - 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, - 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, - 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, - 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, - 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, - 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, - 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, - 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, - 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, - 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, - 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, - 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, - 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, - 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, - 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, - 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, - 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, - 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, - 0x9324fd72UL - }, - { - 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, - 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, - 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, - 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, - 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, - 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, - 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, - 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, - 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, - 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, - 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, - 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, - 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, - 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, - 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, - 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, - 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, - 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, - 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, - 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, - 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, - 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, - 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, - 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, - 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, - 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, - 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, - 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, - 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, - 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, - 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, - 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, - 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, - 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, - 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, - 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, - 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, - 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, - 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, - 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, - 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, - 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, - 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, - 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, - 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, - 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, - 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, - 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, - 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, - 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, - 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, - 0xbe9834edUL - }, - { - 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, - 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, - 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, - 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, - 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, - 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, - 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, - 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, - 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, - 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, - 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, - 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, - 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, - 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, - 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, - 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, - 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, - 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, - 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, - 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, - 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, - 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, - 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, - 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, - 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, - 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, - 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, - 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, - 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, - 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, - 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, - 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, - 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, - 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, - 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, - 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, - 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, - 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, - 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, - 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, - 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, - 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, - 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, - 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, - 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, - 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, - 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, - 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, - 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, - 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, - 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, - 0xde0506f1UL - }, - { - 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, - 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, - 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, - 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, - 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, - 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, - 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, - 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, - 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, - 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, - 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, - 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, - 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, - 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, - 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, - 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, - 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, - 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, - 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, - 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, - 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, - 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, - 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, - 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, - 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, - 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, - 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, - 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, - 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, - 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, - 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, - 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, - 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, - 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, - 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, - 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, - 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, - 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, - 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, - 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, - 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, - 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, - 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, - 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, - 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, - 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, - 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, - 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, - 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, - 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, - 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, - 0x8def022dUL - }, - { - 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, - 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, - 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, - 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, - 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, - 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, - 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, - 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, - 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, - 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, - 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, - 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, - 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, - 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, - 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, - 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, - 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, - 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, - 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, - 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, - 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, - 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, - 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, - 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, - 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, - 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, - 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, - 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, - 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, - 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, - 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, - 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, - 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, - 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, - 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, - 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, - 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, - 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, - 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, - 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, - 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, - 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, - 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, - 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, - 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, - 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, - 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, - 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, - 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, - 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, - 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, - 0x72fd2493UL - }, - { - 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, - 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, - 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, - 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, - 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, - 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, - 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, - 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, - 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, - 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, - 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, - 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, - 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, - 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, - 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, - 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, - 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, - 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, - 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, - 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, - 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, - 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, - 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, - 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, - 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, - 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, - 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, - 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, - 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, - 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, - 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, - 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, - 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, - 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, - 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, - 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, - 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, - 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, - 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, - 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, - 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, - 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, - 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, - 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, - 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, - 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, - 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, - 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, - 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, - 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, - 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, - 0xed3498beUL - }, - { - 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, - 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, - 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, - 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, - 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, - 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, - 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, - 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, - 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, - 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, - 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, - 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, - 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, - 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, - 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, - 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, - 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, - 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, - 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, - 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, - 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, - 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, - 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, - 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, - 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, - 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, - 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, - 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, - 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, - 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, - 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, - 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, - 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, - 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, - 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, - 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, - 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, - 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, - 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, - 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, - 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, - 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, - 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, - 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, - 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, - 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, - 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, - 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, - 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, - 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, - 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, - 0xf10605deUL -#endif - } -}; diff --git a/builtins/zlib/crc32_cf.c b/builtins/zlib/crc32_cf.c deleted file mode 100644 index c4392bb842b5b..0000000000000 --- a/builtins/zlib/crc32_cf.c +++ /dev/null @@ -1,474 +0,0 @@ -/* crc32.c -- compute the CRC-32 of a data stream - * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - * - * Thanks to Rodney Brown for his contribution of faster - * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing - * tables for updating the shift register in one step with three exclusive-ors - * instead of four steps with four exclusive-ors. This results in about a - * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. - */ - -/* @(#) $Id$ */ - -/* - Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore - protection on the static variables used to control the first-use generation - of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should - first call get_crc_table() to initialize the tables before allowing more than - one thread to use crc32(). - - DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. - */ -#if defined(__GNUC__) && !defined(__clang__) -#pragma GCC push_options -#if __ARM_ARCH >= 8 -#pragma GCC target ("arch=armv8-a+crc") -#endif -#endif - -#if defined (__ARM_FEATURE_CRC32) -#include -#include - -#include -#include - -uint32_t crc32(uint32_t crc, uint8_t *buf, size_t len) { - crc = ~crc; - - while (len > 8) { - crc = __crc32d(crc, *(uint64_t*)buf); - len -= 8; - buf += 8; - } - - while (len) { - crc = __crc32b(crc, *buf); - len--; - buf++; - } - - return ~crc; -} - -#else - -#include - -#ifdef MAKECRCH -# include -# ifndef DYNAMIC_CRC_TABLE -# define DYNAMIC_CRC_TABLE -# endif /* !DYNAMIC_CRC_TABLE */ -#endif /* MAKECRCH */ - -#include "zutil.h" /* for STDC and FAR definitions */ - -#ifdef __x86_64__ -#include "cpuid.h" -#endif - -#define local static - -/* Definitions for doing the crc four data bytes at a time. */ -#if !defined(NOBYFOUR) && defined(Z_U4) -# define BYFOUR -#endif -#ifdef BYFOUR - local unsigned long crc32_little OF((unsigned long, - const unsigned char FAR *, unsigned)); - local unsigned long crc32_big OF((unsigned long, - const unsigned char FAR *, unsigned)); -# define TBLS 8 -#else -# define TBLS 1 -#endif /* BYFOUR */ - -/* Local functions for crc concatenation */ -local unsigned long gf2_matrix_times OF((unsigned long *mat, - unsigned long vec)); -local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); -local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); - - -#ifdef DYNAMIC_CRC_TABLE - -local volatile int crc_table_empty = 1; -local z_crc_t FAR crc_table[TBLS][256]; -local void make_crc_table OF((void)); -#ifdef MAKECRCH - local void write_table OF((FILE *, const z_crc_t FAR *)); -#endif /* MAKECRCH */ -/* - Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: - x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. - - Polynomials over GF(2) are represented in binary, one bit per coefficient, - with the lowest powers in the most significant bit. Then adding polynomials - is just exclusive-or, and multiplying a polynomial by x is a right shift by - one. If we call the above polynomial p, and represent a byte as the - polynomial q, also with the lowest power in the most significant bit (so the - byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, - where a mod b means the remainder after dividing a by b. - - This calculation is done using the shift-register method of multiplying and - taking the remainder. The register is initialized to zero, and for each - incoming bit, x^32 is added mod p to the register if the bit is a one (where - x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by - x (which is shifting right by one and adding x^32 mod p if the bit shifted - out is a one). We start with the highest power (least significant bit) of - q and repeat for all eight bits of q. - - The first table is simply the CRC of all possible eight bit values. This is - all the information needed to generate CRCs on data a byte at a time for all - combinations of CRC register values and incoming bytes. The remaining tables - allow for word-at-a-time CRC calculation for both big-endian and little- - endian machines, where a word is four bytes. -*/ -local void make_crc_table() -{ - z_crc_t c; - int n, k; - z_crc_t poly; /* polynomial exclusive-or pattern */ - /* terms of polynomial defining this crc (except x^32): */ - static volatile int first = 1; /* flag to limit concurrent making */ - static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; - - /* See if another task is already doing this (not thread-safe, but better - than nothing -- significantly reduces duration of vulnerability in - case the advice about DYNAMIC_CRC_TABLE is ignored) */ - if (first) { - first = 0; - - /* make exclusive-or pattern from polynomial (0xedb88320UL) */ - poly = 0; - for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) - poly |= (z_crc_t)1 << (31 - p[n]); - - /* generate a crc for every 8-bit value */ - for (n = 0; n < 256; n++) { - c = (z_crc_t)n; - for (k = 0; k < 8; k++) - c = c & 1 ? poly ^ (c >> 1) : c >> 1; - crc_table[0][n] = c; - } - -#ifdef BYFOUR - /* generate crc for each value followed by one, two, and three zeros, - and then the byte reversal of those as well as the first table */ - for (n = 0; n < 256; n++) { - c = crc_table[0][n]; - crc_table[4][n] = ZSWAP32(c); - for (k = 1; k < 4; k++) { - c = crc_table[0][c & 0xff] ^ (c >> 8); - crc_table[k][n] = c; - crc_table[k + 4][n] = ZSWAP32(c); - } - } -#endif /* BYFOUR */ - - crc_table_empty = 0; - } - else { /* not first */ - /* wait for the other guy to finish (not efficient, but rare) */ - while (crc_table_empty) - ; - } - -#ifdef MAKECRCH - /* write out CRC tables to crc32.h */ - { - FILE *out; - - out = fopen("crc32.h", "w"); - if (out == NULL) return; - fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); - fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); - fprintf(out, "local const z_crc_t FAR "); - fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); - write_table(out, crc_table[0]); -# ifdef BYFOUR - fprintf(out, "#ifdef BYFOUR\n"); - for (k = 1; k < 8; k++) { - fprintf(out, " },\n {\n"); - write_table(out, crc_table[k]); - } - fprintf(out, "#endif\n"); -# endif /* BYFOUR */ - fprintf(out, " }\n};\n"); - fclose(out); - } -#endif /* MAKECRCH */ -} - -#ifdef MAKECRCH -local void write_table(out, table) - FILE *out; - const z_crc_t FAR *table; -{ - int n; - - for (n = 0; n < 256; n++) - fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", - (unsigned long)(table[n]), - n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); -} -#endif /* MAKECRCH */ - -#else /* !DYNAMIC_CRC_TABLE */ -/* ======================================================================== - * Tables of CRC-32s of all single-byte values, made by make_crc_table(). - */ -#include "crc32.h" -#endif /* DYNAMIC_CRC_TABLE */ - -/* ========================================================================= - * This function can be used by asm versions of crc32() - */ -const z_crc_t FAR * ZEXPORT get_crc_table() -{ -#ifdef DYNAMIC_CRC_TABLE - if (crc_table_empty) - make_crc_table(); -#endif /* DYNAMIC_CRC_TABLE */ - return (const z_crc_t FAR *)crc_table; -} - -/* ========================================================================= */ -#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) -#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 - -/* ========================================================================= */ -local unsigned long crc32_generic(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - uInt len; -{ - if (buf == Z_NULL) return 0UL; - -#ifdef DYNAMIC_CRC_TABLE - if (crc_table_empty) - make_crc_table(); -#endif /* DYNAMIC_CRC_TABLE */ - -#ifdef BYFOUR - if (sizeof(void *) == sizeof(ptrdiff_t)) { - z_crc_t endian; - - endian = 1; - if (*((unsigned char *)(&endian))) - return crc32_little(crc, buf, len); - else - return crc32_big(crc, buf, len); - } -#endif /* BYFOUR */ - crc = crc ^ 0xffffffffUL; - while (len >= 8) { - DO8; - len -= 8; - } - if (len) do { - DO1; - } while (--len); - return crc ^ 0xffffffffUL; -} - -uLong crc32(crc, buf, len) - uLong crc; - const Bytef *buf; - uInt len; -{ - return crc32_generic(crc, buf, len); -} - -#ifdef BYFOUR - -/* ========================================================================= */ -#define DOLIT4 c ^= *buf4++; \ - c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ - crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] -#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 - -/* ========================================================================= */ -local unsigned long crc32_little(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - unsigned len; -{ - register z_crc_t c; - register const z_crc_t FAR *buf4; - - c = (z_crc_t)crc; - c = ~c; - while (len && ((ptrdiff_t)buf & 3)) { - c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); - len--; - } - - buf4 = (const z_crc_t FAR *)(const void FAR *)buf; - while (len >= 32) { - DOLIT32; - len -= 32; - } - while (len >= 4) { - DOLIT4; - len -= 4; - } - buf = (const unsigned char FAR *)buf4; - - if (len) do { - c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); - } while (--len); - c = ~c; - return (unsigned long)c; -} - -/* ========================================================================= */ -#define DOBIG4 c ^= *++buf4; \ - c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ - crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] -#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 - -/* ========================================================================= */ -local unsigned long crc32_big(crc, buf, len) - unsigned long crc; - const unsigned char FAR *buf; - unsigned len; -{ - register z_crc_t c; - register const z_crc_t FAR *buf4; - - c = ZSWAP32((z_crc_t)crc); - c = ~c; - while (len && ((ptrdiff_t)buf & 3)) { - c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); - len--; - } - - buf4 = (const z_crc_t FAR *)(const void FAR *)buf; - buf4--; - while (len >= 32) { - DOBIG32; - len -= 32; - } - while (len >= 4) { - DOBIG4; - len -= 4; - } - buf4++; - buf = (const unsigned char FAR *)buf4; - - if (len) do { - c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); - } while (--len); - c = ~c; - return (unsigned long)(ZSWAP32(c)); -} - -#endif /* BYFOUR */ - -#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ - -/* ========================================================================= */ -local unsigned long gf2_matrix_times(mat, vec) - unsigned long *mat; - unsigned long vec; -{ - unsigned long sum; - - sum = 0; - while (vec) { - if (vec & 1) - sum ^= *mat; - vec >>= 1; - mat++; - } - return sum; -} - -/* ========================================================================= */ -local void gf2_matrix_square(square, mat) - unsigned long *square; - unsigned long *mat; -{ - int n; - - for (n = 0; n < GF2_DIM; n++) - square[n] = gf2_matrix_times(mat, mat[n]); -} - -/* ========================================================================= */ -local uLong crc32_combine_(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off64_t len2; -{ - int n; - unsigned long row; - unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ - unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ - - /* degenerate case (also disallow negative lengths) */ - if (len2 <= 0) - return crc1; - - /* put operator for one zero bit in odd */ - odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ - row = 1; - for (n = 1; n < GF2_DIM; n++) { - odd[n] = row; - row <<= 1; - } - - /* put operator for two zero bits in even */ - gf2_matrix_square(even, odd); - - /* put operator for four zero bits in odd */ - gf2_matrix_square(odd, even); - - /* apply len2 zeros to crc1 (first square will put the operator for one - zero byte, eight zero bits, in even) */ - do { - /* apply zeros operator for this bit of len2 */ - gf2_matrix_square(even, odd); - if (len2 & 1) - crc1 = gf2_matrix_times(even, crc1); - len2 >>= 1; - - /* if no more bits set, then done */ - if (len2 == 0) - break; - - /* another iteration of the loop with odd and even swapped */ - gf2_matrix_square(odd, even); - if (len2 & 1) - crc1 = gf2_matrix_times(odd, crc1); - len2 >>= 1; - - /* if no more bits set, then done */ - } while (len2 != 0); - - /* return combined crc */ - crc1 ^= crc2; - return crc1; -} - -/* ========================================================================= */ -uLong ZEXPORT crc32_combine(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off_t len2; -{ - return crc32_combine_(crc1, crc2, len2); -} - -uLong ZEXPORT crc32_combine64(crc1, crc2, len2) - uLong crc1; - uLong crc2; - z_off64_t len2; -{ - return crc32_combine_(crc1, crc2, len2); -} - -#endif diff --git a/builtins/zlib/deflate.c b/builtins/zlib/deflate.c deleted file mode 100644 index d56571b143359..0000000000000 --- a/builtins/zlib/deflate.c +++ /dev/null @@ -1,1907 +0,0 @@ -/* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * ALGORITHM - * - * The "deflation" process depends on being able to identify portions - * of the input text which are identical to earlier input (within a - * sliding window trailing behind the input currently being processed). - * - * The most straightforward technique turns out to be the fastest for - * most input files: try all possible matches and select the longest. - * The key feature of this algorithm is that insertions into the string - * dictionary are very simple and thus fast, and deletions are avoided - * completely. Insertions are performed at each input character, whereas - * string matches are performed only when the previous match ends. So it - * is preferable to spend more time in matches to allow very fast string - * insertions and avoid deletions. The matching algorithm for small - * strings is inspired from that of Rabin & Karp. A brute force approach - * is used to find longer strings when a small match has been found. - * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze - * (by Leonid Broukhis). - * A previous version of this file used a more sophisticated algorithm - * (by Fiala and Greene) which is guaranteed to run in linear amortized - * time, but has a larger average cost, uses more memory and is patented. - * However the F&G algorithm may be faster for some highly redundant - * files if the parameter max_chain_length (described below) is too large. - * - * ACKNOWLEDGEMENTS - * - * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and - * I found it in 'freeze' written by Leonid Broukhis. - * Thanks to many people for bug reports and testing. - * - * REFERENCES - * - * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". - * Available in http://tools.ietf.org/html/rfc1951 - * - * A description of the Rabin and Karp algorithm is given in the book - * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. - * - * Fiala,E.R., and Greene,D.H. - * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 - * - */ - -/* @(#) $Id$ */ - -#include "deflate.h" - -const char deflate_copyright[] = - " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; -/* - If you use the zlib library in a product, an acknowledgment is welcome - in the documentation of your product. If for some reason you cannot - include such an acknowledgment, I would appreciate that you keep this - copyright string in the executable of your product. - */ - -/* =========================================================================== - * Function prototypes. - */ -typedef enum { - need_more, /* block not completed, need more input or more output */ - block_done, /* block flush performed */ - finish_started, /* finish started, need only more output at next deflate */ - finish_done /* finish done, accept no more input or output */ -} block_state; - -typedef block_state (*compress_func) OF((deflate_state *s, int flush)); -/* Compression function. Returns the block state after the call. */ - -local void fill_window OF((deflate_state *s)); -local block_state deflate_stored OF((deflate_state *s, int flush)); -local block_state deflate_fast OF((deflate_state *s, int flush)); -#ifndef FASTEST -local block_state deflate_slow OF((deflate_state *s, int flush)); -#endif -local block_state deflate_rle OF((deflate_state *s, int flush)); -local block_state deflate_huff OF((deflate_state *s, int flush)); -local void lm_init OF((deflate_state *s)); -local void putShortMSB OF((deflate_state *s, uInt b)); -local void flush_pending OF((z_streamp strm)); -local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); -#ifdef ASMV - void match_init OF((void)); /* asm code initialization */ - uInt longest_match OF((deflate_state *s, IPos cur_match)); -#else -local uInt longest_match OF((deflate_state *s, IPos cur_match)); -#endif - -#ifdef DEBUG -local void check_match OF((deflate_state *s, IPos start, IPos match, - int length)); -#endif - -/* =========================================================================== - * Local data - */ - -#define NIL 0 -/* Tail of hash chains */ - -#ifndef TOO_FAR -# define TOO_FAR 4096 -#endif -/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ - -/* Values for max_lazy_match, good_match and max_chain_length, depending on - * the desired pack level (0..9). The values given below have been tuned to - * exclude worst case performance for pathological files. Better values may be - * found for specific files. - */ -typedef struct config_s { - ush good_length; /* reduce lazy search above this match length */ - ush max_lazy; /* do not perform lazy search above this match length */ - ush nice_length; /* quit search above this match length */ - ush max_chain; - compress_func func; -} config; - -#ifdef FASTEST -local const config configuration_table[2] = { -/* good lazy nice chain */ -/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ -/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ -#else -local const config configuration_table[10] = { -/* good lazy nice chain */ -/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ -/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ -/* 2 */ {4, 5, 16, 8, deflate_fast}, -/* 3 */ {4, 6, 32, 32, deflate_fast}, - -/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ -/* 5 */ {8, 16, 32, 32, deflate_slow}, -/* 6 */ {8, 16, 128, 128, deflate_slow}, -/* 7 */ {8, 32, 128, 256, deflate_slow}, -/* 8 */ {32, 128, 258, 1024, deflate_slow}, -/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ -#endif - -/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 - * For deflate_fast() (levels <= 3) good is ignored and lazy has a different - * meaning. - */ - -#define EQUAL 0 -/* result of memcmp for equal strings */ - -#ifndef NO_DUMMY_DECL -struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ -#endif - -/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ -#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) - -/* =========================================================================== - * Update a hash value with the given input byte - * IN assertion: all calls to to UPDATE_HASH are made with consecutive - * input characters, so that a running hash key can be computed from the - * previous key instead of complete recalculation each time. - */ -#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) - - -/* =========================================================================== - * Insert string str in the dictionary and set match_head to the previous head - * of the hash chain (the most recent string with same hash key). Return - * the previous length of the hash chain. - * If this file is compiled with -DFASTEST, the compression level is forced - * to 1, and no hash chains are maintained. - * IN assertion: all calls to to INSERT_STRING are made with consecutive - * input characters and the first MIN_MATCH bytes of str are valid - * (except for the last MIN_MATCH-1 bytes of the input file). - */ -#ifdef FASTEST -#define INSERT_STRING(s, str, match_head) \ - (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ - match_head = s->head[s->ins_h], \ - s->head[s->ins_h] = (Pos)(str)) -#else -#define INSERT_STRING(s, str, match_head) \ - (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ - match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ - s->head[s->ins_h] = (Pos)(str)) -#endif - -/* =========================================================================== - * Initialize the hash table (avoiding 64K overflow for 16 bit systems). - * prev[] will be initialized on the fly. - */ -#define CLEAR_HASH(s) \ - s->head[s->hash_size-1] = NIL; \ - zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); - -/* ========================================================================= */ -int ZEXPORT deflateInit_(z_streamp strm, int level, const char *version, int stream_size) -{ - return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, version, stream_size); - /* To do: ignore strm->next_in if we use it as window */ -} - -/* ========================================================================= */ -int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, - const char *version, int stream_size) -{ - deflate_state *s; - int wrap = 1; - static const char my_version[] = ZLIB_VERSION; - - ushf *overlay; - /* We overlay pending_buf and d_buf+l_buf. This works since the average - * output size for (length,distance) codes is <= 24 bits. - */ - - if (version == Z_NULL || version[0] != my_version[0] || - stream_size != sizeof(z_stream)) { - return Z_VERSION_ERROR; - } - if (strm == Z_NULL) return Z_STREAM_ERROR; - - strm->msg = Z_NULL; - if (strm->zalloc == (alloc_func)0) { -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zalloc = zcalloc; - strm->opaque = (voidpf)0; -#endif - } - if (strm->zfree == (free_func)0) -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zfree = zcfree; -#endif - -#ifdef FASTEST - if (level != 0) level = 1; -#else - if (level == Z_DEFAULT_COMPRESSION) level = 6; -#endif - - if (windowBits < 0) { /* suppress zlib wrapper */ - wrap = 0; - windowBits = -windowBits; - } -#ifdef GZIP - else if (windowBits > 15) { - wrap = 2; /* write gzip wrapper instead */ - windowBits -= 16; - } -#endif - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || - windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED) { - return Z_STREAM_ERROR; - } - if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ - s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); - if (s == Z_NULL) return Z_MEM_ERROR; - strm->state = (struct internal_state FAR *)s; - s->strm = strm; - - s->wrap = wrap; - s->gzhead = Z_NULL; - s->w_bits = windowBits; - s->w_size = 1 << s->w_bits; - s->w_mask = s->w_size - 1; - - s->hash_bits = memLevel + 7; - s->hash_size = 1 << s->hash_bits; - s->hash_mask = s->hash_size - 1; - s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); - - s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); - s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); - s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); - - s->high_water = 0; /* nothing written to s->window yet */ - - s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ - - overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); - s->pending_buf = (uchf *) overlay; - s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); - - if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || - s->pending_buf == Z_NULL) { - s->status = FINISH_STATE; - strm->msg = ERR_MSG(Z_MEM_ERROR); - deflateEnd (strm); - return Z_MEM_ERROR; - } - s->d_buf = overlay + s->lit_bufsize/sizeof(ush); - s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; - - s->level = level; - s->strategy = strategy; - s->method = (Byte)method; - - return deflateReset(strm); -} - -/* ========================================================================= */ -int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt dictLength) -{ - deflate_state *s; - uInt str, n; - int wrap; - unsigned avail; - z_const unsigned char *next; - - if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) - return Z_STREAM_ERROR; - s = strm->state; - wrap = s->wrap; - if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) - return Z_STREAM_ERROR; - - /* when using zlib wrappers, compute Adler-32 for provided dictionary */ - if (wrap == 1) - strm->adler = adler32(strm->adler, dictionary, dictLength); - s->wrap = 0; /* avoid computing Adler-32 in read_buf */ - - /* if dictionary would fill window, just replace the history */ - if (dictLength >= s->w_size) { - if (wrap == 0) { /* already empty otherwise */ - CLEAR_HASH(s); - s->strstart = 0; - s->block_start = 0L; - s->insert = 0; - } - dictionary += dictLength - s->w_size; /* use the tail */ - dictLength = s->w_size; - } - - /* insert dictionary into window and hash */ - avail = strm->avail_in; - next = strm->next_in; - strm->avail_in = dictLength; - strm->next_in = (z_const Bytef *)dictionary; - fill_window(s); - while (s->lookahead >= MIN_MATCH) { - str = s->strstart; - n = s->lookahead - (MIN_MATCH-1); - do { - UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); -#ifndef FASTEST - s->prev[str & s->w_mask] = s->head[s->ins_h]; -#endif - s->head[s->ins_h] = (Pos)str; - str++; - } while (--n); - s->strstart = str; - s->lookahead = MIN_MATCH-1; - fill_window(s); - } - s->strstart += s->lookahead; - s->block_start = (long)s->strstart; - s->insert = s->lookahead; - s->lookahead = 0; - s->match_length = s->prev_length = MIN_MATCH-1; - s->match_available = 0; - strm->next_in = next; - strm->avail_in = avail; - s->wrap = wrap; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateResetKeep (z_streamp strm) -{ - deflate_state *s; - - if (strm == Z_NULL || strm->state == Z_NULL || - strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { - return Z_STREAM_ERROR; - } - - strm->total_in = strm->total_out = 0; - strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ - strm->data_type = Z_UNKNOWN; - - s = (deflate_state *)strm->state; - s->pending = 0; - s->pending_out = s->pending_buf; - - if (s->wrap < 0) { - s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ - } - s->status = s->wrap ? INIT_STATE : BUSY_STATE; - strm->adler = -#ifdef GZIP - s->wrap == 2 ? crc32(0L, Z_NULL, 0) : -#endif - adler32(0L, Z_NULL, 0); - s->last_flush = Z_NO_FLUSH; - - _tr_init(s); - - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateReset (z_streamp strm) -{ - int ret; - - ret = deflateResetKeep(strm); - if (ret == Z_OK) - lm_init(strm->state); - return ret; -} - -/* ========================================================================= */ -int ZEXPORT deflateSetHeader (z_streamp strm, gz_headerp head) -{ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - if (strm->state->wrap != 2) return Z_STREAM_ERROR; - strm->state->gzhead = head; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflatePending (z_streamp strm, unsigned *pending, int *bits) -{ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - if (pending != Z_NULL) - *pending = strm->state->pending; - if (bits != Z_NULL) - *bits = strm->state->bi_valid; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflatePrime (z_streamp strm, int bits, int value) -{ - deflate_state *s; - int put; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - s = strm->state; - if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) - return Z_BUF_ERROR; - do { - put = Buf_size - s->bi_valid; - if (put > bits) - put = bits; - s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); - s->bi_valid += put; - _tr_flush_bits(s); - value >>= put; - bits -= put; - } while (bits); - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) -{ - deflate_state *s; - compress_func func; - int err = Z_OK; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - s = strm->state; - -#ifdef FASTEST - if (level != 0) level = 1; -#else - if (level == Z_DEFAULT_COMPRESSION) level = 6; -#endif - if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { - return Z_STREAM_ERROR; - } - func = configuration_table[s->level].func; - - if ((strategy != s->strategy || func != configuration_table[level].func) && - strm->total_in != 0) { - /* Flush the last buffer: */ - err = deflate(strm, Z_BLOCK); - if (err == Z_BUF_ERROR && s->pending == 0) - err = Z_OK; - } - if (s->level != level) { - s->level = level; - s->max_lazy_match = configuration_table[level].max_lazy; - s->good_match = configuration_table[level].good_length; - s->nice_match = configuration_table[level].nice_length; - s->max_chain_length = configuration_table[level].max_chain; - } - s->strategy = strategy; - return err; -} - -/* ========================================================================= */ -int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy, int nice_length, int max_chain) -{ - deflate_state *s; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - s = strm->state; - s->good_match = good_length; - s->max_lazy_match = max_lazy; - s->nice_match = nice_length; - s->max_chain_length = max_chain; - return Z_OK; -} - -/* ========================================================================= - * For the default windowBits of 15 and memLevel of 8, this function returns - * a close to exact, as well as small, upper bound on the compressed size. - * They are coded as constants here for a reason--if the #define's are - * changed, then this function needs to be changed as well. The return - * value for 15 and 8 only works for those exact settings. - * - * For any setting other than those defaults for windowBits and memLevel, - * the value returned is a conservative worst case for the maximum expansion - * resulting from using fixed blocks instead of stored blocks, which deflate - * can emit on compressed data for some combinations of the parameters. - * - * This function could be more sophisticated to provide closer upper bounds for - * every combination of windowBits and memLevel. But even the conservative - * upper bound of about 14% expansion does not seem onerous for output buffer - * allocation. - */ -uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) -{ - deflate_state *s; - uLong complen, wraplen; - Bytef *str; - - /* conservative upper bound for compressed data */ - complen = sourceLen + - ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; - - /* if can't get parameters, return conservative bound plus zlib wrapper */ - if (strm == Z_NULL || strm->state == Z_NULL) - return complen + 6; - - /* compute wrapper length */ - s = strm->state; - switch (s->wrap) { - case 0: /* raw deflate */ - wraplen = 0; - break; - case 1: /* zlib wrapper */ - wraplen = 6 + (s->strstart ? 4 : 0); - break; - case 2: /* gzip wrapper */ - wraplen = 18; - if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ - if (s->gzhead->extra != Z_NULL) - wraplen += 2 + s->gzhead->extra_len; - str = s->gzhead->name; - if (str != Z_NULL) - do { - wraplen++; - } while (*str++); - str = s->gzhead->comment; - if (str != Z_NULL) - do { - wraplen++; - } while (*str++); - if (s->gzhead->hcrc) - wraplen += 2; - } - break; - default: /* for compiler happiness */ - wraplen = 6; - } - - /* if not default parameters, return conservative bound */ - if (s->w_bits != 15 || s->hash_bits != 8 + 7) - return complen + wraplen; - - /* default settings: return tight bound for that case */ - return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + - (sourceLen >> 25) + 13 - 6 + wraplen; -} - -/* ========================================================================= - * Put a short in the pending buffer. The 16-bit value is put in MSB order. - * IN assertion: the stream state is correct and there is enough room in - * pending_buf. - */ -local void putShortMSB (deflate_state *s, uInt b) -{ - put_byte(s, (Byte)(b >> 8)); - put_byte(s, (Byte)(b & 0xff)); -} - -/* ========================================================================= - * Flush as much pending output as possible. All deflate() output goes - * through this function so some applications may wish to modify it - * to avoid allocating a large strm->next_out buffer and copying into it. - * (See also read_buf()). - */ -local void flush_pending(z_streamp strm) -{ - unsigned len; - deflate_state *s = strm->state; - - _tr_flush_bits(s); - len = s->pending; - if (len > strm->avail_out) len = strm->avail_out; - if (len == 0) return; - - zmemcpy(strm->next_out, s->pending_out, len); - strm->next_out += len; - s->pending_out += len; - strm->total_out += len; - strm->avail_out -= len; - s->pending -= len; - if (s->pending == 0) { - s->pending_out = s->pending_buf; - } -} - -/* ========================================================================= */ -int ZEXPORT deflate (z_streamp strm, int flush) -{ - int old_flush; /* value of flush param for previous deflate call */ - deflate_state *s; - - if (strm == Z_NULL || strm->state == Z_NULL || - flush > Z_BLOCK || flush < 0) { - return Z_STREAM_ERROR; - } - s = strm->state; - - if (strm->next_out == Z_NULL || - (strm->next_in == Z_NULL && strm->avail_in != 0) || - (s->status == FINISH_STATE && flush != Z_FINISH)) { - ERR_RETURN(strm, Z_STREAM_ERROR); - } - if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); - - s->strm = strm; /* just in case */ - old_flush = s->last_flush; - s->last_flush = flush; - - /* Write the header */ - if (s->status == INIT_STATE) { -#ifdef GZIP - if (s->wrap == 2) { - strm->adler = crc32(0L, Z_NULL, 0); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (s->gzhead == Z_NULL) { - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s->status = BUSY_STATE; - } - else { - put_byte(s, (s->gzhead->text ? 1 : 0) + - (s->gzhead->hcrc ? 2 : 0) + - (s->gzhead->extra == Z_NULL ? 0 : 4) + - (s->gzhead->name == Z_NULL ? 0 : 8) + - (s->gzhead->comment == Z_NULL ? 0 : 16) - ); - put_byte(s, (Byte)(s->gzhead->time & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); - put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, s->gzhead->os & 0xff); - if (s->gzhead->extra != Z_NULL) { - put_byte(s, s->gzhead->extra_len & 0xff); - put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); - } - if (s->gzhead->hcrc) - strm->adler = crc32(strm->adler, s->pending_buf, - s->pending); - s->gzindex = 0; - s->status = EXTRA_STATE; - } - } - else -#endif - { - uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; - uInt level_flags; - - if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) - level_flags = 0; - else if (s->level < 6) - level_flags = 1; - else if (s->level == 6) - level_flags = 2; - else - level_flags = 3; - header |= (level_flags << 6); - if (s->strstart != 0) header |= PRESET_DICT; - header += 31 - (header % 31); - - s->status = BUSY_STATE; - putShortMSB(s, header); - - /* Save the adler32 of the preset dictionary: */ - if (s->strstart != 0) { - putShortMSB(s, (uInt)(strm->adler >> 16)); - putShortMSB(s, (uInt)(strm->adler & 0xffff)); - } - strm->adler = adler32(0L, Z_NULL, 0); - } - } -#ifdef GZIP - if (s->status == EXTRA_STATE) { - if (s->gzhead->extra != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ - - while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) - break; - } - put_byte(s, s->gzhead->extra[s->gzindex]); - s->gzindex++; - } - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (s->gzindex == s->gzhead->extra_len) { - s->gzindex = 0; - s->status = NAME_STATE; - } - } - else - s->status = NAME_STATE; - } - if (s->status == NAME_STATE) { - if (s->gzhead->name != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ - int val; - - do { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; - } - } - val = s->gzhead->name[s->gzindex++]; - put_byte(s, val); - } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (val == 0) { - s->gzindex = 0; - s->status = COMMENT_STATE; - } - } - else - s->status = COMMENT_STATE; - } - if (s->status == COMMENT_STATE) { - if (s->gzhead->comment != Z_NULL) { - uInt beg = s->pending; /* start of bytes to update crc */ - int val; - - do { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; - } - } - val = s->gzhead->comment[s->gzindex++]; - put_byte(s, val); - } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (val == 0) - s->status = HCRC_STATE; - } - else - s->status = HCRC_STATE; - } - if (s->status == HCRC_STATE) { - if (s->gzhead->hcrc) { - if (s->pending + 2 > s->pending_buf_size) - flush_pending(strm); - if (s->pending + 2 <= s->pending_buf_size) { - put_byte(s, (Byte)(strm->adler & 0xff)); - put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); - strm->adler = crc32(0L, Z_NULL, 0); - s->status = BUSY_STATE; - } - } - else - s->status = BUSY_STATE; - } -#endif - - /* Flush as much pending output as possible */ - if (s->pending != 0) { - flush_pending(strm); - if (strm->avail_out == 0) { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ - s->last_flush = -1; - return Z_OK; - } - - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Z_FINISH, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && - flush != Z_FINISH) { - ERR_RETURN(strm, Z_BUF_ERROR); - } - - /* User must not provide more input after the first FINISH: */ - if (s->status == FINISH_STATE && strm->avail_in != 0) { - ERR_RETURN(strm, Z_BUF_ERROR); - } - - /* Start a new block or continue the current one. - */ - if (strm->avail_in != 0 || s->lookahead != 0 || - (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { - block_state bstate; - - bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : - (s->strategy == Z_RLE ? deflate_rle(s, flush) : - (*(configuration_table[s->level].func))(s, flush)); - - if (bstate == finish_started || bstate == finish_done) { - s->status = FINISH_STATE; - } - if (bstate == need_more || bstate == finish_started) { - if (strm->avail_out == 0) { - s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ - } - return Z_OK; - /* If flush != Z_NO_FLUSH && avail_out == 0, the next call - * of deflate should use the same flush parameter to make sure - * that the flush is complete. So we don't have to output an - * empty block here, this will be done at next call. This also - * ensures that for a very small output buffer, we emit at most - * one empty block. - */ - } - if (bstate == block_done) { - if (flush == Z_PARTIAL_FLUSH) { - _tr_align(s); - } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ - _tr_stored_block(s, (char*)0, 0L, 0); - /* For a full flush, this empty block will be recognized - * as a special marker by inflate_sync(). - */ - if (flush == Z_FULL_FLUSH) { - CLEAR_HASH(s); /* forget history */ - if (s->lookahead == 0) { - s->strstart = 0; - s->block_start = 0L; - s->insert = 0; - } - } - } - flush_pending(strm); - if (strm->avail_out == 0) { - s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ - return Z_OK; - } - } - } - Assert(strm->avail_out > 0, "bug2"); - - if (flush != Z_FINISH) return Z_OK; - if (s->wrap <= 0) return Z_STREAM_END; - - /* Write the trailer */ -#ifdef GZIP - if (s->wrap == 2) { - put_byte(s, (Byte)(strm->adler & 0xff)); - put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); - put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); - put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); - put_byte(s, (Byte)(strm->total_in & 0xff)); - put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); - put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); - put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); - } - else -#endif - { - putShortMSB(s, (uInt)(strm->adler >> 16)); - putShortMSB(s, (uInt)(strm->adler & 0xffff)); - } - flush_pending(strm); - /* If avail_out is zero, the application will call deflate again - * to flush the rest. - */ - if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ - return s->pending != 0 ? Z_OK : Z_STREAM_END; -} - -/* ========================================================================= */ -int ZEXPORT deflateEnd (z_streamp strm) -{ - int status; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - - status = strm->state->status; - if (status != INIT_STATE && - status != EXTRA_STATE && - status != NAME_STATE && - status != COMMENT_STATE && - status != HCRC_STATE && - status != BUSY_STATE && - status != FINISH_STATE) { - return Z_STREAM_ERROR; - } - - /* Deallocate in reverse order of allocations: */ - TRY_FREE(strm, strm->state->pending_buf); - TRY_FREE(strm, strm->state->head); - TRY_FREE(strm, strm->state->prev); - TRY_FREE(strm, strm->state->window); - - ZFREE(strm, strm->state); - strm->state = Z_NULL; - - return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; -} - -/* ========================================================================= - * Copy the source state to the destination state. - * To simplify the source, this is not supported for 16-bit MSDOS (which - * doesn't have enough memory anyway to duplicate compression states). - */ -int ZEXPORT deflateCopy (z_streamp dest, z_streamp source) -{ -#ifdef MAXSEG_64K - return Z_STREAM_ERROR; -#else - deflate_state *ds; - deflate_state *ss; - ushf *overlay; - - - if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { - return Z_STREAM_ERROR; - } - - ss = source->state; - - zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); - - ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); - if (ds == Z_NULL) return Z_MEM_ERROR; - dest->state = (struct internal_state FAR *) ds; - zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); - ds->strm = dest; - - ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); - ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); - ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); - overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); - ds->pending_buf = (uchf *) overlay; - - if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || - ds->pending_buf == Z_NULL) { - deflateEnd (dest); - return Z_MEM_ERROR; - } - /* following zmemcpy do not work for 16-bit MSDOS */ - zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); - zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); - zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); - zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); - - ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); - ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); - ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; - - ds->l_desc.dyn_tree = ds->dyn_ltree; - ds->d_desc.dyn_tree = ds->dyn_dtree; - ds->bl_desc.dyn_tree = ds->bl_tree; - - return Z_OK; -#endif /* MAXSEG_64K */ -} - -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->next_in buffer and copying from it. - * (See also flush_pending()). - */ -local int read_buf(z_streamp strm, Bytef *buf, unsigned size) -{ - unsigned len = strm->avail_in; - - if (len > size) len = size; - if (len == 0) return 0; - - strm->avail_in -= len; - - zmemcpy(buf, strm->next_in, len); - if (strm->state->wrap == 1) { - strm->adler = adler32(strm->adler, buf, len); - } -#ifdef GZIP - else if (strm->state->wrap == 2) { - strm->adler = crc32(strm->adler, buf, len); - } -#endif - strm->next_in += len; - strm->total_in += len; - - return (int)len; -} - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -local void lm_init (deflate_state *s) -{ - s->window_size = (ulg)2L*s->w_size; - - CLEAR_HASH(s); - - /* Set the default configuration parameters: - */ - s->max_lazy_match = configuration_table[s->level].max_lazy; - s->good_match = configuration_table[s->level].good_length; - s->nice_match = configuration_table[s->level].nice_length; - s->max_chain_length = configuration_table[s->level].max_chain; - - s->strstart = 0; - s->block_start = 0L; - s->lookahead = 0; - s->insert = 0; - s->match_length = s->prev_length = MIN_MATCH-1; - s->match_available = 0; - s->ins_h = 0; -#ifndef FASTEST -#ifdef ASMV - match_init(); /* initialize the asm code */ -#endif -#endif -} - -#ifndef FASTEST -/* =========================================================================== - * Set match_start to the longest match starting at the given string and - * return its length. Matches shorter or equal to prev_length are discarded, - * in which case the result is equal to prev_length and match_start is - * garbage. - * IN assertions: cur_match is the head of the hash chain for the current - * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 - * OUT assertion: the match length is not greater than s->lookahead. - */ -#ifndef ASMV -/* For 80x86 and 680x0, an optimized version will be provided in match.asm or - * match.S. The code will be functionally equivalent. - */ -local uInt longest_match(deflate_state *s, IPos cur_match) -{ - unsigned chain_length = s->max_chain_length;/* max hash chain length */ - register Bytef *scan = s->window + s->strstart; /* current string */ - register Bytef *match; /* matched string */ - register int len; /* length of current match */ - int best_len = s->prev_length; /* best match length so far */ - int nice_match = s->nice_match; /* stop if match long enough */ - IPos limit = s->strstart > (IPos)MAX_DIST(s) ? - s->strstart - (IPos)MAX_DIST(s) : NIL; - /* Stop when cur_match becomes <= limit. To simplify the code, - * we prevent matches with the string of window index 0. - */ - Posf *prev = s->prev; - uInt wmask = s->w_mask; - -#ifdef UNALIGNED_OK - /* Compare two bytes at a time. Note: this is not always beneficial. - * Try with and without -DUNALIGNED_OK to check. - */ - register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; - register ush scan_start = *(ushf*)scan; - register ush scan_end = *(ushf*)(scan+best_len-1); -#else - register Bytef *strend = s->window + s->strstart + MAX_MATCH; - register Byte scan_end1 = scan[best_len-1]; - register Byte scan_end = scan[best_len]; -#endif - - /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - - /* Do not waste too much time if we already have a good match: */ - if (s->prev_length >= s->good_match) { - chain_length >>= 2; - } - /* Do not look for matches beyond the end of the input. This is necessary - * to make deflate deterministic. - */ - if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; - - Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); - - do { - Assert(cur_match < s->strstart, "no future"); - match = s->window + cur_match; - - /* Skip to next match if the match length cannot increase - * or if the match length is less than 2. Note that the checks below - * for insufficient lookahead only occur occasionally for performance - * reasons. Therefore uninitialized memory will be accessed, and - * conditional jumps will be made that depend on those values. - * However the length of the match is limited to the lookahead, so - * the output of deflate is not affected by the uninitialized values. - */ -#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) - /* This code assumes sizeof(unsigned short) == 2. Do not use - * UNALIGNED_OK if your compiler uses a different size. - */ - if (*(ushf*)(match+best_len-1) != scan_end || - *(ushf*)match != scan_start) continue; - - /* It is not necessary to compare scan[2] and match[2] since they are - * always equal when the other bytes match, given that the hash keys - * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at - * strstart+3, +5, ... up to strstart+257. We check for insufficient - * lookahead only every 4th comparison; the 128th check will be made - * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is - * necessary to put more guard bytes at the end of the window, or - * to check more often for insufficient lookahead. - */ - Assert(scan[2] == match[2], "scan[2]?"); - scan++, match++; - do { - } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && - *(ushf*)(scan+=2) == *(ushf*)(match+=2) && - *(ushf*)(scan+=2) == *(ushf*)(match+=2) && - *(ushf*)(scan+=2) == *(ushf*)(match+=2) && - scan < strend); - /* The funny "do {}" generates better code on most compilers */ - - /* Here, scan <= window+strstart+257 */ - Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); - if (*scan == *match) scan++; - - len = (MAX_MATCH - 1) - (int)(strend-scan); - scan = strend - (MAX_MATCH-1); - -#else /* UNALIGNED_OK */ - - if (match[best_len] != scan_end || - match[best_len-1] != scan_end1 || - *match != *scan || - *++match != scan[1]) continue; - - /* The check at best_len-1 can be removed because it will be made - * again later. (This heuristic is not always a win.) - * It is not necessary to compare scan[2] and match[2] since they - * are always equal when the other bytes match, given that - * the hash keys are equal and that HASH_BITS >= 8. - */ - scan += 2, match++; - Assert(*scan == *match, "match[2]?"); - - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. - */ - do { - } while (*++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - scan < strend); - - Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); - - len = MAX_MATCH - (int)(strend - scan); - scan = strend - MAX_MATCH; - -#endif /* UNALIGNED_OK */ - - if (len > best_len) { - s->match_start = cur_match; - best_len = len; - if (len >= nice_match) break; -#ifdef UNALIGNED_OK - scan_end = *(ushf*)(scan+best_len-1); -#else - scan_end1 = scan[best_len-1]; - scan_end = scan[best_len]; -#endif - } - } while ((cur_match = prev[cur_match & wmask]) > limit - && --chain_length != 0); - - if ((uInt)best_len <= s->lookahead) return (uInt)best_len; - return s->lookahead; -} -#endif /* ASMV */ - -#else /* FASTEST */ - -/* --------------------------------------------------------------------------- - * Optimized version for FASTEST only - */ -local uInt longest_match(s, cur_match) - deflate_state *s; - IPos cur_match; /* current match */ -{ - register Bytef *scan = s->window + s->strstart; /* current string */ - register Bytef *match; /* matched string */ - register int len; /* length of current match */ - register Bytef *strend = s->window + s->strstart + MAX_MATCH; - - /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - - Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); - - Assert(cur_match < s->strstart, "no future"); - - match = s->window + cur_match; - - /* Return failure if the match length is less than 2: - */ - if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; - - /* The check at best_len-1 can be removed because it will be made - * again later. (This heuristic is not always a win.) - * It is not necessary to compare scan[2] and match[2] since they - * are always equal when the other bytes match, given that - * the hash keys are equal and that HASH_BITS >= 8. - */ - scan += 2, match += 2; - Assert(*scan == *match, "match[2]?"); - - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. - */ - do { - } while (*++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - scan < strend); - - Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); - - len = MAX_MATCH - (int)(strend - scan); - - if (len < MIN_MATCH) return MIN_MATCH - 1; - - s->match_start = cur_match; - return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; -} - -#endif /* FASTEST */ - -#ifdef DEBUG -/* =========================================================================== - * Check that the match at match_start is indeed a match. - */ -local void check_match(s, start, match, length) - deflate_state *s; - IPos start, match; - int length; -{ - /* check that the match is indeed a match */ - if (zmemcmp(s->window + match, - s->window + start, length) != EQUAL) { - fprintf(stderr, " start %u, match %u, length %d\n", - start, match, length); - do { - fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); - } while (--length != 0); - z_error("invalid match"); - } - if (z_verbose > 1) { - fprintf(stderr,"\\[%d,%d]", start-match, length); - do { putc(s->window[start++], stderr); } while (--length != 0); - } -} -#else -# define check_match(s, start, match, length) -#endif /* DEBUG */ - -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -local void fill_window(deflate_state *s) -{ - register unsigned n, m; - register Posf *p; - unsigned more; /* Amount of free space at the end of the window. */ - uInt wsize = s->w_size; - - Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); - - /* Deal with !@#$% 64K limit: */ - if (sizeof(int) <= 2) { - if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - more = wsize; - - } else if (more == (unsigned)(-1)) { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s->strstart >= wsize+MAX_DIST(s)) { - - zmemcpy(s->window, s->window+wsize, (unsigned)wsize); - s->match_start -= wsize; - s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ - s->block_start -= (long) wsize; - - /* Slide the hash table (could be avoided with 32 bit values - at the expense of memory usage). We slide even when level == 0 - to keep the hash table consistent if we switch back to level > 0 - later. (Using level 0 permanently is not an optimal usage of - zlib, so we don't care about this pathological case.) - */ - n = s->hash_size; - p = &s->head[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m-wsize : NIL); - } while (--n); - - n = wsize; -#ifndef FASTEST - p = &s->prev[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m-wsize : NIL); - /* If n is not on any hash chain, prev[n] is garbage but - * its value will never be used. - */ - } while (--n); -#endif - more += wsize; - } - if (s->strm->avail_in == 0) break; - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - Assert(more >= 2, "more < 2"); - - n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); - s->lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s->lookahead + s->insert >= MIN_MATCH) { - uInt str = s->strstart - s->insert; - s->ins_h = s->window[str]; - UPDATE_HASH(s, s->ins_h, s->window[str + 1]); -#if MIN_MATCH != 3 - Call UPDATE_HASH() MIN_MATCH-3 more times -#endif - while (s->insert) { - UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); -#ifndef FASTEST - s->prev[str & s->w_mask] = s->head[s->ins_h]; -#endif - s->head[s->ins_h] = (Pos)str; - str++; - s->insert--; - if (s->lookahead + s->insert < MIN_MATCH) - break; - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ - if (s->high_water < s->window_size) { - ulg curr = s->strstart + (ulg)(s->lookahead); - ulg init; - - if (s->high_water < curr) { - /* Previous high water mark below current data -- zero WIN_INIT - * bytes or up to end of window, whichever is less. - */ - init = s->window_size - curr; - if (init > WIN_INIT) - init = WIN_INIT; - zmemzero(s->window + curr, (unsigned)init); - s->high_water = curr + init; - } - else if (s->high_water < (ulg)curr + WIN_INIT) { - /* High water mark at or above current data, but below current data - * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up - * to end of window, whichever is less. - */ - init = (ulg)curr + WIN_INIT - s->high_water; - if (init > s->window_size - s->high_water) - init = s->window_size - s->high_water; - zmemzero(s->window + s->high_water, (unsigned)init); - s->high_water += init; - } - } - - Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "not enough room for search"); -} - -/* =========================================================================== - * Flush the current block, with given end-of-file flag. - * IN assertion: strstart is set to the end of the current match. - */ -#define FLUSH_BLOCK_ONLY(s, last) { \ - _tr_flush_block(s, (s->block_start >= 0L ? \ - (charf *)&s->window[(unsigned)s->block_start] : \ - (charf *)Z_NULL), \ - (ulg)((long)s->strstart - s->block_start), \ - (last)); \ - s->block_start = s->strstart; \ - flush_pending(s->strm); \ - Tracev((stderr,"[FLUSH]")); \ -} - -/* Same but force premature exit if necessary. */ -#define FLUSH_BLOCK(s, last) { \ - FLUSH_BLOCK_ONLY(s, last); \ - if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ -} - -/* =========================================================================== - * Copy without compression as much as possible from the input stream, return - * the current block state. - * This function does not insert new strings in the dictionary since - * uncompressible data is probably not useful. This function is used - * only for the level=0 compression option. - * NOTE: this function should be optimized to avoid extra copying from - * window to pending_buf. - */ -local block_state deflate_stored(deflate_state *s, int flush) -{ - /* Stored blocks are limited to 0xffff bytes, pending_buf is limited - * to pending_buf_size, and each stored block has a 5 byte header: - */ - ulg max_block_size = 0xffff; - ulg max_start; - - if (max_block_size > s->pending_buf_size - 5) { - max_block_size = s->pending_buf_size - 5; - } - - /* Copy as much as possible from input to output: */ - for (;;) { - /* Fill the window as much as possible: */ - if (s->lookahead <= 1) { - - Assert(s->strstart < s->w_size+MAX_DIST(s) || - s->block_start >= (long)s->w_size, "slide too late"); - - fill_window(s); - if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; - - if (s->lookahead == 0) break; /* flush the current block */ - } - Assert(s->block_start >= 0L, "block gone"); - - s->strstart += s->lookahead; - s->lookahead = 0; - - /* Emit a stored block if pending_buf will be full: */ - max_start = s->block_start + max_block_size; - if (s->strstart == 0 || (ulg)s->strstart >= max_start) { - /* strstart == 0 is possible when wraparound on 16-bit machine */ - s->lookahead = (uInt)(s->strstart - max_start); - s->strstart = (uInt)max_start; - FLUSH_BLOCK(s, 0); - } - /* Flush if we may have to slide, otherwise block_start may become - * negative and the data will be gone: - */ - if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { - FLUSH_BLOCK(s, 0); - } - } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if ((long)s->strstart > s->block_start) - FLUSH_BLOCK(s, 0); - return block_done; -} - -/* =========================================================================== - * Compress as much as possible from the input stream, return the current - * block state. - * This function does not perform lazy evaluation of matches and inserts - * new strings in the dictionary only for unmatched strings or for short - * matches. It is used only for the fast compression options. - */ -local block_state deflate_fast(deflate_state *s, int flush) -{ - IPos hash_head; /* head of the hash chain */ - int bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s->lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = NIL; - if (s->lookahead >= MIN_MATCH) { - INSERT_STRING(s, s->strstart, hash_head); - } - - /* Find the longest match, discarding those <= prev_length. - * At this point we have always match_length < MIN_MATCH - */ - if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s->match_length = longest_match (s, hash_head); - /* longest_match() sets match_start */ - } - if (s->match_length >= MIN_MATCH) { - check_match(s, s->strstart, s->match_start, s->match_length); - - _tr_tally_dist(s, s->strstart - s->match_start, - s->match_length - MIN_MATCH, bflush); - - s->lookahead -= s->match_length; - - /* Insert new strings in the hash table only if the match length - * is not too large. This saves time but degrades compression. - */ -#ifndef FASTEST - if (s->match_length <= s->max_insert_length && - s->lookahead >= MIN_MATCH) { - s->match_length--; /* string at strstart already in table */ - do { - s->strstart++; - INSERT_STRING(s, s->strstart, hash_head); - /* strstart never exceeds WSIZE-MAX_MATCH, so there are - * always MIN_MATCH bytes ahead. - */ - } while (--s->match_length != 0); - s->strstart++; - } else -#endif - { - s->strstart += s->match_length; - s->match_length = 0; - s->ins_h = s->window[s->strstart]; - UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); -#if MIN_MATCH != 3 - Call UPDATE_HASH() MIN_MATCH-3 more times -#endif - /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not - * matter since it will be recomputed at next deflate call. - */ - } - } else { - /* No match, output a literal byte */ - Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit (s, s->window[s->strstart], bflush); - s->lookahead--; - s->strstart++; - } - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->last_lit) - FLUSH_BLOCK(s, 0); - return block_done; -} - -#ifndef FASTEST -/* =========================================================================== - * Same as above, but achieves better compression. We use a lazy - * evaluation for matches: a match is finally adopted only if there is - * no better match at the next window position. - */ -local block_state deflate_slow(deflate_state *s, int flush) -{ - IPos hash_head; /* head of hash chain */ - int bflush; /* set if current block must be flushed */ - - /* Process the input block. */ - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s->lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = NIL; - if (s->lookahead >= MIN_MATCH) { - INSERT_STRING(s, s->strstart, hash_head); - } - - /* Find the longest match, discarding those <= prev_length. - */ - s->prev_length = s->match_length, s->prev_match = s->match_start; - s->match_length = MIN_MATCH-1; - - if (hash_head != NIL && s->prev_length < s->max_lazy_match && - s->strstart - hash_head <= MAX_DIST(s)) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s->match_length = longest_match (s, hash_head); - /* longest_match() sets match_start */ - - if (s->match_length <= 5 && (s->strategy == Z_FILTERED -#if TOO_FAR <= 32767 - || (s->match_length == MIN_MATCH && - s->strstart - s->match_start > TOO_FAR) -#endif - )) { - - /* If prev_match is also MIN_MATCH, match_start is garbage - * but we will ignore the current match anyway. - */ - s->match_length = MIN_MATCH-1; - } - } - /* If there was a match at the previous step and the current - * match is not better, output the previous match: - */ - if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { - uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; - /* Do not insert strings in hash table beyond this. */ - - check_match(s, s->strstart-1, s->prev_match, s->prev_length); - - _tr_tally_dist(s, s->strstart -1 - s->prev_match, - s->prev_length - MIN_MATCH, bflush); - - /* Insert in hash table all strings up to the end of the match. - * strstart-1 and strstart are already inserted. If there is not - * enough lookahead, the last two strings are not inserted in - * the hash table. - */ - s->lookahead -= s->prev_length-1; - s->prev_length -= 2; - do { - if (++s->strstart <= max_insert) { - INSERT_STRING(s, s->strstart, hash_head); - } - } while (--s->prev_length != 0); - s->match_available = 0; - s->match_length = MIN_MATCH-1; - s->strstart++; - - if (bflush) FLUSH_BLOCK(s, 0); - - } else if (s->match_available) { - /* If there was no match at the previous position, output a - * single literal. If there was a match but the current match - * is longer, truncate the previous match to a single literal. - */ - Tracevv((stderr,"%c", s->window[s->strstart-1])); - _tr_tally_lit(s, s->window[s->strstart-1], bflush); - if (bflush) { - FLUSH_BLOCK_ONLY(s, 0); - } - s->strstart++; - s->lookahead--; - if (s->strm->avail_out == 0) return need_more; - } else { - /* There is no previous match to compare with, wait for - * the next step to decide. - */ - s->match_available = 1; - s->strstart++; - s->lookahead--; - } - } - Assert (flush != Z_NO_FLUSH, "no flush?"); - if (s->match_available) { - Tracevv((stderr,"%c", s->window[s->strstart-1])); - _tr_tally_lit(s, s->window[s->strstart-1], bflush); - s->match_available = 0; - } - s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->last_lit) - FLUSH_BLOCK(s, 0); - return block_done; -} -#endif /* FASTEST */ - -/* =========================================================================== - * For Z_RLE, simply look for runs of bytes, generate matches only of distance - * one. Do not maintain a hash table. (It will be regenerated if this run of - * deflate switches away from Z_RLE.) - */ -local block_state deflate_rle(deflate_state *s, int flush) -{ - int bflush; /* set if current block must be flushed */ - uInt prev; /* byte at distance one to match */ - Bytef *scan, *strend; /* scan goes up to strend for length of run */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the longest run, plus one for the unrolled loop. - */ - if (s->lookahead <= MAX_MATCH) { - fill_window(s); - if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* See how many times the previous byte repeats */ - s->match_length = 0; - if (s->lookahead >= MIN_MATCH && s->strstart > 0) { - scan = s->window + s->strstart - 1; - prev = *scan; - if (prev == *++scan && prev == *++scan && prev == *++scan) { - strend = s->window + s->strstart + MAX_MATCH; - do { - } while (prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - scan < strend); - s->match_length = MAX_MATCH - (int)(strend - scan); - if (s->match_length > s->lookahead) - s->match_length = s->lookahead; - } - Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); - } - - /* Emit match if have run of MIN_MATCH or longer, else emit literal */ - if (s->match_length >= MIN_MATCH) { - check_match(s, s->strstart, s->strstart - 1, s->match_length); - - _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); - - s->lookahead -= s->match_length; - s->strstart += s->match_length; - s->match_length = 0; - } else { - /* No match, output a literal byte */ - Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit (s, s->window[s->strstart], bflush); - s->lookahead--; - s->strstart++; - } - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->last_lit) - FLUSH_BLOCK(s, 0); - return block_done; -} - -/* =========================================================================== - * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. - * (It will be regenerated if this run of deflate switches away from Huffman.) - */ -local block_state deflate_huff(deflate_state *s, int flush) -{ - int bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we have a literal to write. */ - if (s->lookahead == 0) { - fill_window(s); - if (s->lookahead == 0) { - if (flush == Z_NO_FLUSH) - return need_more; - break; /* flush the current block */ - } - } - - /* Output a literal byte */ - s->match_length = 0; - Tracevv((stderr,"%c", s->window[s->strstart])); - _tr_tally_lit (s, s->window[s->strstart], bflush); - s->lookahead--; - s->strstart++; - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->last_lit) - FLUSH_BLOCK(s, 0); - return block_done; -} diff --git a/builtins/zlib/deflate.h b/builtins/zlib/deflate.h deleted file mode 100644 index ce0299edd1916..0000000000000 --- a/builtins/zlib/deflate.h +++ /dev/null @@ -1,346 +0,0 @@ -/* deflate.h -- internal compression state - * Copyright (C) 1995-2012 Jean-loup Gailly - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* @(#) $Id$ */ - -#ifndef DEFLATE_H -#define DEFLATE_H - -#include "zutil.h" - -/* define NO_GZIP when compiling if you want to disable gzip header and - trailer creation by deflate(). NO_GZIP would be used to avoid linking in - the crc code when it is not needed. For shared libraries, gzip encoding - should be left enabled. */ -#ifndef NO_GZIP -# define GZIP -#endif - -/* =========================================================================== - * Internal compression state. - */ - -#define LENGTH_CODES 29 -/* number of length codes, not counting the special END_BLOCK code */ - -#define LITERALS 256 -/* number of literal bytes 0..255 */ - -#define L_CODES (LITERALS+1+LENGTH_CODES) -/* number of Literal or Length codes, including the END_BLOCK code */ - -#define D_CODES 30 -/* number of distance codes */ - -#define BL_CODES 19 -/* number of codes used to transfer the bit lengths */ - -#define HEAP_SIZE (2*L_CODES+1) -/* maximum heap size */ - -#define MAX_BITS 15 -/* All codes must not exceed MAX_BITS bits */ - -#define Buf_size 16 -/* size of bit buffer in bi_buf */ - -#define INIT_STATE 42 -#define EXTRA_STATE 69 -#define NAME_STATE 73 -#define COMMENT_STATE 91 -#define HCRC_STATE 103 -#define BUSY_STATE 113 -#define FINISH_STATE 666 -/* Stream status */ - - -/* Data structure describing a single value and its code string. */ -typedef struct ct_data_s { - union { - ush freq; /* frequency count */ - ush code; /* bit string */ - } fc; - union { - ush dad; /* father node in Huffman tree */ - ush len; /* length of bit string */ - } dl; -} FAR ct_data; - -#define Freq fc.freq -#define Code fc.code -#define Dad dl.dad -#define Len dl.len - -typedef struct static_tree_desc_s static_tree_desc; - -typedef struct tree_desc_s { - ct_data *dyn_tree; /* the dynamic tree */ - int max_code; /* largest code with non zero frequency */ - static_tree_desc *stat_desc; /* the corresponding static tree */ -} FAR tree_desc; - -typedef ush Pos; -typedef Pos FAR Posf; -typedef unsigned IPos; - -/* A Pos is an index in the character window. We use short instead of int to - * save space in the various tables. IPos is used only for parameter passing. - */ - -typedef struct internal_state { - z_streamp strm; /* pointer back to this zlib stream */ - int status; /* as the name implies */ - Bytef *pending_buf; /* output still pending */ - ulg pending_buf_size; /* size of pending_buf */ - Bytef *pending_out; /* next pending byte to output to the stream */ - uInt pending; /* nb of bytes in the pending buffer */ - int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ - gz_headerp gzhead; /* gzip header information to write */ - uInt gzindex; /* where in extra, name, or comment */ - Byte method; /* can only be DEFLATED */ - int last_flush; /* value of flush param for previous deflate call */ - - /* used by deflate.c: */ - - uInt w_size; /* LZ77 window size (32K by default) */ - uInt w_bits; /* log2(w_size) (8..16) */ - uInt w_mask; /* w_size - 1 */ - - Bytef *window; - /* Sliding window. Input bytes are read into the second half of the window, - * and move to the first half later to keep a dictionary of at least wSize - * bytes. With this organization, matches are limited to a distance of - * wSize-MAX_MATCH bytes, but this ensures that IO is always - * performed with a length multiple of the block size. Also, it limits - * the window size to 64K, which is quite useful on MSDOS. - * To do: use the user input buffer as sliding window. - */ - - ulg window_size; - /* Actual size of window: 2*wSize, except when the user input buffer - * is directly used as sliding window. - */ - - Posf *prev; - /* Link to older string with same hash index. To limit the size of this - * array to 64K, this link is maintained only for the last 32K strings. - * An index in this array is thus a window index modulo 32K. - */ - - Posf *head; /* Heads of the hash chains or NIL. */ - - uInt ins_h; /* hash index of string to be inserted */ - uInt hash_size; /* number of elements in hash table */ - uInt hash_bits; /* log2(hash_size) */ - uInt hash_mask; /* hash_size-1 */ - - uInt hash_shift; - /* Number of bits by which ins_h must be shifted at each input - * step. It must be such that after MIN_MATCH steps, the oldest - * byte no longer takes part in the hash key, that is: - * hash_shift * MIN_MATCH >= hash_bits - */ - - long block_start; - /* Window position at the beginning of the current output block. Gets - * negative when the window is moved backwards. - */ - - uInt match_length; /* length of best match */ - IPos prev_match; /* previous match */ - int match_available; /* set if previous match exists */ - uInt strstart; /* start of string to insert */ - uInt match_start; /* start of matching string */ - uInt lookahead; /* number of valid bytes ahead in window */ - - uInt prev_length; - /* Length of the best match at previous step. Matches not greater than this - * are discarded. This is used in the lazy match evaluation. - */ - - uInt max_chain_length; - /* To speed up deflation, hash chains are never searched beyond this - * length. A higher limit improves compression ratio but degrades the - * speed. - */ - - uInt max_lazy_match; - /* Attempt to find a better match only when the current match is strictly - * smaller than this value. This mechanism is used only for compression - * levels >= 4. - */ -# define max_insert_length max_lazy_match - /* Insert new strings in the hash table only if the match length is not - * greater than this length. This saves time but degrades compression. - * max_insert_length is used only for compression levels <= 3. - */ - - int level; /* compression level (1..9) */ - int strategy; /* favor or force Huffman coding*/ - - uInt good_match; - /* Use a faster search when the previous match is longer than this */ - - int nice_match; /* Stop searching when current match exceeds this */ - - /* used by trees.c: */ - /* Didn't use ct_data typedef below to suppress compiler warning */ - struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ - struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ - struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ - - struct tree_desc_s l_desc; /* desc. for literal tree */ - struct tree_desc_s d_desc; /* desc. for distance tree */ - struct tree_desc_s bl_desc; /* desc. for bit length tree */ - - ush bl_count[MAX_BITS+1]; - /* number of codes at each bit length for an optimal tree */ - - int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ - int heap_len; /* number of elements in the heap */ - int heap_max; /* element of largest frequency */ - /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - * The same heap array is used to build all trees. - */ - - uch depth[2*L_CODES+1]; - /* Depth of each subtree used as tie breaker for trees of equal frequency - */ - - uchf *l_buf; /* buffer for literals or lengths */ - - uInt lit_bufsize; - /* Size of match buffer for literals/lengths. There are 4 reasons for - * limiting lit_bufsize to 64K: - * - frequencies can be kept in 16 bit counters - * - if compression is not successful for the first block, all input - * data is still in the window so we can still emit a stored block even - * when input comes from standard input. (This can also be done for - * all blocks if lit_bufsize is not greater than 32K.) - * - if compression is not successful for a file smaller than 64K, we can - * even emit a stored file instead of a stored block (saving 5 bytes). - * This is applicable only for zip (not gzip or zlib). - * - creating new Huffman trees less frequently may not provide fast - * adaptation to changes in the input data statistics. (Take for - * example a binary file with poorly compressible code followed by - * a highly compressible string table.) Smaller buffer sizes give - * fast adaptation but have of course the overhead of transmitting - * trees more frequently. - * - I can't count above 4 - */ - - uInt last_lit; /* running index in l_buf */ - - ushf *d_buf; - /* Buffer for distances. To simplify the code, d_buf and l_buf have - * the same number of elements. To use different lengths, an extra flag - * array would be necessary. - */ - - ulg opt_len; /* bit length of current block with optimal trees */ - ulg static_len; /* bit length of current block with static trees */ - uInt matches; /* number of string matches in current block */ - uInt insert; /* bytes at end of window left to insert */ - -#ifdef DEBUG - ulg compressed_len; /* total bit length of compressed file mod 2^32 */ - ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ -#endif - - ush bi_buf; - /* Output buffer. bits are inserted starting at the bottom (least - * significant bits). - */ - int bi_valid; - /* Number of valid bits in bi_buf. All bits above the last valid bit - * are always zero. - */ - - ulg high_water; - /* High water mark offset in window for initialized bytes -- bytes above - * this are set to zero in order to avoid memory check warnings when - * longest match routines access bytes past the input. This is then - * updated to the new high water mark. - */ - -} FAR deflate_state; - -/* Output a byte on the stream. - * IN assertion: there is enough room in pending_buf. - */ -#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} - - -#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) -/* Minimum amount of lookahead, except at the end of the input file. - * See deflate.c for comments about the MIN_MATCH+1. - */ - -#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) -/* In order to simplify the code, particularly on 16 bit machines, match - * distances are limited to MAX_DIST instead of WSIZE. - */ - -#define WIN_INIT MAX_MATCH -/* Number of bytes after end of data in window to initialize in order to avoid - memory checker errors from longest match routines */ - - /* in trees.c */ -void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); -int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); -void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, - ulg stored_len, int last)); -void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); -void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); -void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, - ulg stored_len, int last)); - -#define d_code(dist) \ - ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) -/* Mapping from a distance to a distance code. dist is the distance - 1 and - * must not have side effects. _dist_code[256] and _dist_code[257] are never - * used. - */ - -#ifndef DEBUG -/* Inline versions of _tr_tally for speed: */ - -#if defined(GEN_TREES_H) || !defined(STDC) - extern uch ZLIB_INTERNAL _length_code[]; - extern uch ZLIB_INTERNAL _dist_code[]; -#else - extern const uch ZLIB_INTERNAL _length_code[]; - extern const uch ZLIB_INTERNAL _dist_code[]; -#endif - -# define _tr_tally_lit(s, c, flush) \ - { uch cc = (c); \ - s->d_buf[s->last_lit] = 0; \ - s->l_buf[s->last_lit++] = cc; \ - s->dyn_ltree[cc].Freq++; \ - flush = (s->last_lit == s->lit_bufsize-1); \ - } -# define _tr_tally_dist(s, distance, length, flush) \ - { uch len = (length); \ - ush dist = (distance); \ - s->d_buf[s->last_lit] = dist; \ - s->l_buf[s->last_lit++] = len; \ - dist--; \ - s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ - s->dyn_dtree[d_code(dist)].Freq++; \ - flush = (s->last_lit == s->lit_bufsize-1); \ - } -#else -# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) -# define _tr_tally_dist(s, distance, length, flush) \ - flush = _tr_tally(s, distance, length) -#endif - -#endif /* DEFLATE_H */ diff --git a/builtins/zlib/deflate_cf.c b/builtins/zlib/deflate_cf.c deleted file mode 100644 index 54aebec62a838..0000000000000 --- a/builtins/zlib/deflate_cf.c +++ /dev/null @@ -1,3211 +0,0 @@ -/* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * ALGORITHM - * - * The "deflation" process depends on being able to identify portions - * of the input text which are identical to earlier input (within a - * sliding window trailing behind the input currently being processed). - * - * The most straightforward technique turns out to be the fastest for - * most input files: try all possible matches and select the longest. - * The key feature of this algorithm is that insertions into the string - * dictionary are very simple and thus fast, and deletions are avoided - * completely. Insertions are performed at each input character, whereas - * string matches are performed only when the previous match ends. So it - * is preferable to spend more time in matches to allow very fast string - * insertions and avoid deletions. The matching algorithm for small - * strings is inspired from that of Rabin & Karp. A brute force approach - * is used to find longer strings when a small match has been found. - * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze - * (by Leonid Broukhis). - * A previous version of this file used a more sophisticated algorithm - * (by Fiala and Greene) which is guaranteed to run in linear amortized - * time, but has a larger average cost, uses more memory and is patented. - * However the F&G algorithm may be faster for some highly redundant - * files if the parameter max_chain_length (described below) is too large. - * - * ACKNOWLEDGEMENTS - * - * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and - * I found it in 'freeze' written by Leonid Broukhis. - * Thanks to many people for bug reports and testing. - * - * REFERENCES - * - * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". - * Available in http://tools.ietf.org/html/rfc1951 - * - * A description of the Rabin and Karp algorithm is given in the book - * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. - * - * Fiala,E.R., and Greene,D.H. - * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 - * - */ - -#if defined(__cplusplus) -#define UNUSED(x) // = nothing -#elif defined(__GNUC__) -#define UNUSED(x) x##_UNUSED __attribute__((unused)) -#else -#define UNUSED(x) x##_UNUSED -#endif - -/* @(#) $Id$ */ - -#include "deflate_cf.h" - -#ifdef __x86_64__ -#include "cpuid.h" -#endif - -#if defined(_MSC_VER) - /* Microsoft C/C++-compatible compiler */ - #include -#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) - /* GCC-compatible compiler, targeting x86/x86-64 */ - #include -#elif defined(__GNUC__) && defined(__ARM_NEON__) - /* GCC-compatible compiler, targeting ARM with NEON */ - #include -#elif defined(__GNUC__) && defined(__IWMMXT__) - /* GCC-compatible compiler, targeting ARM with WMMX */ - #include -#elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__)) - /* XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX */ - #include -#elif defined(__GNUC__) && defined(__SPE__) - /* GCC-compatible compiler, targeting PowerPC with SPE */ - #include -#elif defined(__clang__) && defined(__linux__) - #include -#endif - -const char deflate_copyright[] = - " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; -/* - If you use the zlib library in a product, an acknowledgment is welcome - in the documentation of your product. If for some reason you cannot - include such an acknowledgment, I would appreciate that you keep this - copyright string in the executable of your product. - */ - -/* =========================================================================== - * Function prototypes. - */ -typedef enum { - need_more, /* block not completed, need more input or more output */ - block_done, /* block flush performed */ - finish_started, /* finish started, need only more output at next deflate */ - finish_done /* finish done, accept no more input or output */ -} block_state; - -typedef block_state (*compress_func)(deflate_state *s, int flush); -/* Compression function. Returns the block state after the call. */ - -static void fill_window(deflate_state *s); -static block_state deflate_stored(deflate_state *s, int flush); -static block_state deflate_fast(deflate_state *s, int flush); -static block_state deflate_slow(deflate_state *s, int flush); -static block_state deflate_rle(deflate_state *s, int flush); -static block_state deflate_huff(deflate_state *s, int flush); -static void lm_init(deflate_state *s); -static void putShortMSB(deflate_state *s, uint32_t b); -static void flush_pending(z_streamp strm); -static int read_buf(z_streamp strm, uint8_t *buf, uint32_t size); - -#ifdef DEBUG -static void check_match(deflate_state *s, IPos start, IPos match, - int length); -#endif - -/* =========================================================================== - * Local data - */ - -#define NIL 0 -/* Tail of hash chains */ -#define ACTUAL_MIN_MATCH 4 -/* Values for max_lazy_match, good_match and max_chain_length, depending on - * the desired pack level (0..9). The values given below have been tuned to - * exclude worst case performance for pathological files. Better values may be - * found for specific files. - */ -typedef struct config_s { - uint16_t good_length; /* reduce lazy search above this match length */ - uint16_t max_lazy; /* do not perform lazy search above this match length */ - uint16_t nice_length; /* quit search above this match length */ - uint16_t max_chain; - compress_func func; -} config; - -static const config configuration_table[10] = { -/* good lazy nice chain */ -/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ -/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ -/* 2 */ {4, 5, 16, 8, deflate_fast}, -/* 3 */ {4, 6, 32, 32, deflate_fast}, - -/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ -/* 5 */ {8, 16, 32, 32, deflate_slow}, -/* 6 */ {8, 16, 128, 128, deflate_slow}, -/* 7 */ {8, 32, 128, 256, deflate_slow}, -/* 8 */ {32, 128, 258, 1024, deflate_slow}, -/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ - -/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 - * For deflate_fast() (levels <= 3) good is ignored and lazy has a different - * meaning. - */ - -#define EQUAL 0 -/* result of memcmp for equal strings */ - -/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ -#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) - -// ////////////////////////////////////////////////////////// -// Crc32.h -// Copyright (c) 2011-2016 Stephan Brumme. All rights reserved. -// Slicing-by-16 contributed by Bulat Ziganshin -// Tableless bytewise CRC contributed by Hagai Gold -// see http://create.stephan-brumme.com/disclaimer.html -// - -#ifdef _MSC_VER -#define PREFETCH(location) _mm_prefetch(location, _MM_HINT_T0) -#else -#ifdef __GNUC__ -#define PREFETCH(location) __builtin_prefetch(location) -#else -#define PREFETCH(location); -#endif -#endif - -// zlib's CRC32 polynomial -const uint32_t Polynomial = 0xEDB88320; - -/// swap endianess -static inline uint32_t swap(uint32_t x) -{ -#if defined(__GNUC__) || defined(__clang__) - return __builtin_bswap32(x); -#else - return (x >> 24) | - ((x >> 8) & 0x0000FF00) | - ((x << 8) & 0x00FF0000) | - (x << 24); -#endif -} - -/// Slicing-By-16 -#define MaxSlice 16 - -/// look-up table, already declared above -const uint32_t Crc32Lookup[MaxSlice][256] = -{ - //// same algorithm as crc32_bitwise - //for (int i = 0; i <= 0xFF; i++) - //{ - // uint32_t crc = i; - // for (int j = 0; j < 8; j++) - // crc = (crc >> 1) ^ ((crc & 1) * Polynomial); - // Crc32Lookup[0][i] = crc; - //} - //// ... and the following slicing-by-8 algorithm (from Intel): - //// http://www.intel.com/technology/comms/perfnet/download/CRC_generators.pdf - //// http://sourceforge.net/projects/slicing-by-8/ - //for (int slice = 1; slice < MaxSlice; slice++) - // Crc32Lookup[slice][i] = (Crc32Lookup[slice - 1][i] >> 8) ^ Crc32Lookup[0][Crc32Lookup[slice - 1][i] & 0xFF]; - { - // note: the first number of every second row corresponds to the half-byte look-up table ! - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, - }, - - // beyond this point only relevant for Slicing-by-4, Slicing-by-8 and Slicing-by-16 - { - 0x00000000, 0x191B3141, 0x32366282, 0x2B2D53C3, 0x646CC504, 0x7D77F445, 0x565AA786, 0x4F4196C7, - 0xC8D98A08, 0xD1C2BB49, 0xFAEFE88A, 0xE3F4D9CB, 0xACB54F0C, 0xB5AE7E4D, 0x9E832D8E, 0x87981CCF, - 0x4AC21251, 0x53D92310, 0x78F470D3, 0x61EF4192, 0x2EAED755, 0x37B5E614, 0x1C98B5D7, 0x05838496, - 0x821B9859, 0x9B00A918, 0xB02DFADB, 0xA936CB9A, 0xE6775D5D, 0xFF6C6C1C, 0xD4413FDF, 0xCD5A0E9E, - 0x958424A2, 0x8C9F15E3, 0xA7B24620, 0xBEA97761, 0xF1E8E1A6, 0xE8F3D0E7, 0xC3DE8324, 0xDAC5B265, - 0x5D5DAEAA, 0x44469FEB, 0x6F6BCC28, 0x7670FD69, 0x39316BAE, 0x202A5AEF, 0x0B07092C, 0x121C386D, - 0xDF4636F3, 0xC65D07B2, 0xED705471, 0xF46B6530, 0xBB2AF3F7, 0xA231C2B6, 0x891C9175, 0x9007A034, - 0x179FBCFB, 0x0E848DBA, 0x25A9DE79, 0x3CB2EF38, 0x73F379FF, 0x6AE848BE, 0x41C51B7D, 0x58DE2A3C, - 0xF0794F05, 0xE9627E44, 0xC24F2D87, 0xDB541CC6, 0x94158A01, 0x8D0EBB40, 0xA623E883, 0xBF38D9C2, - 0x38A0C50D, 0x21BBF44C, 0x0A96A78F, 0x138D96CE, 0x5CCC0009, 0x45D73148, 0x6EFA628B, 0x77E153CA, - 0xBABB5D54, 0xA3A06C15, 0x888D3FD6, 0x91960E97, 0xDED79850, 0xC7CCA911, 0xECE1FAD2, 0xF5FACB93, - 0x7262D75C, 0x6B79E61D, 0x4054B5DE, 0x594F849F, 0x160E1258, 0x0F152319, 0x243870DA, 0x3D23419B, - 0x65FD6BA7, 0x7CE65AE6, 0x57CB0925, 0x4ED03864, 0x0191AEA3, 0x188A9FE2, 0x33A7CC21, 0x2ABCFD60, - 0xAD24E1AF, 0xB43FD0EE, 0x9F12832D, 0x8609B26C, 0xC94824AB, 0xD05315EA, 0xFB7E4629, 0xE2657768, - 0x2F3F79F6, 0x362448B7, 0x1D091B74, 0x04122A35, 0x4B53BCF2, 0x52488DB3, 0x7965DE70, 0x607EEF31, - 0xE7E6F3FE, 0xFEFDC2BF, 0xD5D0917C, 0xCCCBA03D, 0x838A36FA, 0x9A9107BB, 0xB1BC5478, 0xA8A76539, - 0x3B83984B, 0x2298A90A, 0x09B5FAC9, 0x10AECB88, 0x5FEF5D4F, 0x46F46C0E, 0x6DD93FCD, 0x74C20E8C, - 0xF35A1243, 0xEA412302, 0xC16C70C1, 0xD8774180, 0x9736D747, 0x8E2DE606, 0xA500B5C5, 0xBC1B8484, - 0x71418A1A, 0x685ABB5B, 0x4377E898, 0x5A6CD9D9, 0x152D4F1E, 0x0C367E5F, 0x271B2D9C, 0x3E001CDD, - 0xB9980012, 0xA0833153, 0x8BAE6290, 0x92B553D1, 0xDDF4C516, 0xC4EFF457, 0xEFC2A794, 0xF6D996D5, - 0xAE07BCE9, 0xB71C8DA8, 0x9C31DE6B, 0x852AEF2A, 0xCA6B79ED, 0xD37048AC, 0xF85D1B6F, 0xE1462A2E, - 0x66DE36E1, 0x7FC507A0, 0x54E85463, 0x4DF36522, 0x02B2F3E5, 0x1BA9C2A4, 0x30849167, 0x299FA026, - 0xE4C5AEB8, 0xFDDE9FF9, 0xD6F3CC3A, 0xCFE8FD7B, 0x80A96BBC, 0x99B25AFD, 0xB29F093E, 0xAB84387F, - 0x2C1C24B0, 0x350715F1, 0x1E2A4632, 0x07317773, 0x4870E1B4, 0x516BD0F5, 0x7A468336, 0x635DB277, - 0xCBFAD74E, 0xD2E1E60F, 0xF9CCB5CC, 0xE0D7848D, 0xAF96124A, 0xB68D230B, 0x9DA070C8, 0x84BB4189, - 0x03235D46, 0x1A386C07, 0x31153FC4, 0x280E0E85, 0x674F9842, 0x7E54A903, 0x5579FAC0, 0x4C62CB81, - 0x8138C51F, 0x9823F45E, 0xB30EA79D, 0xAA1596DC, 0xE554001B, 0xFC4F315A, 0xD7626299, 0xCE7953D8, - 0x49E14F17, 0x50FA7E56, 0x7BD72D95, 0x62CC1CD4, 0x2D8D8A13, 0x3496BB52, 0x1FBBE891, 0x06A0D9D0, - 0x5E7EF3EC, 0x4765C2AD, 0x6C48916E, 0x7553A02F, 0x3A1236E8, 0x230907A9, 0x0824546A, 0x113F652B, - 0x96A779E4, 0x8FBC48A5, 0xA4911B66, 0xBD8A2A27, 0xF2CBBCE0, 0xEBD08DA1, 0xC0FDDE62, 0xD9E6EF23, - 0x14BCE1BD, 0x0DA7D0FC, 0x268A833F, 0x3F91B27E, 0x70D024B9, 0x69CB15F8, 0x42E6463B, 0x5BFD777A, - 0xDC656BB5, 0xC57E5AF4, 0xEE530937, 0xF7483876, 0xB809AEB1, 0xA1129FF0, 0x8A3FCC33, 0x9324FD72, - }, - - { - 0x00000000, 0x01C26A37, 0x0384D46E, 0x0246BE59, 0x0709A8DC, 0x06CBC2EB, 0x048D7CB2, 0x054F1685, - 0x0E1351B8, 0x0FD13B8F, 0x0D9785D6, 0x0C55EFE1, 0x091AF964, 0x08D89353, 0x0A9E2D0A, 0x0B5C473D, - 0x1C26A370, 0x1DE4C947, 0x1FA2771E, 0x1E601D29, 0x1B2F0BAC, 0x1AED619B, 0x18ABDFC2, 0x1969B5F5, - 0x1235F2C8, 0x13F798FF, 0x11B126A6, 0x10734C91, 0x153C5A14, 0x14FE3023, 0x16B88E7A, 0x177AE44D, - 0x384D46E0, 0x398F2CD7, 0x3BC9928E, 0x3A0BF8B9, 0x3F44EE3C, 0x3E86840B, 0x3CC03A52, 0x3D025065, - 0x365E1758, 0x379C7D6F, 0x35DAC336, 0x3418A901, 0x3157BF84, 0x3095D5B3, 0x32D36BEA, 0x331101DD, - 0x246BE590, 0x25A98FA7, 0x27EF31FE, 0x262D5BC9, 0x23624D4C, 0x22A0277B, 0x20E69922, 0x2124F315, - 0x2A78B428, 0x2BBADE1F, 0x29FC6046, 0x283E0A71, 0x2D711CF4, 0x2CB376C3, 0x2EF5C89A, 0x2F37A2AD, - 0x709A8DC0, 0x7158E7F7, 0x731E59AE, 0x72DC3399, 0x7793251C, 0x76514F2B, 0x7417F172, 0x75D59B45, - 0x7E89DC78, 0x7F4BB64F, 0x7D0D0816, 0x7CCF6221, 0x798074A4, 0x78421E93, 0x7A04A0CA, 0x7BC6CAFD, - 0x6CBC2EB0, 0x6D7E4487, 0x6F38FADE, 0x6EFA90E9, 0x6BB5866C, 0x6A77EC5B, 0x68315202, 0x69F33835, - 0x62AF7F08, 0x636D153F, 0x612BAB66, 0x60E9C151, 0x65A6D7D4, 0x6464BDE3, 0x662203BA, 0x67E0698D, - 0x48D7CB20, 0x4915A117, 0x4B531F4E, 0x4A917579, 0x4FDE63FC, 0x4E1C09CB, 0x4C5AB792, 0x4D98DDA5, - 0x46C49A98, 0x4706F0AF, 0x45404EF6, 0x448224C1, 0x41CD3244, 0x400F5873, 0x4249E62A, 0x438B8C1D, - 0x54F16850, 0x55330267, 0x5775BC3E, 0x56B7D609, 0x53F8C08C, 0x523AAABB, 0x507C14E2, 0x51BE7ED5, - 0x5AE239E8, 0x5B2053DF, 0x5966ED86, 0x58A487B1, 0x5DEB9134, 0x5C29FB03, 0x5E6F455A, 0x5FAD2F6D, - 0xE1351B80, 0xE0F771B7, 0xE2B1CFEE, 0xE373A5D9, 0xE63CB35C, 0xE7FED96B, 0xE5B86732, 0xE47A0D05, - 0xEF264A38, 0xEEE4200F, 0xECA29E56, 0xED60F461, 0xE82FE2E4, 0xE9ED88D3, 0xEBAB368A, 0xEA695CBD, - 0xFD13B8F0, 0xFCD1D2C7, 0xFE976C9E, 0xFF5506A9, 0xFA1A102C, 0xFBD87A1B, 0xF99EC442, 0xF85CAE75, - 0xF300E948, 0xF2C2837F, 0xF0843D26, 0xF1465711, 0xF4094194, 0xF5CB2BA3, 0xF78D95FA, 0xF64FFFCD, - 0xD9785D60, 0xD8BA3757, 0xDAFC890E, 0xDB3EE339, 0xDE71F5BC, 0xDFB39F8B, 0xDDF521D2, 0xDC374BE5, - 0xD76B0CD8, 0xD6A966EF, 0xD4EFD8B6, 0xD52DB281, 0xD062A404, 0xD1A0CE33, 0xD3E6706A, 0xD2241A5D, - 0xC55EFE10, 0xC49C9427, 0xC6DA2A7E, 0xC7184049, 0xC25756CC, 0xC3953CFB, 0xC1D382A2, 0xC011E895, - 0xCB4DAFA8, 0xCA8FC59F, 0xC8C97BC6, 0xC90B11F1, 0xCC440774, 0xCD866D43, 0xCFC0D31A, 0xCE02B92D, - 0x91AF9640, 0x906DFC77, 0x922B422E, 0x93E92819, 0x96A63E9C, 0x976454AB, 0x9522EAF2, 0x94E080C5, - 0x9FBCC7F8, 0x9E7EADCF, 0x9C381396, 0x9DFA79A1, 0x98B56F24, 0x99770513, 0x9B31BB4A, 0x9AF3D17D, - 0x8D893530, 0x8C4B5F07, 0x8E0DE15E, 0x8FCF8B69, 0x8A809DEC, 0x8B42F7DB, 0x89044982, 0x88C623B5, - 0x839A6488, 0x82580EBF, 0x801EB0E6, 0x81DCDAD1, 0x8493CC54, 0x8551A663, 0x8717183A, 0x86D5720D, - 0xA9E2D0A0, 0xA820BA97, 0xAA6604CE, 0xABA46EF9, 0xAEEB787C, 0xAF29124B, 0xAD6FAC12, 0xACADC625, - 0xA7F18118, 0xA633EB2F, 0xA4755576, 0xA5B73F41, 0xA0F829C4, 0xA13A43F3, 0xA37CFDAA, 0xA2BE979D, - 0xB5C473D0, 0xB40619E7, 0xB640A7BE, 0xB782CD89, 0xB2CDDB0C, 0xB30FB13B, 0xB1490F62, 0xB08B6555, - 0xBBD72268, 0xBA15485F, 0xB853F606, 0xB9919C31, 0xBCDE8AB4, 0xBD1CE083, 0xBF5A5EDA, 0xBE9834ED, - }, - - { - 0x00000000, 0xB8BC6765, 0xAA09C88B, 0x12B5AFEE, 0x8F629757, 0x37DEF032, 0x256B5FDC, 0x9DD738B9, - 0xC5B428EF, 0x7D084F8A, 0x6FBDE064, 0xD7018701, 0x4AD6BFB8, 0xF26AD8DD, 0xE0DF7733, 0x58631056, - 0x5019579F, 0xE8A530FA, 0xFA109F14, 0x42ACF871, 0xDF7BC0C8, 0x67C7A7AD, 0x75720843, 0xCDCE6F26, - 0x95AD7F70, 0x2D111815, 0x3FA4B7FB, 0x8718D09E, 0x1ACFE827, 0xA2738F42, 0xB0C620AC, 0x087A47C9, - 0xA032AF3E, 0x188EC85B, 0x0A3B67B5, 0xB28700D0, 0x2F503869, 0x97EC5F0C, 0x8559F0E2, 0x3DE59787, - 0x658687D1, 0xDD3AE0B4, 0xCF8F4F5A, 0x7733283F, 0xEAE41086, 0x525877E3, 0x40EDD80D, 0xF851BF68, - 0xF02BF8A1, 0x48979FC4, 0x5A22302A, 0xE29E574F, 0x7F496FF6, 0xC7F50893, 0xD540A77D, 0x6DFCC018, - 0x359FD04E, 0x8D23B72B, 0x9F9618C5, 0x272A7FA0, 0xBAFD4719, 0x0241207C, 0x10F48F92, 0xA848E8F7, - 0x9B14583D, 0x23A83F58, 0x311D90B6, 0x89A1F7D3, 0x1476CF6A, 0xACCAA80F, 0xBE7F07E1, 0x06C36084, - 0x5EA070D2, 0xE61C17B7, 0xF4A9B859, 0x4C15DF3C, 0xD1C2E785, 0x697E80E0, 0x7BCB2F0E, 0xC377486B, - 0xCB0D0FA2, 0x73B168C7, 0x6104C729, 0xD9B8A04C, 0x446F98F5, 0xFCD3FF90, 0xEE66507E, 0x56DA371B, - 0x0EB9274D, 0xB6054028, 0xA4B0EFC6, 0x1C0C88A3, 0x81DBB01A, 0x3967D77F, 0x2BD27891, 0x936E1FF4, - 0x3B26F703, 0x839A9066, 0x912F3F88, 0x299358ED, 0xB4446054, 0x0CF80731, 0x1E4DA8DF, 0xA6F1CFBA, - 0xFE92DFEC, 0x462EB889, 0x549B1767, 0xEC277002, 0x71F048BB, 0xC94C2FDE, 0xDBF98030, 0x6345E755, - 0x6B3FA09C, 0xD383C7F9, 0xC1366817, 0x798A0F72, 0xE45D37CB, 0x5CE150AE, 0x4E54FF40, 0xF6E89825, - 0xAE8B8873, 0x1637EF16, 0x048240F8, 0xBC3E279D, 0x21E91F24, 0x99557841, 0x8BE0D7AF, 0x335CB0CA, - 0xED59B63B, 0x55E5D15E, 0x47507EB0, 0xFFEC19D5, 0x623B216C, 0xDA874609, 0xC832E9E7, 0x708E8E82, - 0x28ED9ED4, 0x9051F9B1, 0x82E4565F, 0x3A58313A, 0xA78F0983, 0x1F336EE6, 0x0D86C108, 0xB53AA66D, - 0xBD40E1A4, 0x05FC86C1, 0x1749292F, 0xAFF54E4A, 0x322276F3, 0x8A9E1196, 0x982BBE78, 0x2097D91D, - 0x78F4C94B, 0xC048AE2E, 0xD2FD01C0, 0x6A4166A5, 0xF7965E1C, 0x4F2A3979, 0x5D9F9697, 0xE523F1F2, - 0x4D6B1905, 0xF5D77E60, 0xE762D18E, 0x5FDEB6EB, 0xC2098E52, 0x7AB5E937, 0x680046D9, 0xD0BC21BC, - 0x88DF31EA, 0x3063568F, 0x22D6F961, 0x9A6A9E04, 0x07BDA6BD, 0xBF01C1D8, 0xADB46E36, 0x15080953, - 0x1D724E9A, 0xA5CE29FF, 0xB77B8611, 0x0FC7E174, 0x9210D9CD, 0x2AACBEA8, 0x38191146, 0x80A57623, - 0xD8C66675, 0x607A0110, 0x72CFAEFE, 0xCA73C99B, 0x57A4F122, 0xEF189647, 0xFDAD39A9, 0x45115ECC, - 0x764DEE06, 0xCEF18963, 0xDC44268D, 0x64F841E8, 0xF92F7951, 0x41931E34, 0x5326B1DA, 0xEB9AD6BF, - 0xB3F9C6E9, 0x0B45A18C, 0x19F00E62, 0xA14C6907, 0x3C9B51BE, 0x842736DB, 0x96929935, 0x2E2EFE50, - 0x2654B999, 0x9EE8DEFC, 0x8C5D7112, 0x34E11677, 0xA9362ECE, 0x118A49AB, 0x033FE645, 0xBB838120, - 0xE3E09176, 0x5B5CF613, 0x49E959FD, 0xF1553E98, 0x6C820621, 0xD43E6144, 0xC68BCEAA, 0x7E37A9CF, - 0xD67F4138, 0x6EC3265D, 0x7C7689B3, 0xC4CAEED6, 0x591DD66F, 0xE1A1B10A, 0xF3141EE4, 0x4BA87981, - 0x13CB69D7, 0xAB770EB2, 0xB9C2A15C, 0x017EC639, 0x9CA9FE80, 0x241599E5, 0x36A0360B, 0x8E1C516E, - 0x866616A7, 0x3EDA71C2, 0x2C6FDE2C, 0x94D3B949, 0x090481F0, 0xB1B8E695, 0xA30D497B, 0x1BB12E1E, - 0x43D23E48, 0xFB6E592D, 0xE9DBF6C3, 0x516791A6, 0xCCB0A91F, 0x740CCE7A, 0x66B96194, 0xDE0506F1, - }, - - // beyond this point only relevant for Slicing-by-8 and Slicing-by-16 - { - 0x00000000, 0x3D6029B0, 0x7AC05360, 0x47A07AD0, 0xF580A6C0, 0xC8E08F70, 0x8F40F5A0, 0xB220DC10, - 0x30704BC1, 0x0D106271, 0x4AB018A1, 0x77D03111, 0xC5F0ED01, 0xF890C4B1, 0xBF30BE61, 0x825097D1, - 0x60E09782, 0x5D80BE32, 0x1A20C4E2, 0x2740ED52, 0x95603142, 0xA80018F2, 0xEFA06222, 0xD2C04B92, - 0x5090DC43, 0x6DF0F5F3, 0x2A508F23, 0x1730A693, 0xA5107A83, 0x98705333, 0xDFD029E3, 0xE2B00053, - 0xC1C12F04, 0xFCA106B4, 0xBB017C64, 0x866155D4, 0x344189C4, 0x0921A074, 0x4E81DAA4, 0x73E1F314, - 0xF1B164C5, 0xCCD14D75, 0x8B7137A5, 0xB6111E15, 0x0431C205, 0x3951EBB5, 0x7EF19165, 0x4391B8D5, - 0xA121B886, 0x9C419136, 0xDBE1EBE6, 0xE681C256, 0x54A11E46, 0x69C137F6, 0x2E614D26, 0x13016496, - 0x9151F347, 0xAC31DAF7, 0xEB91A027, 0xD6F18997, 0x64D15587, 0x59B17C37, 0x1E1106E7, 0x23712F57, - 0x58F35849, 0x659371F9, 0x22330B29, 0x1F532299, 0xAD73FE89, 0x9013D739, 0xD7B3ADE9, 0xEAD38459, - 0x68831388, 0x55E33A38, 0x124340E8, 0x2F236958, 0x9D03B548, 0xA0639CF8, 0xE7C3E628, 0xDAA3CF98, - 0x3813CFCB, 0x0573E67B, 0x42D39CAB, 0x7FB3B51B, 0xCD93690B, 0xF0F340BB, 0xB7533A6B, 0x8A3313DB, - 0x0863840A, 0x3503ADBA, 0x72A3D76A, 0x4FC3FEDA, 0xFDE322CA, 0xC0830B7A, 0x872371AA, 0xBA43581A, - 0x9932774D, 0xA4525EFD, 0xE3F2242D, 0xDE920D9D, 0x6CB2D18D, 0x51D2F83D, 0x167282ED, 0x2B12AB5D, - 0xA9423C8C, 0x9422153C, 0xD3826FEC, 0xEEE2465C, 0x5CC29A4C, 0x61A2B3FC, 0x2602C92C, 0x1B62E09C, - 0xF9D2E0CF, 0xC4B2C97F, 0x8312B3AF, 0xBE729A1F, 0x0C52460F, 0x31326FBF, 0x7692156F, 0x4BF23CDF, - 0xC9A2AB0E, 0xF4C282BE, 0xB362F86E, 0x8E02D1DE, 0x3C220DCE, 0x0142247E, 0x46E25EAE, 0x7B82771E, - 0xB1E6B092, 0x8C869922, 0xCB26E3F2, 0xF646CA42, 0x44661652, 0x79063FE2, 0x3EA64532, 0x03C66C82, - 0x8196FB53, 0xBCF6D2E3, 0xFB56A833, 0xC6368183, 0x74165D93, 0x49767423, 0x0ED60EF3, 0x33B62743, - 0xD1062710, 0xEC660EA0, 0xABC67470, 0x96A65DC0, 0x248681D0, 0x19E6A860, 0x5E46D2B0, 0x6326FB00, - 0xE1766CD1, 0xDC164561, 0x9BB63FB1, 0xA6D61601, 0x14F6CA11, 0x2996E3A1, 0x6E369971, 0x5356B0C1, - 0x70279F96, 0x4D47B626, 0x0AE7CCF6, 0x3787E546, 0x85A73956, 0xB8C710E6, 0xFF676A36, 0xC2074386, - 0x4057D457, 0x7D37FDE7, 0x3A978737, 0x07F7AE87, 0xB5D77297, 0x88B75B27, 0xCF1721F7, 0xF2770847, - 0x10C70814, 0x2DA721A4, 0x6A075B74, 0x576772C4, 0xE547AED4, 0xD8278764, 0x9F87FDB4, 0xA2E7D404, - 0x20B743D5, 0x1DD76A65, 0x5A7710B5, 0x67173905, 0xD537E515, 0xE857CCA5, 0xAFF7B675, 0x92979FC5, - 0xE915E8DB, 0xD475C16B, 0x93D5BBBB, 0xAEB5920B, 0x1C954E1B, 0x21F567AB, 0x66551D7B, 0x5B3534CB, - 0xD965A31A, 0xE4058AAA, 0xA3A5F07A, 0x9EC5D9CA, 0x2CE505DA, 0x11852C6A, 0x562556BA, 0x6B457F0A, - 0x89F57F59, 0xB49556E9, 0xF3352C39, 0xCE550589, 0x7C75D999, 0x4115F029, 0x06B58AF9, 0x3BD5A349, - 0xB9853498, 0x84E51D28, 0xC34567F8, 0xFE254E48, 0x4C059258, 0x7165BBE8, 0x36C5C138, 0x0BA5E888, - 0x28D4C7DF, 0x15B4EE6F, 0x521494BF, 0x6F74BD0F, 0xDD54611F, 0xE03448AF, 0xA794327F, 0x9AF41BCF, - 0x18A48C1E, 0x25C4A5AE, 0x6264DF7E, 0x5F04F6CE, 0xED242ADE, 0xD044036E, 0x97E479BE, 0xAA84500E, - 0x4834505D, 0x755479ED, 0x32F4033D, 0x0F942A8D, 0xBDB4F69D, 0x80D4DF2D, 0xC774A5FD, 0xFA148C4D, - 0x78441B9C, 0x4524322C, 0x028448FC, 0x3FE4614C, 0x8DC4BD5C, 0xB0A494EC, 0xF704EE3C, 0xCA64C78C, - }, - - { - 0x00000000, 0xCB5CD3A5, 0x4DC8A10B, 0x869472AE, 0x9B914216, 0x50CD91B3, 0xD659E31D, 0x1D0530B8, - 0xEC53826D, 0x270F51C8, 0xA19B2366, 0x6AC7F0C3, 0x77C2C07B, 0xBC9E13DE, 0x3A0A6170, 0xF156B2D5, - 0x03D6029B, 0xC88AD13E, 0x4E1EA390, 0x85427035, 0x9847408D, 0x531B9328, 0xD58FE186, 0x1ED33223, - 0xEF8580F6, 0x24D95353, 0xA24D21FD, 0x6911F258, 0x7414C2E0, 0xBF481145, 0x39DC63EB, 0xF280B04E, - 0x07AC0536, 0xCCF0D693, 0x4A64A43D, 0x81387798, 0x9C3D4720, 0x57619485, 0xD1F5E62B, 0x1AA9358E, - 0xEBFF875B, 0x20A354FE, 0xA6372650, 0x6D6BF5F5, 0x706EC54D, 0xBB3216E8, 0x3DA66446, 0xF6FAB7E3, - 0x047A07AD, 0xCF26D408, 0x49B2A6A6, 0x82EE7503, 0x9FEB45BB, 0x54B7961E, 0xD223E4B0, 0x197F3715, - 0xE82985C0, 0x23755665, 0xA5E124CB, 0x6EBDF76E, 0x73B8C7D6, 0xB8E41473, 0x3E7066DD, 0xF52CB578, - 0x0F580A6C, 0xC404D9C9, 0x4290AB67, 0x89CC78C2, 0x94C9487A, 0x5F959BDF, 0xD901E971, 0x125D3AD4, - 0xE30B8801, 0x28575BA4, 0xAEC3290A, 0x659FFAAF, 0x789ACA17, 0xB3C619B2, 0x35526B1C, 0xFE0EB8B9, - 0x0C8E08F7, 0xC7D2DB52, 0x4146A9FC, 0x8A1A7A59, 0x971F4AE1, 0x5C439944, 0xDAD7EBEA, 0x118B384F, - 0xE0DD8A9A, 0x2B81593F, 0xAD152B91, 0x6649F834, 0x7B4CC88C, 0xB0101B29, 0x36846987, 0xFDD8BA22, - 0x08F40F5A, 0xC3A8DCFF, 0x453CAE51, 0x8E607DF4, 0x93654D4C, 0x58399EE9, 0xDEADEC47, 0x15F13FE2, - 0xE4A78D37, 0x2FFB5E92, 0xA96F2C3C, 0x6233FF99, 0x7F36CF21, 0xB46A1C84, 0x32FE6E2A, 0xF9A2BD8F, - 0x0B220DC1, 0xC07EDE64, 0x46EAACCA, 0x8DB67F6F, 0x90B34FD7, 0x5BEF9C72, 0xDD7BEEDC, 0x16273D79, - 0xE7718FAC, 0x2C2D5C09, 0xAAB92EA7, 0x61E5FD02, 0x7CE0CDBA, 0xB7BC1E1F, 0x31286CB1, 0xFA74BF14, - 0x1EB014D8, 0xD5ECC77D, 0x5378B5D3, 0x98246676, 0x852156CE, 0x4E7D856B, 0xC8E9F7C5, 0x03B52460, - 0xF2E396B5, 0x39BF4510, 0xBF2B37BE, 0x7477E41B, 0x6972D4A3, 0xA22E0706, 0x24BA75A8, 0xEFE6A60D, - 0x1D661643, 0xD63AC5E6, 0x50AEB748, 0x9BF264ED, 0x86F75455, 0x4DAB87F0, 0xCB3FF55E, 0x006326FB, - 0xF135942E, 0x3A69478B, 0xBCFD3525, 0x77A1E680, 0x6AA4D638, 0xA1F8059D, 0x276C7733, 0xEC30A496, - 0x191C11EE, 0xD240C24B, 0x54D4B0E5, 0x9F886340, 0x828D53F8, 0x49D1805D, 0xCF45F2F3, 0x04192156, - 0xF54F9383, 0x3E134026, 0xB8873288, 0x73DBE12D, 0x6EDED195, 0xA5820230, 0x2316709E, 0xE84AA33B, - 0x1ACA1375, 0xD196C0D0, 0x5702B27E, 0x9C5E61DB, 0x815B5163, 0x4A0782C6, 0xCC93F068, 0x07CF23CD, - 0xF6999118, 0x3DC542BD, 0xBB513013, 0x700DE3B6, 0x6D08D30E, 0xA65400AB, 0x20C07205, 0xEB9CA1A0, - 0x11E81EB4, 0xDAB4CD11, 0x5C20BFBF, 0x977C6C1A, 0x8A795CA2, 0x41258F07, 0xC7B1FDA9, 0x0CED2E0C, - 0xFDBB9CD9, 0x36E74F7C, 0xB0733DD2, 0x7B2FEE77, 0x662ADECF, 0xAD760D6A, 0x2BE27FC4, 0xE0BEAC61, - 0x123E1C2F, 0xD962CF8A, 0x5FF6BD24, 0x94AA6E81, 0x89AF5E39, 0x42F38D9C, 0xC467FF32, 0x0F3B2C97, - 0xFE6D9E42, 0x35314DE7, 0xB3A53F49, 0x78F9ECEC, 0x65FCDC54, 0xAEA00FF1, 0x28347D5F, 0xE368AEFA, - 0x16441B82, 0xDD18C827, 0x5B8CBA89, 0x90D0692C, 0x8DD55994, 0x46898A31, 0xC01DF89F, 0x0B412B3A, - 0xFA1799EF, 0x314B4A4A, 0xB7DF38E4, 0x7C83EB41, 0x6186DBF9, 0xAADA085C, 0x2C4E7AF2, 0xE712A957, - 0x15921919, 0xDECECABC, 0x585AB812, 0x93066BB7, 0x8E035B0F, 0x455F88AA, 0xC3CBFA04, 0x089729A1, - 0xF9C19B74, 0x329D48D1, 0xB4093A7F, 0x7F55E9DA, 0x6250D962, 0xA90C0AC7, 0x2F987869, 0xE4C4ABCC, - }, - - { - 0x00000000, 0xA6770BB4, 0x979F1129, 0x31E81A9D, 0xF44F2413, 0x52382FA7, 0x63D0353A, 0xC5A73E8E, - 0x33EF4E67, 0x959845D3, 0xA4705F4E, 0x020754FA, 0xC7A06A74, 0x61D761C0, 0x503F7B5D, 0xF64870E9, - 0x67DE9CCE, 0xC1A9977A, 0xF0418DE7, 0x56368653, 0x9391B8DD, 0x35E6B369, 0x040EA9F4, 0xA279A240, - 0x5431D2A9, 0xF246D91D, 0xC3AEC380, 0x65D9C834, 0xA07EF6BA, 0x0609FD0E, 0x37E1E793, 0x9196EC27, - 0xCFBD399C, 0x69CA3228, 0x582228B5, 0xFE552301, 0x3BF21D8F, 0x9D85163B, 0xAC6D0CA6, 0x0A1A0712, - 0xFC5277FB, 0x5A257C4F, 0x6BCD66D2, 0xCDBA6D66, 0x081D53E8, 0xAE6A585C, 0x9F8242C1, 0x39F54975, - 0xA863A552, 0x0E14AEE6, 0x3FFCB47B, 0x998BBFCF, 0x5C2C8141, 0xFA5B8AF5, 0xCBB39068, 0x6DC49BDC, - 0x9B8CEB35, 0x3DFBE081, 0x0C13FA1C, 0xAA64F1A8, 0x6FC3CF26, 0xC9B4C492, 0xF85CDE0F, 0x5E2BD5BB, - 0x440B7579, 0xE27C7ECD, 0xD3946450, 0x75E36FE4, 0xB044516A, 0x16335ADE, 0x27DB4043, 0x81AC4BF7, - 0x77E43B1E, 0xD19330AA, 0xE07B2A37, 0x460C2183, 0x83AB1F0D, 0x25DC14B9, 0x14340E24, 0xB2430590, - 0x23D5E9B7, 0x85A2E203, 0xB44AF89E, 0x123DF32A, 0xD79ACDA4, 0x71EDC610, 0x4005DC8D, 0xE672D739, - 0x103AA7D0, 0xB64DAC64, 0x87A5B6F9, 0x21D2BD4D, 0xE47583C3, 0x42028877, 0x73EA92EA, 0xD59D995E, - 0x8BB64CE5, 0x2DC14751, 0x1C295DCC, 0xBA5E5678, 0x7FF968F6, 0xD98E6342, 0xE86679DF, 0x4E11726B, - 0xB8590282, 0x1E2E0936, 0x2FC613AB, 0x89B1181F, 0x4C162691, 0xEA612D25, 0xDB8937B8, 0x7DFE3C0C, - 0xEC68D02B, 0x4A1FDB9F, 0x7BF7C102, 0xDD80CAB6, 0x1827F438, 0xBE50FF8C, 0x8FB8E511, 0x29CFEEA5, - 0xDF879E4C, 0x79F095F8, 0x48188F65, 0xEE6F84D1, 0x2BC8BA5F, 0x8DBFB1EB, 0xBC57AB76, 0x1A20A0C2, - 0x8816EAF2, 0x2E61E146, 0x1F89FBDB, 0xB9FEF06F, 0x7C59CEE1, 0xDA2EC555, 0xEBC6DFC8, 0x4DB1D47C, - 0xBBF9A495, 0x1D8EAF21, 0x2C66B5BC, 0x8A11BE08, 0x4FB68086, 0xE9C18B32, 0xD82991AF, 0x7E5E9A1B, - 0xEFC8763C, 0x49BF7D88, 0x78576715, 0xDE206CA1, 0x1B87522F, 0xBDF0599B, 0x8C184306, 0x2A6F48B2, - 0xDC27385B, 0x7A5033EF, 0x4BB82972, 0xEDCF22C6, 0x28681C48, 0x8E1F17FC, 0xBFF70D61, 0x198006D5, - 0x47ABD36E, 0xE1DCD8DA, 0xD034C247, 0x7643C9F3, 0xB3E4F77D, 0x1593FCC9, 0x247BE654, 0x820CEDE0, - 0x74449D09, 0xD23396BD, 0xE3DB8C20, 0x45AC8794, 0x800BB91A, 0x267CB2AE, 0x1794A833, 0xB1E3A387, - 0x20754FA0, 0x86024414, 0xB7EA5E89, 0x119D553D, 0xD43A6BB3, 0x724D6007, 0x43A57A9A, 0xE5D2712E, - 0x139A01C7, 0xB5ED0A73, 0x840510EE, 0x22721B5A, 0xE7D525D4, 0x41A22E60, 0x704A34FD, 0xD63D3F49, - 0xCC1D9F8B, 0x6A6A943F, 0x5B828EA2, 0xFDF58516, 0x3852BB98, 0x9E25B02C, 0xAFCDAAB1, 0x09BAA105, - 0xFFF2D1EC, 0x5985DA58, 0x686DC0C5, 0xCE1ACB71, 0x0BBDF5FF, 0xADCAFE4B, 0x9C22E4D6, 0x3A55EF62, - 0xABC30345, 0x0DB408F1, 0x3C5C126C, 0x9A2B19D8, 0x5F8C2756, 0xF9FB2CE2, 0xC813367F, 0x6E643DCB, - 0x982C4D22, 0x3E5B4696, 0x0FB35C0B, 0xA9C457BF, 0x6C636931, 0xCA146285, 0xFBFC7818, 0x5D8B73AC, - 0x03A0A617, 0xA5D7ADA3, 0x943FB73E, 0x3248BC8A, 0xF7EF8204, 0x519889B0, 0x6070932D, 0xC6079899, - 0x304FE870, 0x9638E3C4, 0xA7D0F959, 0x01A7F2ED, 0xC400CC63, 0x6277C7D7, 0x539FDD4A, 0xF5E8D6FE, - 0x647E3AD9, 0xC209316D, 0xF3E12BF0, 0x55962044, 0x90311ECA, 0x3646157E, 0x07AE0FE3, 0xA1D90457, - 0x579174BE, 0xF1E67F0A, 0xC00E6597, 0x66796E23, 0xA3DE50AD, 0x05A95B19, 0x34414184, 0x92364A30, - }, - - { - 0x00000000, 0xCCAA009E, 0x4225077D, 0x8E8F07E3, 0x844A0EFA, 0x48E00E64, 0xC66F0987, 0x0AC50919, - 0xD3E51BB5, 0x1F4F1B2B, 0x91C01CC8, 0x5D6A1C56, 0x57AF154F, 0x9B0515D1, 0x158A1232, 0xD92012AC, - 0x7CBB312B, 0xB01131B5, 0x3E9E3656, 0xF23436C8, 0xF8F13FD1, 0x345B3F4F, 0xBAD438AC, 0x767E3832, - 0xAF5E2A9E, 0x63F42A00, 0xED7B2DE3, 0x21D12D7D, 0x2B142464, 0xE7BE24FA, 0x69312319, 0xA59B2387, - 0xF9766256, 0x35DC62C8, 0xBB53652B, 0x77F965B5, 0x7D3C6CAC, 0xB1966C32, 0x3F196BD1, 0xF3B36B4F, - 0x2A9379E3, 0xE639797D, 0x68B67E9E, 0xA41C7E00, 0xAED97719, 0x62737787, 0xECFC7064, 0x205670FA, - 0x85CD537D, 0x496753E3, 0xC7E85400, 0x0B42549E, 0x01875D87, 0xCD2D5D19, 0x43A25AFA, 0x8F085A64, - 0x562848C8, 0x9A824856, 0x140D4FB5, 0xD8A74F2B, 0xD2624632, 0x1EC846AC, 0x9047414F, 0x5CED41D1, - 0x299DC2ED, 0xE537C273, 0x6BB8C590, 0xA712C50E, 0xADD7CC17, 0x617DCC89, 0xEFF2CB6A, 0x2358CBF4, - 0xFA78D958, 0x36D2D9C6, 0xB85DDE25, 0x74F7DEBB, 0x7E32D7A2, 0xB298D73C, 0x3C17D0DF, 0xF0BDD041, - 0x5526F3C6, 0x998CF358, 0x1703F4BB, 0xDBA9F425, 0xD16CFD3C, 0x1DC6FDA2, 0x9349FA41, 0x5FE3FADF, - 0x86C3E873, 0x4A69E8ED, 0xC4E6EF0E, 0x084CEF90, 0x0289E689, 0xCE23E617, 0x40ACE1F4, 0x8C06E16A, - 0xD0EBA0BB, 0x1C41A025, 0x92CEA7C6, 0x5E64A758, 0x54A1AE41, 0x980BAEDF, 0x1684A93C, 0xDA2EA9A2, - 0x030EBB0E, 0xCFA4BB90, 0x412BBC73, 0x8D81BCED, 0x8744B5F4, 0x4BEEB56A, 0xC561B289, 0x09CBB217, - 0xAC509190, 0x60FA910E, 0xEE7596ED, 0x22DF9673, 0x281A9F6A, 0xE4B09FF4, 0x6A3F9817, 0xA6959889, - 0x7FB58A25, 0xB31F8ABB, 0x3D908D58, 0xF13A8DC6, 0xFBFF84DF, 0x37558441, 0xB9DA83A2, 0x7570833C, - 0x533B85DA, 0x9F918544, 0x111E82A7, 0xDDB48239, 0xD7718B20, 0x1BDB8BBE, 0x95548C5D, 0x59FE8CC3, - 0x80DE9E6F, 0x4C749EF1, 0xC2FB9912, 0x0E51998C, 0x04949095, 0xC83E900B, 0x46B197E8, 0x8A1B9776, - 0x2F80B4F1, 0xE32AB46F, 0x6DA5B38C, 0xA10FB312, 0xABCABA0B, 0x6760BA95, 0xE9EFBD76, 0x2545BDE8, - 0xFC65AF44, 0x30CFAFDA, 0xBE40A839, 0x72EAA8A7, 0x782FA1BE, 0xB485A120, 0x3A0AA6C3, 0xF6A0A65D, - 0xAA4DE78C, 0x66E7E712, 0xE868E0F1, 0x24C2E06F, 0x2E07E976, 0xE2ADE9E8, 0x6C22EE0B, 0xA088EE95, - 0x79A8FC39, 0xB502FCA7, 0x3B8DFB44, 0xF727FBDA, 0xFDE2F2C3, 0x3148F25D, 0xBFC7F5BE, 0x736DF520, - 0xD6F6D6A7, 0x1A5CD639, 0x94D3D1DA, 0x5879D144, 0x52BCD85D, 0x9E16D8C3, 0x1099DF20, 0xDC33DFBE, - 0x0513CD12, 0xC9B9CD8C, 0x4736CA6F, 0x8B9CCAF1, 0x8159C3E8, 0x4DF3C376, 0xC37CC495, 0x0FD6C40B, - 0x7AA64737, 0xB60C47A9, 0x3883404A, 0xF42940D4, 0xFEEC49CD, 0x32464953, 0xBCC94EB0, 0x70634E2E, - 0xA9435C82, 0x65E95C1C, 0xEB665BFF, 0x27CC5B61, 0x2D095278, 0xE1A352E6, 0x6F2C5505, 0xA386559B, - 0x061D761C, 0xCAB77682, 0x44387161, 0x889271FF, 0x825778E6, 0x4EFD7878, 0xC0727F9B, 0x0CD87F05, - 0xD5F86DA9, 0x19526D37, 0x97DD6AD4, 0x5B776A4A, 0x51B26353, 0x9D1863CD, 0x1397642E, 0xDF3D64B0, - 0x83D02561, 0x4F7A25FF, 0xC1F5221C, 0x0D5F2282, 0x079A2B9B, 0xCB302B05, 0x45BF2CE6, 0x89152C78, - 0x50353ED4, 0x9C9F3E4A, 0x121039A9, 0xDEBA3937, 0xD47F302E, 0x18D530B0, 0x965A3753, 0x5AF037CD, - 0xFF6B144A, 0x33C114D4, 0xBD4E1337, 0x71E413A9, 0x7B211AB0, 0xB78B1A2E, 0x39041DCD, 0xF5AE1D53, - 0x2C8E0FFF, 0xE0240F61, 0x6EAB0882, 0xA201081C, 0xA8C40105, 0x646E019B, 0xEAE10678, 0x264B06E6, - }, - - // beyond this point only relevant for Slicing-by-16 - { - 0x00000000, 0x177B1443, 0x2EF62886, 0x398D3CC5, 0x5DEC510C, 0x4A97454F, 0x731A798A, 0x64616DC9, - 0xBBD8A218, 0xACA3B65B, 0x952E8A9E, 0x82559EDD, 0xE634F314, 0xF14FE757, 0xC8C2DB92, 0xDFB9CFD1, - 0xACC04271, 0xBBBB5632, 0x82366AF7, 0x954D7EB4, 0xF12C137D, 0xE657073E, 0xDFDA3BFB, 0xC8A12FB8, - 0x1718E069, 0x0063F42A, 0x39EEC8EF, 0x2E95DCAC, 0x4AF4B165, 0x5D8FA526, 0x640299E3, 0x73798DA0, - 0x82F182A3, 0x958A96E0, 0xAC07AA25, 0xBB7CBE66, 0xDF1DD3AF, 0xC866C7EC, 0xF1EBFB29, 0xE690EF6A, - 0x392920BB, 0x2E5234F8, 0x17DF083D, 0x00A41C7E, 0x64C571B7, 0x73BE65F4, 0x4A335931, 0x5D484D72, - 0x2E31C0D2, 0x394AD491, 0x00C7E854, 0x17BCFC17, 0x73DD91DE, 0x64A6859D, 0x5D2BB958, 0x4A50AD1B, - 0x95E962CA, 0x82927689, 0xBB1F4A4C, 0xAC645E0F, 0xC80533C6, 0xDF7E2785, 0xE6F31B40, 0xF1880F03, - 0xDE920307, 0xC9E91744, 0xF0642B81, 0xE71F3FC2, 0x837E520B, 0x94054648, 0xAD887A8D, 0xBAF36ECE, - 0x654AA11F, 0x7231B55C, 0x4BBC8999, 0x5CC79DDA, 0x38A6F013, 0x2FDDE450, 0x1650D895, 0x012BCCD6, - 0x72524176, 0x65295535, 0x5CA469F0, 0x4BDF7DB3, 0x2FBE107A, 0x38C50439, 0x014838FC, 0x16332CBF, - 0xC98AE36E, 0xDEF1F72D, 0xE77CCBE8, 0xF007DFAB, 0x9466B262, 0x831DA621, 0xBA909AE4, 0xADEB8EA7, - 0x5C6381A4, 0x4B1895E7, 0x7295A922, 0x65EEBD61, 0x018FD0A8, 0x16F4C4EB, 0x2F79F82E, 0x3802EC6D, - 0xE7BB23BC, 0xF0C037FF, 0xC94D0B3A, 0xDE361F79, 0xBA5772B0, 0xAD2C66F3, 0x94A15A36, 0x83DA4E75, - 0xF0A3C3D5, 0xE7D8D796, 0xDE55EB53, 0xC92EFF10, 0xAD4F92D9, 0xBA34869A, 0x83B9BA5F, 0x94C2AE1C, - 0x4B7B61CD, 0x5C00758E, 0x658D494B, 0x72F65D08, 0x169730C1, 0x01EC2482, 0x38611847, 0x2F1A0C04, - 0x6655004F, 0x712E140C, 0x48A328C9, 0x5FD83C8A, 0x3BB95143, 0x2CC24500, 0x154F79C5, 0x02346D86, - 0xDD8DA257, 0xCAF6B614, 0xF37B8AD1, 0xE4009E92, 0x8061F35B, 0x971AE718, 0xAE97DBDD, 0xB9ECCF9E, - 0xCA95423E, 0xDDEE567D, 0xE4636AB8, 0xF3187EFB, 0x97791332, 0x80020771, 0xB98F3BB4, 0xAEF42FF7, - 0x714DE026, 0x6636F465, 0x5FBBC8A0, 0x48C0DCE3, 0x2CA1B12A, 0x3BDAA569, 0x025799AC, 0x152C8DEF, - 0xE4A482EC, 0xF3DF96AF, 0xCA52AA6A, 0xDD29BE29, 0xB948D3E0, 0xAE33C7A3, 0x97BEFB66, 0x80C5EF25, - 0x5F7C20F4, 0x480734B7, 0x718A0872, 0x66F11C31, 0x029071F8, 0x15EB65BB, 0x2C66597E, 0x3B1D4D3D, - 0x4864C09D, 0x5F1FD4DE, 0x6692E81B, 0x71E9FC58, 0x15889191, 0x02F385D2, 0x3B7EB917, 0x2C05AD54, - 0xF3BC6285, 0xE4C776C6, 0xDD4A4A03, 0xCA315E40, 0xAE503389, 0xB92B27CA, 0x80A61B0F, 0x97DD0F4C, - 0xB8C70348, 0xAFBC170B, 0x96312BCE, 0x814A3F8D, 0xE52B5244, 0xF2504607, 0xCBDD7AC2, 0xDCA66E81, - 0x031FA150, 0x1464B513, 0x2DE989D6, 0x3A929D95, 0x5EF3F05C, 0x4988E41F, 0x7005D8DA, 0x677ECC99, - 0x14074139, 0x037C557A, 0x3AF169BF, 0x2D8A7DFC, 0x49EB1035, 0x5E900476, 0x671D38B3, 0x70662CF0, - 0xAFDFE321, 0xB8A4F762, 0x8129CBA7, 0x9652DFE4, 0xF233B22D, 0xE548A66E, 0xDCC59AAB, 0xCBBE8EE8, - 0x3A3681EB, 0x2D4D95A8, 0x14C0A96D, 0x03BBBD2E, 0x67DAD0E7, 0x70A1C4A4, 0x492CF861, 0x5E57EC22, - 0x81EE23F3, 0x969537B0, 0xAF180B75, 0xB8631F36, 0xDC0272FF, 0xCB7966BC, 0xF2F45A79, 0xE58F4E3A, - 0x96F6C39A, 0x818DD7D9, 0xB800EB1C, 0xAF7BFF5F, 0xCB1A9296, 0xDC6186D5, 0xE5ECBA10, 0xF297AE53, - 0x2D2E6182, 0x3A5575C1, 0x03D84904, 0x14A35D47, 0x70C2308E, 0x67B924CD, 0x5E341808, 0x494F0C4B, - }, - - { - 0x00000000, 0xEFC26B3E, 0x04F5D03D, 0xEB37BB03, 0x09EBA07A, 0xE629CB44, 0x0D1E7047, 0xE2DC1B79, - 0x13D740F4, 0xFC152BCA, 0x172290C9, 0xF8E0FBF7, 0x1A3CE08E, 0xF5FE8BB0, 0x1EC930B3, 0xF10B5B8D, - 0x27AE81E8, 0xC86CEAD6, 0x235B51D5, 0xCC993AEB, 0x2E452192, 0xC1874AAC, 0x2AB0F1AF, 0xC5729A91, - 0x3479C11C, 0xDBBBAA22, 0x308C1121, 0xDF4E7A1F, 0x3D926166, 0xD2500A58, 0x3967B15B, 0xD6A5DA65, - 0x4F5D03D0, 0xA09F68EE, 0x4BA8D3ED, 0xA46AB8D3, 0x46B6A3AA, 0xA974C894, 0x42437397, 0xAD8118A9, - 0x5C8A4324, 0xB348281A, 0x587F9319, 0xB7BDF827, 0x5561E35E, 0xBAA38860, 0x51943363, 0xBE56585D, - 0x68F38238, 0x8731E906, 0x6C065205, 0x83C4393B, 0x61182242, 0x8EDA497C, 0x65EDF27F, 0x8A2F9941, - 0x7B24C2CC, 0x94E6A9F2, 0x7FD112F1, 0x901379CF, 0x72CF62B6, 0x9D0D0988, 0x763AB28B, 0x99F8D9B5, - 0x9EBA07A0, 0x71786C9E, 0x9A4FD79D, 0x758DBCA3, 0x9751A7DA, 0x7893CCE4, 0x93A477E7, 0x7C661CD9, - 0x8D6D4754, 0x62AF2C6A, 0x89989769, 0x665AFC57, 0x8486E72E, 0x6B448C10, 0x80733713, 0x6FB15C2D, - 0xB9148648, 0x56D6ED76, 0xBDE15675, 0x52233D4B, 0xB0FF2632, 0x5F3D4D0C, 0xB40AF60F, 0x5BC89D31, - 0xAAC3C6BC, 0x4501AD82, 0xAE361681, 0x41F47DBF, 0xA32866C6, 0x4CEA0DF8, 0xA7DDB6FB, 0x481FDDC5, - 0xD1E70470, 0x3E256F4E, 0xD512D44D, 0x3AD0BF73, 0xD80CA40A, 0x37CECF34, 0xDCF97437, 0x333B1F09, - 0xC2304484, 0x2DF22FBA, 0xC6C594B9, 0x2907FF87, 0xCBDBE4FE, 0x24198FC0, 0xCF2E34C3, 0x20EC5FFD, - 0xF6498598, 0x198BEEA6, 0xF2BC55A5, 0x1D7E3E9B, 0xFFA225E2, 0x10604EDC, 0xFB57F5DF, 0x14959EE1, - 0xE59EC56C, 0x0A5CAE52, 0xE16B1551, 0x0EA97E6F, 0xEC756516, 0x03B70E28, 0xE880B52B, 0x0742DE15, - 0xE6050901, 0x09C7623F, 0xE2F0D93C, 0x0D32B202, 0xEFEEA97B, 0x002CC245, 0xEB1B7946, 0x04D91278, - 0xF5D249F5, 0x1A1022CB, 0xF12799C8, 0x1EE5F2F6, 0xFC39E98F, 0x13FB82B1, 0xF8CC39B2, 0x170E528C, - 0xC1AB88E9, 0x2E69E3D7, 0xC55E58D4, 0x2A9C33EA, 0xC8402893, 0x278243AD, 0xCCB5F8AE, 0x23779390, - 0xD27CC81D, 0x3DBEA323, 0xD6891820, 0x394B731E, 0xDB976867, 0x34550359, 0xDF62B85A, 0x30A0D364, - 0xA9580AD1, 0x469A61EF, 0xADADDAEC, 0x426FB1D2, 0xA0B3AAAB, 0x4F71C195, 0xA4467A96, 0x4B8411A8, - 0xBA8F4A25, 0x554D211B, 0xBE7A9A18, 0x51B8F126, 0xB364EA5F, 0x5CA68161, 0xB7913A62, 0x5853515C, - 0x8EF68B39, 0x6134E007, 0x8A035B04, 0x65C1303A, 0x871D2B43, 0x68DF407D, 0x83E8FB7E, 0x6C2A9040, - 0x9D21CBCD, 0x72E3A0F3, 0x99D41BF0, 0x761670CE, 0x94CA6BB7, 0x7B080089, 0x903FBB8A, 0x7FFDD0B4, - 0x78BF0EA1, 0x977D659F, 0x7C4ADE9C, 0x9388B5A2, 0x7154AEDB, 0x9E96C5E5, 0x75A17EE6, 0x9A6315D8, - 0x6B684E55, 0x84AA256B, 0x6F9D9E68, 0x805FF556, 0x6283EE2F, 0x8D418511, 0x66763E12, 0x89B4552C, - 0x5F118F49, 0xB0D3E477, 0x5BE45F74, 0xB426344A, 0x56FA2F33, 0xB938440D, 0x520FFF0E, 0xBDCD9430, - 0x4CC6CFBD, 0xA304A483, 0x48331F80, 0xA7F174BE, 0x452D6FC7, 0xAAEF04F9, 0x41D8BFFA, 0xAE1AD4C4, - 0x37E20D71, 0xD820664F, 0x3317DD4C, 0xDCD5B672, 0x3E09AD0B, 0xD1CBC635, 0x3AFC7D36, 0xD53E1608, - 0x24354D85, 0xCBF726BB, 0x20C09DB8, 0xCF02F686, 0x2DDEEDFF, 0xC21C86C1, 0x292B3DC2, 0xC6E956FC, - 0x104C8C99, 0xFF8EE7A7, 0x14B95CA4, 0xFB7B379A, 0x19A72CE3, 0xF66547DD, 0x1D52FCDE, 0xF29097E0, - 0x039BCC6D, 0xEC59A753, 0x076E1C50, 0xE8AC776E, 0x0A706C17, 0xE5B20729, 0x0E85BC2A, 0xE147D714, - }, - - { - 0x00000000, 0xC18EDFC0, 0x586CB9C1, 0x99E26601, 0xB0D97382, 0x7157AC42, 0xE8B5CA43, 0x293B1583, - 0xBAC3E145, 0x7B4D3E85, 0xE2AF5884, 0x23218744, 0x0A1A92C7, 0xCB944D07, 0x52762B06, 0x93F8F4C6, - 0xAEF6C4CB, 0x6F781B0B, 0xF69A7D0A, 0x3714A2CA, 0x1E2FB749, 0xDFA16889, 0x46430E88, 0x87CDD148, - 0x1435258E, 0xD5BBFA4E, 0x4C599C4F, 0x8DD7438F, 0xA4EC560C, 0x656289CC, 0xFC80EFCD, 0x3D0E300D, - 0x869C8FD7, 0x47125017, 0xDEF03616, 0x1F7EE9D6, 0x3645FC55, 0xF7CB2395, 0x6E294594, 0xAFA79A54, - 0x3C5F6E92, 0xFDD1B152, 0x6433D753, 0xA5BD0893, 0x8C861D10, 0x4D08C2D0, 0xD4EAA4D1, 0x15647B11, - 0x286A4B1C, 0xE9E494DC, 0x7006F2DD, 0xB1882D1D, 0x98B3389E, 0x593DE75E, 0xC0DF815F, 0x01515E9F, - 0x92A9AA59, 0x53277599, 0xCAC51398, 0x0B4BCC58, 0x2270D9DB, 0xE3FE061B, 0x7A1C601A, 0xBB92BFDA, - 0xD64819EF, 0x17C6C62F, 0x8E24A02E, 0x4FAA7FEE, 0x66916A6D, 0xA71FB5AD, 0x3EFDD3AC, 0xFF730C6C, - 0x6C8BF8AA, 0xAD05276A, 0x34E7416B, 0xF5699EAB, 0xDC528B28, 0x1DDC54E8, 0x843E32E9, 0x45B0ED29, - 0x78BEDD24, 0xB93002E4, 0x20D264E5, 0xE15CBB25, 0xC867AEA6, 0x09E97166, 0x900B1767, 0x5185C8A7, - 0xC27D3C61, 0x03F3E3A1, 0x9A1185A0, 0x5B9F5A60, 0x72A44FE3, 0xB32A9023, 0x2AC8F622, 0xEB4629E2, - 0x50D49638, 0x915A49F8, 0x08B82FF9, 0xC936F039, 0xE00DE5BA, 0x21833A7A, 0xB8615C7B, 0x79EF83BB, - 0xEA17777D, 0x2B99A8BD, 0xB27BCEBC, 0x73F5117C, 0x5ACE04FF, 0x9B40DB3F, 0x02A2BD3E, 0xC32C62FE, - 0xFE2252F3, 0x3FAC8D33, 0xA64EEB32, 0x67C034F2, 0x4EFB2171, 0x8F75FEB1, 0x169798B0, 0xD7194770, - 0x44E1B3B6, 0x856F6C76, 0x1C8D0A77, 0xDD03D5B7, 0xF438C034, 0x35B61FF4, 0xAC5479F5, 0x6DDAA635, - 0x77E1359F, 0xB66FEA5F, 0x2F8D8C5E, 0xEE03539E, 0xC738461D, 0x06B699DD, 0x9F54FFDC, 0x5EDA201C, - 0xCD22D4DA, 0x0CAC0B1A, 0x954E6D1B, 0x54C0B2DB, 0x7DFBA758, 0xBC757898, 0x25971E99, 0xE419C159, - 0xD917F154, 0x18992E94, 0x817B4895, 0x40F59755, 0x69CE82D6, 0xA8405D16, 0x31A23B17, 0xF02CE4D7, - 0x63D41011, 0xA25ACFD1, 0x3BB8A9D0, 0xFA367610, 0xD30D6393, 0x1283BC53, 0x8B61DA52, 0x4AEF0592, - 0xF17DBA48, 0x30F36588, 0xA9110389, 0x689FDC49, 0x41A4C9CA, 0x802A160A, 0x19C8700B, 0xD846AFCB, - 0x4BBE5B0D, 0x8A3084CD, 0x13D2E2CC, 0xD25C3D0C, 0xFB67288F, 0x3AE9F74F, 0xA30B914E, 0x62854E8E, - 0x5F8B7E83, 0x9E05A143, 0x07E7C742, 0xC6691882, 0xEF520D01, 0x2EDCD2C1, 0xB73EB4C0, 0x76B06B00, - 0xE5489FC6, 0x24C64006, 0xBD242607, 0x7CAAF9C7, 0x5591EC44, 0x941F3384, 0x0DFD5585, 0xCC738A45, - 0xA1A92C70, 0x6027F3B0, 0xF9C595B1, 0x384B4A71, 0x11705FF2, 0xD0FE8032, 0x491CE633, 0x889239F3, - 0x1B6ACD35, 0xDAE412F5, 0x430674F4, 0x8288AB34, 0xABB3BEB7, 0x6A3D6177, 0xF3DF0776, 0x3251D8B6, - 0x0F5FE8BB, 0xCED1377B, 0x5733517A, 0x96BD8EBA, 0xBF869B39, 0x7E0844F9, 0xE7EA22F8, 0x2664FD38, - 0xB59C09FE, 0x7412D63E, 0xEDF0B03F, 0x2C7E6FFF, 0x05457A7C, 0xC4CBA5BC, 0x5D29C3BD, 0x9CA71C7D, - 0x2735A3A7, 0xE6BB7C67, 0x7F591A66, 0xBED7C5A6, 0x97ECD025, 0x56620FE5, 0xCF8069E4, 0x0E0EB624, - 0x9DF642E2, 0x5C789D22, 0xC59AFB23, 0x041424E3, 0x2D2F3160, 0xECA1EEA0, 0x754388A1, 0xB4CD5761, - 0x89C3676C, 0x484DB8AC, 0xD1AFDEAD, 0x1021016D, 0x391A14EE, 0xF894CB2E, 0x6176AD2F, 0xA0F872EF, - 0x33008629, 0xF28E59E9, 0x6B6C3FE8, 0xAAE2E028, 0x83D9F5AB, 0x42572A6B, 0xDBB54C6A, 0x1A3B93AA, - }, - - { - 0x00000000, 0x9BA54C6F, 0xEC3B9E9F, 0x779ED2F0, 0x03063B7F, 0x98A37710, 0xEF3DA5E0, 0x7498E98F, - 0x060C76FE, 0x9DA93A91, 0xEA37E861, 0x7192A40E, 0x050A4D81, 0x9EAF01EE, 0xE931D31E, 0x72949F71, - 0x0C18EDFC, 0x97BDA193, 0xE0237363, 0x7B863F0C, 0x0F1ED683, 0x94BB9AEC, 0xE325481C, 0x78800473, - 0x0A149B02, 0x91B1D76D, 0xE62F059D, 0x7D8A49F2, 0x0912A07D, 0x92B7EC12, 0xE5293EE2, 0x7E8C728D, - 0x1831DBF8, 0x83949797, 0xF40A4567, 0x6FAF0908, 0x1B37E087, 0x8092ACE8, 0xF70C7E18, 0x6CA93277, - 0x1E3DAD06, 0x8598E169, 0xF2063399, 0x69A37FF6, 0x1D3B9679, 0x869EDA16, 0xF10008E6, 0x6AA54489, - 0x14293604, 0x8F8C7A6B, 0xF812A89B, 0x63B7E4F4, 0x172F0D7B, 0x8C8A4114, 0xFB1493E4, 0x60B1DF8B, - 0x122540FA, 0x89800C95, 0xFE1EDE65, 0x65BB920A, 0x11237B85, 0x8A8637EA, 0xFD18E51A, 0x66BDA975, - 0x3063B7F0, 0xABC6FB9F, 0xDC58296F, 0x47FD6500, 0x33658C8F, 0xA8C0C0E0, 0xDF5E1210, 0x44FB5E7F, - 0x366FC10E, 0xADCA8D61, 0xDA545F91, 0x41F113FE, 0x3569FA71, 0xAECCB61E, 0xD95264EE, 0x42F72881, - 0x3C7B5A0C, 0xA7DE1663, 0xD040C493, 0x4BE588FC, 0x3F7D6173, 0xA4D82D1C, 0xD346FFEC, 0x48E3B383, - 0x3A772CF2, 0xA1D2609D, 0xD64CB26D, 0x4DE9FE02, 0x3971178D, 0xA2D45BE2, 0xD54A8912, 0x4EEFC57D, - 0x28526C08, 0xB3F72067, 0xC469F297, 0x5FCCBEF8, 0x2B545777, 0xB0F11B18, 0xC76FC9E8, 0x5CCA8587, - 0x2E5E1AF6, 0xB5FB5699, 0xC2658469, 0x59C0C806, 0x2D582189, 0xB6FD6DE6, 0xC163BF16, 0x5AC6F379, - 0x244A81F4, 0xBFEFCD9B, 0xC8711F6B, 0x53D45304, 0x274CBA8B, 0xBCE9F6E4, 0xCB772414, 0x50D2687B, - 0x2246F70A, 0xB9E3BB65, 0xCE7D6995, 0x55D825FA, 0x2140CC75, 0xBAE5801A, 0xCD7B52EA, 0x56DE1E85, - 0x60C76FE0, 0xFB62238F, 0x8CFCF17F, 0x1759BD10, 0x63C1549F, 0xF86418F0, 0x8FFACA00, 0x145F866F, - 0x66CB191E, 0xFD6E5571, 0x8AF08781, 0x1155CBEE, 0x65CD2261, 0xFE686E0E, 0x89F6BCFE, 0x1253F091, - 0x6CDF821C, 0xF77ACE73, 0x80E41C83, 0x1B4150EC, 0x6FD9B963, 0xF47CF50C, 0x83E227FC, 0x18476B93, - 0x6AD3F4E2, 0xF176B88D, 0x86E86A7D, 0x1D4D2612, 0x69D5CF9D, 0xF27083F2, 0x85EE5102, 0x1E4B1D6D, - 0x78F6B418, 0xE353F877, 0x94CD2A87, 0x0F6866E8, 0x7BF08F67, 0xE055C308, 0x97CB11F8, 0x0C6E5D97, - 0x7EFAC2E6, 0xE55F8E89, 0x92C15C79, 0x09641016, 0x7DFCF999, 0xE659B5F6, 0x91C76706, 0x0A622B69, - 0x74EE59E4, 0xEF4B158B, 0x98D5C77B, 0x03708B14, 0x77E8629B, 0xEC4D2EF4, 0x9BD3FC04, 0x0076B06B, - 0x72E22F1A, 0xE9476375, 0x9ED9B185, 0x057CFDEA, 0x71E41465, 0xEA41580A, 0x9DDF8AFA, 0x067AC695, - 0x50A4D810, 0xCB01947F, 0xBC9F468F, 0x273A0AE0, 0x53A2E36F, 0xC807AF00, 0xBF997DF0, 0x243C319F, - 0x56A8AEEE, 0xCD0DE281, 0xBA933071, 0x21367C1E, 0x55AE9591, 0xCE0BD9FE, 0xB9950B0E, 0x22304761, - 0x5CBC35EC, 0xC7197983, 0xB087AB73, 0x2B22E71C, 0x5FBA0E93, 0xC41F42FC, 0xB381900C, 0x2824DC63, - 0x5AB04312, 0xC1150F7D, 0xB68BDD8D, 0x2D2E91E2, 0x59B6786D, 0xC2133402, 0xB58DE6F2, 0x2E28AA9D, - 0x489503E8, 0xD3304F87, 0xA4AE9D77, 0x3F0BD118, 0x4B933897, 0xD03674F8, 0xA7A8A608, 0x3C0DEA67, - 0x4E997516, 0xD53C3979, 0xA2A2EB89, 0x3907A7E6, 0x4D9F4E69, 0xD63A0206, 0xA1A4D0F6, 0x3A019C99, - 0x448DEE14, 0xDF28A27B, 0xA8B6708B, 0x33133CE4, 0x478BD56B, 0xDC2E9904, 0xABB04BF4, 0x3015079B, - 0x428198EA, 0xD924D485, 0xAEBA0675, 0x351F4A1A, 0x4187A395, 0xDA22EFFA, 0xADBC3D0A, 0x36197165, - }, - - { - 0x00000000, 0xDD96D985, 0x605CB54B, 0xBDCA6CCE, 0xC0B96A96, 0x1D2FB313, 0xA0E5DFDD, 0x7D730658, - 0x5A03D36D, 0x87950AE8, 0x3A5F6626, 0xE7C9BFA3, 0x9ABAB9FB, 0x472C607E, 0xFAE60CB0, 0x2770D535, - 0xB407A6DA, 0x69917F5F, 0xD45B1391, 0x09CDCA14, 0x74BECC4C, 0xA92815C9, 0x14E27907, 0xC974A082, - 0xEE0475B7, 0x3392AC32, 0x8E58C0FC, 0x53CE1979, 0x2EBD1F21, 0xF32BC6A4, 0x4EE1AA6A, 0x937773EF, - 0xB37E4BF5, 0x6EE89270, 0xD322FEBE, 0x0EB4273B, 0x73C72163, 0xAE51F8E6, 0x139B9428, 0xCE0D4DAD, - 0xE97D9898, 0x34EB411D, 0x89212DD3, 0x54B7F456, 0x29C4F20E, 0xF4522B8B, 0x49984745, 0x940E9EC0, - 0x0779ED2F, 0xDAEF34AA, 0x67255864, 0xBAB381E1, 0xC7C087B9, 0x1A565E3C, 0xA79C32F2, 0x7A0AEB77, - 0x5D7A3E42, 0x80ECE7C7, 0x3D268B09, 0xE0B0528C, 0x9DC354D4, 0x40558D51, 0xFD9FE19F, 0x2009381A, - 0xBD8D91AB, 0x601B482E, 0xDDD124E0, 0x0047FD65, 0x7D34FB3D, 0xA0A222B8, 0x1D684E76, 0xC0FE97F3, - 0xE78E42C6, 0x3A189B43, 0x87D2F78D, 0x5A442E08, 0x27372850, 0xFAA1F1D5, 0x476B9D1B, 0x9AFD449E, - 0x098A3771, 0xD41CEEF4, 0x69D6823A, 0xB4405BBF, 0xC9335DE7, 0x14A58462, 0xA96FE8AC, 0x74F93129, - 0x5389E41C, 0x8E1F3D99, 0x33D55157, 0xEE4388D2, 0x93308E8A, 0x4EA6570F, 0xF36C3BC1, 0x2EFAE244, - 0x0EF3DA5E, 0xD36503DB, 0x6EAF6F15, 0xB339B690, 0xCE4AB0C8, 0x13DC694D, 0xAE160583, 0x7380DC06, - 0x54F00933, 0x8966D0B6, 0x34ACBC78, 0xE93A65FD, 0x944963A5, 0x49DFBA20, 0xF415D6EE, 0x29830F6B, - 0xBAF47C84, 0x6762A501, 0xDAA8C9CF, 0x073E104A, 0x7A4D1612, 0xA7DBCF97, 0x1A11A359, 0xC7877ADC, - 0xE0F7AFE9, 0x3D61766C, 0x80AB1AA2, 0x5D3DC327, 0x204EC57F, 0xFDD81CFA, 0x40127034, 0x9D84A9B1, - 0xA06A2517, 0x7DFCFC92, 0xC036905C, 0x1DA049D9, 0x60D34F81, 0xBD459604, 0x008FFACA, 0xDD19234F, - 0xFA69F67A, 0x27FF2FFF, 0x9A354331, 0x47A39AB4, 0x3AD09CEC, 0xE7464569, 0x5A8C29A7, 0x871AF022, - 0x146D83CD, 0xC9FB5A48, 0x74313686, 0xA9A7EF03, 0xD4D4E95B, 0x094230DE, 0xB4885C10, 0x691E8595, - 0x4E6E50A0, 0x93F88925, 0x2E32E5EB, 0xF3A43C6E, 0x8ED73A36, 0x5341E3B3, 0xEE8B8F7D, 0x331D56F8, - 0x13146EE2, 0xCE82B767, 0x7348DBA9, 0xAEDE022C, 0xD3AD0474, 0x0E3BDDF1, 0xB3F1B13F, 0x6E6768BA, - 0x4917BD8F, 0x9481640A, 0x294B08C4, 0xF4DDD141, 0x89AED719, 0x54380E9C, 0xE9F26252, 0x3464BBD7, - 0xA713C838, 0x7A8511BD, 0xC74F7D73, 0x1AD9A4F6, 0x67AAA2AE, 0xBA3C7B2B, 0x07F617E5, 0xDA60CE60, - 0xFD101B55, 0x2086C2D0, 0x9D4CAE1E, 0x40DA779B, 0x3DA971C3, 0xE03FA846, 0x5DF5C488, 0x80631D0D, - 0x1DE7B4BC, 0xC0716D39, 0x7DBB01F7, 0xA02DD872, 0xDD5EDE2A, 0x00C807AF, 0xBD026B61, 0x6094B2E4, - 0x47E467D1, 0x9A72BE54, 0x27B8D29A, 0xFA2E0B1F, 0x875D0D47, 0x5ACBD4C2, 0xE701B80C, 0x3A976189, - 0xA9E01266, 0x7476CBE3, 0xC9BCA72D, 0x142A7EA8, 0x695978F0, 0xB4CFA175, 0x0905CDBB, 0xD493143E, - 0xF3E3C10B, 0x2E75188E, 0x93BF7440, 0x4E29ADC5, 0x335AAB9D, 0xEECC7218, 0x53061ED6, 0x8E90C753, - 0xAE99FF49, 0x730F26CC, 0xCEC54A02, 0x13539387, 0x6E2095DF, 0xB3B64C5A, 0x0E7C2094, 0xD3EAF911, - 0xF49A2C24, 0x290CF5A1, 0x94C6996F, 0x495040EA, 0x342346B2, 0xE9B59F37, 0x547FF3F9, 0x89E92A7C, - 0x1A9E5993, 0xC7088016, 0x7AC2ECD8, 0xA754355D, 0xDA273305, 0x07B1EA80, 0xBA7B864E, 0x67ED5FCB, - 0x409D8AFE, 0x9D0B537B, 0x20C13FB5, 0xFD57E630, 0x8024E068, 0x5DB239ED, 0xE0785523, 0x3DEE8CA6, - }, - - { - 0x00000000, 0x9D0FE176, 0xE16EC4AD, 0x7C6125DB, 0x19AC8F1B, 0x84A36E6D, 0xF8C24BB6, 0x65CDAAC0, - 0x33591E36, 0xAE56FF40, 0xD237DA9B, 0x4F383BED, 0x2AF5912D, 0xB7FA705B, 0xCB9B5580, 0x5694B4F6, - 0x66B23C6C, 0xFBBDDD1A, 0x87DCF8C1, 0x1AD319B7, 0x7F1EB377, 0xE2115201, 0x9E7077DA, 0x037F96AC, - 0x55EB225A, 0xC8E4C32C, 0xB485E6F7, 0x298A0781, 0x4C47AD41, 0xD1484C37, 0xAD2969EC, 0x3026889A, - 0xCD6478D8, 0x506B99AE, 0x2C0ABC75, 0xB1055D03, 0xD4C8F7C3, 0x49C716B5, 0x35A6336E, 0xA8A9D218, - 0xFE3D66EE, 0x63328798, 0x1F53A243, 0x825C4335, 0xE791E9F5, 0x7A9E0883, 0x06FF2D58, 0x9BF0CC2E, - 0xABD644B4, 0x36D9A5C2, 0x4AB88019, 0xD7B7616F, 0xB27ACBAF, 0x2F752AD9, 0x53140F02, 0xCE1BEE74, - 0x988F5A82, 0x0580BBF4, 0x79E19E2F, 0xE4EE7F59, 0x8123D599, 0x1C2C34EF, 0x604D1134, 0xFD42F042, - 0x41B9F7F1, 0xDCB61687, 0xA0D7335C, 0x3DD8D22A, 0x581578EA, 0xC51A999C, 0xB97BBC47, 0x24745D31, - 0x72E0E9C7, 0xEFEF08B1, 0x938E2D6A, 0x0E81CC1C, 0x6B4C66DC, 0xF64387AA, 0x8A22A271, 0x172D4307, - 0x270BCB9D, 0xBA042AEB, 0xC6650F30, 0x5B6AEE46, 0x3EA74486, 0xA3A8A5F0, 0xDFC9802B, 0x42C6615D, - 0x1452D5AB, 0x895D34DD, 0xF53C1106, 0x6833F070, 0x0DFE5AB0, 0x90F1BBC6, 0xEC909E1D, 0x719F7F6B, - 0x8CDD8F29, 0x11D26E5F, 0x6DB34B84, 0xF0BCAAF2, 0x95710032, 0x087EE144, 0x741FC49F, 0xE91025E9, - 0xBF84911F, 0x228B7069, 0x5EEA55B2, 0xC3E5B4C4, 0xA6281E04, 0x3B27FF72, 0x4746DAA9, 0xDA493BDF, - 0xEA6FB345, 0x77605233, 0x0B0177E8, 0x960E969E, 0xF3C33C5E, 0x6ECCDD28, 0x12ADF8F3, 0x8FA21985, - 0xD936AD73, 0x44394C05, 0x385869DE, 0xA55788A8, 0xC09A2268, 0x5D95C31E, 0x21F4E6C5, 0xBCFB07B3, - 0x8373EFE2, 0x1E7C0E94, 0x621D2B4F, 0xFF12CA39, 0x9ADF60F9, 0x07D0818F, 0x7BB1A454, 0xE6BE4522, - 0xB02AF1D4, 0x2D2510A2, 0x51443579, 0xCC4BD40F, 0xA9867ECF, 0x34899FB9, 0x48E8BA62, 0xD5E75B14, - 0xE5C1D38E, 0x78CE32F8, 0x04AF1723, 0x99A0F655, 0xFC6D5C95, 0x6162BDE3, 0x1D039838, 0x800C794E, - 0xD698CDB8, 0x4B972CCE, 0x37F60915, 0xAAF9E863, 0xCF3442A3, 0x523BA3D5, 0x2E5A860E, 0xB3556778, - 0x4E17973A, 0xD318764C, 0xAF795397, 0x3276B2E1, 0x57BB1821, 0xCAB4F957, 0xB6D5DC8C, 0x2BDA3DFA, - 0x7D4E890C, 0xE041687A, 0x9C204DA1, 0x012FACD7, 0x64E20617, 0xF9EDE761, 0x858CC2BA, 0x188323CC, - 0x28A5AB56, 0xB5AA4A20, 0xC9CB6FFB, 0x54C48E8D, 0x3109244D, 0xAC06C53B, 0xD067E0E0, 0x4D680196, - 0x1BFCB560, 0x86F35416, 0xFA9271CD, 0x679D90BB, 0x02503A7B, 0x9F5FDB0D, 0xE33EFED6, 0x7E311FA0, - 0xC2CA1813, 0x5FC5F965, 0x23A4DCBE, 0xBEAB3DC8, 0xDB669708, 0x4669767E, 0x3A0853A5, 0xA707B2D3, - 0xF1930625, 0x6C9CE753, 0x10FDC288, 0x8DF223FE, 0xE83F893E, 0x75306848, 0x09514D93, 0x945EACE5, - 0xA478247F, 0x3977C509, 0x4516E0D2, 0xD81901A4, 0xBDD4AB64, 0x20DB4A12, 0x5CBA6FC9, 0xC1B58EBF, - 0x97213A49, 0x0A2EDB3F, 0x764FFEE4, 0xEB401F92, 0x8E8DB552, 0x13825424, 0x6FE371FF, 0xF2EC9089, - 0x0FAE60CB, 0x92A181BD, 0xEEC0A466, 0x73CF4510, 0x1602EFD0, 0x8B0D0EA6, 0xF76C2B7D, 0x6A63CA0B, - 0x3CF77EFD, 0xA1F89F8B, 0xDD99BA50, 0x40965B26, 0x255BF1E6, 0xB8541090, 0xC435354B, 0x593AD43D, - 0x691C5CA7, 0xF413BDD1, 0x8872980A, 0x157D797C, 0x70B0D3BC, 0xEDBF32CA, 0x91DE1711, 0x0CD1F667, - 0x5A454291, 0xC74AA3E7, 0xBB2B863C, 0x2624674A, 0x43E9CD8A, 0xDEE62CFC, 0xA2870927, 0x3F88E851, - }, - - { - 0x00000000, 0xB9FBDBE8, 0xA886B191, 0x117D6A79, 0x8A7C6563, 0x3387BE8B, 0x22FAD4F2, 0x9B010F1A, - 0xCF89CC87, 0x7672176F, 0x670F7D16, 0xDEF4A6FE, 0x45F5A9E4, 0xFC0E720C, 0xED731875, 0x5488C39D, - 0x44629F4F, 0xFD9944A7, 0xECE42EDE, 0x551FF536, 0xCE1EFA2C, 0x77E521C4, 0x66984BBD, 0xDF639055, - 0x8BEB53C8, 0x32108820, 0x236DE259, 0x9A9639B1, 0x019736AB, 0xB86CED43, 0xA911873A, 0x10EA5CD2, - 0x88C53E9E, 0x313EE576, 0x20438F0F, 0x99B854E7, 0x02B95BFD, 0xBB428015, 0xAA3FEA6C, 0x13C43184, - 0x474CF219, 0xFEB729F1, 0xEFCA4388, 0x56319860, 0xCD30977A, 0x74CB4C92, 0x65B626EB, 0xDC4DFD03, - 0xCCA7A1D1, 0x755C7A39, 0x64211040, 0xDDDACBA8, 0x46DBC4B2, 0xFF201F5A, 0xEE5D7523, 0x57A6AECB, - 0x032E6D56, 0xBAD5B6BE, 0xABA8DCC7, 0x1253072F, 0x89520835, 0x30A9D3DD, 0x21D4B9A4, 0x982F624C, - 0xCAFB7B7D, 0x7300A095, 0x627DCAEC, 0xDB861104, 0x40871E1E, 0xF97CC5F6, 0xE801AF8F, 0x51FA7467, - 0x0572B7FA, 0xBC896C12, 0xADF4066B, 0x140FDD83, 0x8F0ED299, 0x36F50971, 0x27886308, 0x9E73B8E0, - 0x8E99E432, 0x37623FDA, 0x261F55A3, 0x9FE48E4B, 0x04E58151, 0xBD1E5AB9, 0xAC6330C0, 0x1598EB28, - 0x411028B5, 0xF8EBF35D, 0xE9969924, 0x506D42CC, 0xCB6C4DD6, 0x7297963E, 0x63EAFC47, 0xDA1127AF, - 0x423E45E3, 0xFBC59E0B, 0xEAB8F472, 0x53432F9A, 0xC8422080, 0x71B9FB68, 0x60C49111, 0xD93F4AF9, - 0x8DB78964, 0x344C528C, 0x253138F5, 0x9CCAE31D, 0x07CBEC07, 0xBE3037EF, 0xAF4D5D96, 0x16B6867E, - 0x065CDAAC, 0xBFA70144, 0xAEDA6B3D, 0x1721B0D5, 0x8C20BFCF, 0x35DB6427, 0x24A60E5E, 0x9D5DD5B6, - 0xC9D5162B, 0x702ECDC3, 0x6153A7BA, 0xD8A87C52, 0x43A97348, 0xFA52A8A0, 0xEB2FC2D9, 0x52D41931, - 0x4E87F0BB, 0xF77C2B53, 0xE601412A, 0x5FFA9AC2, 0xC4FB95D8, 0x7D004E30, 0x6C7D2449, 0xD586FFA1, - 0x810E3C3C, 0x38F5E7D4, 0x29888DAD, 0x90735645, 0x0B72595F, 0xB28982B7, 0xA3F4E8CE, 0x1A0F3326, - 0x0AE56FF4, 0xB31EB41C, 0xA263DE65, 0x1B98058D, 0x80990A97, 0x3962D17F, 0x281FBB06, 0x91E460EE, - 0xC56CA373, 0x7C97789B, 0x6DEA12E2, 0xD411C90A, 0x4F10C610, 0xF6EB1DF8, 0xE7967781, 0x5E6DAC69, - 0xC642CE25, 0x7FB915CD, 0x6EC47FB4, 0xD73FA45C, 0x4C3EAB46, 0xF5C570AE, 0xE4B81AD7, 0x5D43C13F, - 0x09CB02A2, 0xB030D94A, 0xA14DB333, 0x18B668DB, 0x83B767C1, 0x3A4CBC29, 0x2B31D650, 0x92CA0DB8, - 0x8220516A, 0x3BDB8A82, 0x2AA6E0FB, 0x935D3B13, 0x085C3409, 0xB1A7EFE1, 0xA0DA8598, 0x19215E70, - 0x4DA99DED, 0xF4524605, 0xE52F2C7C, 0x5CD4F794, 0xC7D5F88E, 0x7E2E2366, 0x6F53491F, 0xD6A892F7, - 0x847C8BC6, 0x3D87502E, 0x2CFA3A57, 0x9501E1BF, 0x0E00EEA5, 0xB7FB354D, 0xA6865F34, 0x1F7D84DC, - 0x4BF54741, 0xF20E9CA9, 0xE373F6D0, 0x5A882D38, 0xC1892222, 0x7872F9CA, 0x690F93B3, 0xD0F4485B, - 0xC01E1489, 0x79E5CF61, 0x6898A518, 0xD1637EF0, 0x4A6271EA, 0xF399AA02, 0xE2E4C07B, 0x5B1F1B93, - 0x0F97D80E, 0xB66C03E6, 0xA711699F, 0x1EEAB277, 0x85EBBD6D, 0x3C106685, 0x2D6D0CFC, 0x9496D714, - 0x0CB9B558, 0xB5426EB0, 0xA43F04C9, 0x1DC4DF21, 0x86C5D03B, 0x3F3E0BD3, 0x2E4361AA, 0x97B8BA42, - 0xC33079DF, 0x7ACBA237, 0x6BB6C84E, 0xD24D13A6, 0x494C1CBC, 0xF0B7C754, 0xE1CAAD2D, 0x583176C5, - 0x48DB2A17, 0xF120F1FF, 0xE05D9B86, 0x59A6406E, 0xC2A74F74, 0x7B5C949C, 0x6A21FEE5, 0xD3DA250D, - 0x8752E690, 0x3EA93D78, 0x2FD45701, 0x962F8CE9, 0x0D2E83F3, 0xB4D5581B, 0xA5A83262, 0x1C53E98A, - }, - - { - 0x00000000, 0xAE689191, 0x87A02563, 0x29C8B4F2, 0xD4314C87, 0x7A59DD16, 0x539169E4, 0xFDF9F875, - 0x73139F4F, 0xDD7B0EDE, 0xF4B3BA2C, 0x5ADB2BBD, 0xA722D3C8, 0x094A4259, 0x2082F6AB, 0x8EEA673A, - 0xE6273E9E, 0x484FAF0F, 0x61871BFD, 0xCFEF8A6C, 0x32167219, 0x9C7EE388, 0xB5B6577A, 0x1BDEC6EB, - 0x9534A1D1, 0x3B5C3040, 0x129484B2, 0xBCFC1523, 0x4105ED56, 0xEF6D7CC7, 0xC6A5C835, 0x68CD59A4, - 0x173F7B7D, 0xB957EAEC, 0x909F5E1E, 0x3EF7CF8F, 0xC30E37FA, 0x6D66A66B, 0x44AE1299, 0xEAC68308, - 0x642CE432, 0xCA4475A3, 0xE38CC151, 0x4DE450C0, 0xB01DA8B5, 0x1E753924, 0x37BD8DD6, 0x99D51C47, - 0xF11845E3, 0x5F70D472, 0x76B86080, 0xD8D0F111, 0x25290964, 0x8B4198F5, 0xA2892C07, 0x0CE1BD96, - 0x820BDAAC, 0x2C634B3D, 0x05ABFFCF, 0xABC36E5E, 0x563A962B, 0xF85207BA, 0xD19AB348, 0x7FF222D9, - 0x2E7EF6FA, 0x8016676B, 0xA9DED399, 0x07B64208, 0xFA4FBA7D, 0x54272BEC, 0x7DEF9F1E, 0xD3870E8F, - 0x5D6D69B5, 0xF305F824, 0xDACD4CD6, 0x74A5DD47, 0x895C2532, 0x2734B4A3, 0x0EFC0051, 0xA09491C0, - 0xC859C864, 0x663159F5, 0x4FF9ED07, 0xE1917C96, 0x1C6884E3, 0xB2001572, 0x9BC8A180, 0x35A03011, - 0xBB4A572B, 0x1522C6BA, 0x3CEA7248, 0x9282E3D9, 0x6F7B1BAC, 0xC1138A3D, 0xE8DB3ECF, 0x46B3AF5E, - 0x39418D87, 0x97291C16, 0xBEE1A8E4, 0x10893975, 0xED70C100, 0x43185091, 0x6AD0E463, 0xC4B875F2, - 0x4A5212C8, 0xE43A8359, 0xCDF237AB, 0x639AA63A, 0x9E635E4F, 0x300BCFDE, 0x19C37B2C, 0xB7ABEABD, - 0xDF66B319, 0x710E2288, 0x58C6967A, 0xF6AE07EB, 0x0B57FF9E, 0xA53F6E0F, 0x8CF7DAFD, 0x229F4B6C, - 0xAC752C56, 0x021DBDC7, 0x2BD50935, 0x85BD98A4, 0x784460D1, 0xD62CF140, 0xFFE445B2, 0x518CD423, - 0x5CFDEDF4, 0xF2957C65, 0xDB5DC897, 0x75355906, 0x88CCA173, 0x26A430E2, 0x0F6C8410, 0xA1041581, - 0x2FEE72BB, 0x8186E32A, 0xA84E57D8, 0x0626C649, 0xFBDF3E3C, 0x55B7AFAD, 0x7C7F1B5F, 0xD2178ACE, - 0xBADAD36A, 0x14B242FB, 0x3D7AF609, 0x93126798, 0x6EEB9FED, 0xC0830E7C, 0xE94BBA8E, 0x47232B1F, - 0xC9C94C25, 0x67A1DDB4, 0x4E696946, 0xE001F8D7, 0x1DF800A2, 0xB3909133, 0x9A5825C1, 0x3430B450, - 0x4BC29689, 0xE5AA0718, 0xCC62B3EA, 0x620A227B, 0x9FF3DA0E, 0x319B4B9F, 0x1853FF6D, 0xB63B6EFC, - 0x38D109C6, 0x96B99857, 0xBF712CA5, 0x1119BD34, 0xECE04541, 0x4288D4D0, 0x6B406022, 0xC528F1B3, - 0xADE5A817, 0x038D3986, 0x2A458D74, 0x842D1CE5, 0x79D4E490, 0xD7BC7501, 0xFE74C1F3, 0x501C5062, - 0xDEF63758, 0x709EA6C9, 0x5956123B, 0xF73E83AA, 0x0AC77BDF, 0xA4AFEA4E, 0x8D675EBC, 0x230FCF2D, - 0x72831B0E, 0xDCEB8A9F, 0xF5233E6D, 0x5B4BAFFC, 0xA6B25789, 0x08DAC618, 0x211272EA, 0x8F7AE37B, - 0x01908441, 0xAFF815D0, 0x8630A122, 0x285830B3, 0xD5A1C8C6, 0x7BC95957, 0x5201EDA5, 0xFC697C34, - 0x94A42590, 0x3ACCB401, 0x130400F3, 0xBD6C9162, 0x40956917, 0xEEFDF886, 0xC7354C74, 0x695DDDE5, - 0xE7B7BADF, 0x49DF2B4E, 0x60179FBC, 0xCE7F0E2D, 0x3386F658, 0x9DEE67C9, 0xB426D33B, 0x1A4E42AA, - 0x65BC6073, 0xCBD4F1E2, 0xE21C4510, 0x4C74D481, 0xB18D2CF4, 0x1FE5BD65, 0x362D0997, 0x98459806, - 0x16AFFF3C, 0xB8C76EAD, 0x910FDA5F, 0x3F674BCE, 0xC29EB3BB, 0x6CF6222A, 0x453E96D8, 0xEB560749, - 0x839B5EED, 0x2DF3CF7C, 0x043B7B8E, 0xAA53EA1F, 0x57AA126A, 0xF9C283FB, 0xD00A3709, 0x7E62A698, - 0xF088C1A2, 0x5EE05033, 0x7728E4C1, 0xD9407550, 0x24B98D25, 0x8AD11CB4, 0xA319A846, 0x0D7139D7, -} -}; - -/// compute CRC32 (bitwise algorithm) -uint32_t crc32_bitwise(const void* data, size_t length, uint32_t previousCrc32 /*= 0*/) -{ - uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF - const uint8_t* current = (const uint8_t*)data; - int j; - - while(length-- != 0) - { - crc ^= *current++; - for(j = 0; j < 8; j++) - { - // branch-free - crc = (crc >> 1) ^ (-(crc & 1) & Polynomial); - - // branching, much slower: - //if (crc & 1) - // crc = (crc >> 1) ^ Polynomial; - //else - // crc = crc >> 1; - } - } - - return ~crc; // same as crc ^ 0xFFFFFFFF -} - - -/// compute CRC32 (half-byte algoritm) -uint32_t crc32_halfbyte(const void* data, size_t length, uint32_t previousCrc32 /*= 0*/) -{ - uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF - const uint8_t* current = (const uint8_t*)data; - - /// look-up table for half-byte, same as crc32Lookup[0][16*i] - static const uint32_t Crc32Lookup16[16] = - { - 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C, - 0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C, 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C - }; - - while(length-- != 0) - { - crc = Crc32Lookup16[(crc ^ *current) & 0x0F] ^ (crc >> 4); - crc = Crc32Lookup16[(crc ^ (*current >> 4)) & 0x0F] ^ (crc >> 4); - current++; - } - - return ~crc; // same as crc ^ 0xFFFFFFFF -} - - -/// compute CRC32 (standard algorithm) -uint32_t crc32_1byte(const void* data, size_t length, uint32_t previousCrc32 /*= 0*/) -{ - uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF - const uint8_t* current = (const uint8_t*)data; - - while(length-- > 0) - crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *current++]; - - return ~crc; // same as crc ^ 0xFFFFFFFF -} - - -/// compute CRC32 (Slicing-by-4 algorithm) -uint32_t crc32_4bytes(const void* data, size_t length, uint32_t previousCrc32 /*= 0*/) -{ - uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF - const uint32_t* current = (const uint32_t*)data; - - // process four bytes at once (Slicing-by-4) - while(length >= 4) - { -#if __BYTE_ORDER == __BIG_ENDIAN - uint32_t one = *current++ ^ swap(crc); - crc = Crc32Lookup[0][one & 0xFF] ^ - Crc32Lookup[1][(one >> 8) & 0xFF] ^ - Crc32Lookup[2][(one >> 16) & 0xFF] ^ - Crc32Lookup[3][(one >> 24) & 0xFF]; -#else - uint32_t one = *current++ ^ crc; - crc = Crc32Lookup[0][(one >> 24) & 0xFF] ^ - Crc32Lookup[1][(one >> 16) & 0xFF] ^ - Crc32Lookup[2][(one >> 8) & 0xFF] ^ - Crc32Lookup[3][one & 0xFF]; -#endif - - length -= 4; - } - - const uint8_t* currentChar = (const uint8_t*)current; - // remaining 1 to 3 bytes (standard algorithm) - while(length-- != 0) - crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; - - return ~crc; // same as crc ^ 0xFFFFFFFF -} - - -/// compute CRC32 (Slicing-by-8 algorithm) -uint32_t crc32_8bytes(const void* data, size_t length, uint32_t previousCrc32 /*= 0*/) -{ - uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF - const uint32_t* current = (const uint32_t*)data; - - // process eight bytes at once (Slicing-by-8) - while(length >= 8) - { -#if __BYTE_ORDER == __BIG_ENDIAN - uint32_t one = *current++ ^ swap(crc); - uint32_t two = *current++; - crc = Crc32Lookup[0][two & 0xFF] ^ - Crc32Lookup[1][(two >> 8) & 0xFF] ^ - Crc32Lookup[2][(two >> 16) & 0xFF] ^ - Crc32Lookup[3][(two >> 24) & 0xFF] ^ - Crc32Lookup[4][one & 0xFF] ^ - Crc32Lookup[5][(one >> 8) & 0xFF] ^ - Crc32Lookup[6][(one >> 16) & 0xFF] ^ - Crc32Lookup[7][(one >> 24) & 0xFF]; -#else - uint32_t one = *current++ ^ crc; - uint32_t two = *current++; - crc = Crc32Lookup[0][(two >> 24) & 0xFF] ^ - Crc32Lookup[1][(two >> 16) & 0xFF] ^ - Crc32Lookup[2][(two >> 8) & 0xFF] ^ - Crc32Lookup[3][two & 0xFF] ^ - Crc32Lookup[4][(one >> 24) & 0xFF] ^ - Crc32Lookup[5][(one >> 16) & 0xFF] ^ - Crc32Lookup[6][(one >> 8) & 0xFF] ^ - Crc32Lookup[7][one & 0xFF]; -#endif - - length -= 8; - } - - const uint8_t* currentChar = (const uint8_t*)current; - // remaining 1 to 7 bytes (standard algorithm) - while(length-- != 0) - crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; - - return ~crc; // same as crc ^ 0xFFFFFFFF -} - - -/// compute CRC32 (Slicing-by-8 algorithm), unroll inner loop 4 times -uint32_t crc32_4x8bytes(const void* data, size_t length, uint32_t previousCrc32 /*= 0*/) -{ - uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF - const uint32_t* current = (const uint32_t*)data; - - // enabling optimization (at least -O2) automatically unrolls the inner for-loop - const size_t Unroll = 4; - const size_t BytesAtOnce = 8 * Unroll; - size_t unrolling; - // process 4x eight bytes at once (Slicing-by-8) - while(length >= BytesAtOnce) - { - for(unrolling = 0; unrolling < Unroll; unrolling++) - { -#if __BYTE_ORDER == __BIG_ENDIAN - uint32_t one = *current++ ^ swap(crc); - uint32_t two = *current++; - crc = Crc32Lookup[0][two & 0xFF] ^ - Crc32Lookup[1][(two >> 8) & 0xFF] ^ - Crc32Lookup[2][(two >> 16) & 0xFF] ^ - Crc32Lookup[3][(two >> 24) & 0xFF] ^ - Crc32Lookup[4][one & 0xFF] ^ - Crc32Lookup[5][(one >> 8) & 0xFF] ^ - Crc32Lookup[6][(one >> 16) & 0xFF] ^ - Crc32Lookup[7][(one >> 24) & 0xFF]; -#else - uint32_t one = *current++ ^ crc; - uint32_t two = *current++; - crc = Crc32Lookup[0][(two >> 24) & 0xFF] ^ - Crc32Lookup[1][(two >> 16) & 0xFF] ^ - Crc32Lookup[2][(two >> 8) & 0xFF] ^ - Crc32Lookup[3][two & 0xFF] ^ - Crc32Lookup[4][(one >> 24) & 0xFF] ^ - Crc32Lookup[5][(one >> 16) & 0xFF] ^ - Crc32Lookup[6][(one >> 8) & 0xFF] ^ - Crc32Lookup[7][one & 0xFF]; -#endif - - } - - length -= BytesAtOnce; - } - - const uint8_t* currentChar = (const uint8_t*)current; - // remaining 1 to 31 bytes (standard algorithm) - while(length-- != 0) - crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; - - return ~crc; // same as crc ^ 0xFFFFFFFF -} - - -/// compute CRC32 (Slicing-by-16 algorithm) -uint32_t crc32_16bytes(const void* data, size_t length, uint32_t previousCrc32 /*= 0*/) -{ - uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF - const uint32_t* current = (const uint32_t*)data; - - // enabling optimization (at least -O2) automatically unrolls the inner for-loop - const size_t Unroll = 4; - const size_t BytesAtOnce = 16 * Unroll; - size_t unrolling; - - while(length >= BytesAtOnce) - { - for(unrolling = 0; unrolling < Unroll; unrolling++) - { -#if __BYTE_ORDER == __BIG_ENDIAN - uint32_t one = *current++ ^ swap(crc); - uint32_t two = *current++; - uint32_t three = *current++; - uint32_t four = *current++; - crc = Crc32Lookup[0][four & 0xFF] ^ - Crc32Lookup[1][(four >> 8) & 0xFF] ^ - Crc32Lookup[2][(four >> 16) & 0xFF] ^ - Crc32Lookup[3][(four >> 24) & 0xFF] ^ - Crc32Lookup[4][three & 0xFF] ^ - Crc32Lookup[5][(three >> 8) & 0xFF] ^ - Crc32Lookup[6][(three >> 16) & 0xFF] ^ - Crc32Lookup[7][(three >> 24) & 0xFF] ^ - Crc32Lookup[8][two & 0xFF] ^ - Crc32Lookup[9][(two >> 8) & 0xFF] ^ - Crc32Lookup[10][(two >> 16) & 0xFF] ^ - Crc32Lookup[11][(two >> 24) & 0xFF] ^ - Crc32Lookup[12][one & 0xFF] ^ - Crc32Lookup[13][(one >> 8) & 0xFF] ^ - Crc32Lookup[14][(one >> 16) & 0xFF] ^ - Crc32Lookup[15][(one >> 24) & 0xFF]; -#else - uint32_t one = *current++ ^ crc; - uint32_t two = *current++; - uint32_t three = *current++; - uint32_t four = *current++; - crc = Crc32Lookup[0][(four >> 24) & 0xFF] ^ - Crc32Lookup[1][(four >> 16) & 0xFF] ^ - Crc32Lookup[2][(four >> 8) & 0xFF] ^ - Crc32Lookup[3][four & 0xFF] ^ - Crc32Lookup[4][(three >> 24) & 0xFF] ^ - Crc32Lookup[5][(three >> 16) & 0xFF] ^ - Crc32Lookup[6][(three >> 8) & 0xFF] ^ - Crc32Lookup[7][three & 0xFF] ^ - Crc32Lookup[8][(two >> 24) & 0xFF] ^ - Crc32Lookup[9][(two >> 16) & 0xFF] ^ - Crc32Lookup[10][(two >> 8) & 0xFF] ^ - Crc32Lookup[11][two & 0xFF] ^ - Crc32Lookup[12][(one >> 24) & 0xFF] ^ - Crc32Lookup[13][(one >> 16) & 0xFF] ^ - Crc32Lookup[14][(one >> 8) & 0xFF] ^ - Crc32Lookup[15][one & 0xFF]; -#endif - } - - length -= BytesAtOnce; - } - - const uint8_t* currentChar = (const uint8_t*)current; - // remaining 1 to 63 bytes (standard algorithm) - while(length-- != 0) - crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; - - return ~crc; // same as crc ^ 0xFFFFFFFF -} - - -/// compute CRC32 (Slicing-by-16 algorithm, prefetch upcoming data blocks) -uint32_t crc32_16bytes_prefetch(const void* data, size_t length, uint32_t previousCrc32 /*= 0*/, size_t prefetchAhead /*= 256*/) -{ - // CRC code is identical to crc32_16bytes (including unrolling), only added prefetching - // 256 bytes look-ahead seems to be the sweet spot on Core i7 CPUs - - uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF - const uint32_t* current = (const uint32_t*)data; - - // enabling optimization (at least -O2) automatically unrolls the for-loop - const size_t Unroll = 4; - const size_t BytesAtOnce = 16 * Unroll; - size_t unrolling; - - while(length >= BytesAtOnce + prefetchAhead) - { - PREFETCH(((const char*)current) + prefetchAhead); - - for(unrolling = 0; unrolling < Unroll; unrolling++) - { -#if __BYTE_ORDER == __BIG_ENDIAN - uint32_t one = *current++ ^ swap(crc); - uint32_t two = *current++; - uint32_t three = *current++; - uint32_t four = *current++; - crc = Crc32Lookup[0][four & 0xFF] ^ - Crc32Lookup[1][(four >> 8) & 0xFF] ^ - Crc32Lookup[2][(four >> 16) & 0xFF] ^ - Crc32Lookup[3][(four >> 24) & 0xFF] ^ - Crc32Lookup[4][three & 0xFF] ^ - Crc32Lookup[5][(three >> 8) & 0xFF] ^ - Crc32Lookup[6][(three >> 16) & 0xFF] ^ - Crc32Lookup[7][(three >> 24) & 0xFF] ^ - Crc32Lookup[8][two & 0xFF] ^ - Crc32Lookup[9][(two >> 8) & 0xFF] ^ - Crc32Lookup[10][(two >> 16) & 0xFF] ^ - Crc32Lookup[11][(two >> 24) & 0xFF] ^ - Crc32Lookup[12][one & 0xFF] ^ - Crc32Lookup[13][(one >> 8) & 0xFF] ^ - Crc32Lookup[14][(one >> 16) & 0xFF] ^ - Crc32Lookup[15][(one >> 24) & 0xFF]; -#else - uint32_t one = *current++ ^ crc; - uint32_t two = *current++; - uint32_t three = *current++; - uint32_t four = *current++; - crc = Crc32Lookup[0][(four >> 24) & 0xFF] ^ - Crc32Lookup[1][(four >> 16) & 0xFF] ^ - Crc32Lookup[2][(four >> 8) & 0xFF] ^ - Crc32Lookup[3][four & 0xFF] ^ - Crc32Lookup[4][(three >> 24) & 0xFF] ^ - Crc32Lookup[5][(three >> 16) & 0xFF] ^ - Crc32Lookup[6][(three >> 8) & 0xFF] ^ - Crc32Lookup[7][three & 0xFF] ^ - Crc32Lookup[8][(two >> 24) & 0xFF] ^ - Crc32Lookup[9][(two >> 16) & 0xFF] ^ - Crc32Lookup[10][(two >> 8) & 0xFF] ^ - Crc32Lookup[11][two & 0xFF] ^ - Crc32Lookup[12][(one >> 24) & 0xFF] ^ - Crc32Lookup[13][(one >> 16) & 0xFF] ^ - Crc32Lookup[14][(one >> 8) & 0xFF] ^ - Crc32Lookup[15][one & 0xFF]; -#endif - } - - length -= BytesAtOnce; - } - - const uint8_t* currentChar = (const uint8_t*)current; - // remaining 1 to 63 bytes (standard algorithm) - while(length-- != 0) - crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; - - return ~crc; // same as crc ^ 0xFFFFFFFF -} - -// Solution to be used on Mac OS X (it gives better preciseness of data compression -// as in case of zlib-cf) -/* -static uint32_t hash_func_default(deflate_state *s, uint32_t UNUSED(h), void* str) { - return crc32_8bytes(str, 32, 0) & s->hash_mask; -} -*/ - -// CRC32 calculation from zlib 1.2.8 - Madler -static uint32_t hash_func_default(deflate_state *s, uint32_t h, void* str) { - return ((h << s->hash_shift) ^ (*(uint32_t*)str)) & s->hash_mask; -} - -#if defined (__aarch64__) - -#pragma GCC push_options -#if __ARM_ARCH >= 8 -#pragma GCC target ("arch=armv8-a+crc") -#endif - -#if defined (__ARM_FEATURE_CRC32) -#include - -static uint32_t hash_func(deflate_state *s, uint32_t UNUSED(h), void* str) { - return __crc32cw(0, *(uint32_t*)str) & s->hash_mask; -} - -#else // ARMv8 without crc32 support - -static uint32_t hash_func(deflate_state *s, uint32_t h, void* str) { - return hash_func_default(s, h, str); -} - -#endif // ARMv8 without crc32 support -#elif defined (__x86_64__) && defined (__linux__) && ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) || (__clang__)) - -static uint32_t hash_func_sse42(deflate_state *s, uint32_t UNUSED(h), void* str) __attribute__ ((__target__ ("sse4.2"))); - -static uint32_t hash_func_sse42(deflate_state *s, uint32_t UNUSED(h), void* str) { - return _mm_crc32_u32(0, *(uint32_t*)str) & s->hash_mask; -} - -static uint32_t hash_func(deflate_state *s, uint32_t UNUSED(h), void* str) __attribute__ ((ifunc ("resolve_hash_func"))); - -void *resolve_hash_func(void) -{ - unsigned int eax, ebx, ecx, edx; - if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx)) - return hash_func_default; - /* We need SSE4.2 ISA support */ - if (!(ecx & bit_SSE4_2)) - return hash_func_default; - return hash_func_sse42; -} - -#else - -static uint32_t hash_func(deflate_state *s, uint32_t h, void* str) { - return hash_func_default(s, h, str); -} - -#endif - - -/* =========================================================================== - * Insert string str in the dictionary and return the previous head - * of the hash chain (the most recent string with same hash key). - * IN assertion: ACTUAL_MIN_MATCH bytes of str are valid - * (except for the last ACTUAL_MIN_MATCH-1 bytes of the input file). - */ -static Pos insert_string(deflate_state *s, Pos str) { - Pos match_head; - s->ins_h = hash_func(s, s->ins_h, &s->window[str]); - match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h]; - s->head[s->ins_h] = (Pos)str; - return match_head; -} - -static void bulk_insert_str(deflate_state *s, Pos startpos, uint32_t count) { - uint32_t idx; - for (idx = 0; idx < count; idx++) { - s->ins_h = hash_func(s, s->ins_h, &s->window[startpos + idx]); - s->prev[(startpos + idx) & s->w_mask] = s->head[s->ins_h]; - s->head[s->ins_h] = (Pos)(startpos + idx); - } -} - -static int _tr_tally_lit(deflate_state *s, uint8_t cc) { - s->d_buf[s->last_lit] = 0; - s->l_buf[s->last_lit++] = cc; - s->dyn_ltree[cc].Freq++; - return (s->last_lit == s->lit_bufsize-1); -} - -static int _tr_tally_dist(deflate_state *s, uint16_t dist, uint8_t len) { - s->d_buf[s->last_lit] = dist; - s->l_buf[s->last_lit++] = len; - dist--; - s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; - s->dyn_dtree[d_code(dist)].Freq++; - return (s->last_lit == s->lit_bufsize-1); -} -/* =========================================================================== - * Initialize the hash table prev[] will be initialized on the fly. - */ -#define CLEAR_HASH(s) \ - zmemzero((uint8_t *)s->head, (unsigned)(s->hash_size)*sizeof(*s->head)); - -/* ========================================================================= */ -int ZEXPORT deflateInit_(strm, level, version, stream_size) - z_streamp strm; - int level; - const char *version; - int stream_size; -{ - return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, version, stream_size); - /* To do: ignore strm->next_in if we use it as window */ -} - -/* ========================================================================= */ -int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, - version, stream_size) - z_streamp strm; - int level; - int method; - int windowBits; - int memLevel; - int strategy; - const char *version; - int stream_size; -{ - deflate_state *s; - int wrap = 1; - static const char my_version[] = ZLIB_VERSION; - - uint16_t *overlay; - /* We overlay pending_buf and d_buf+l_buf. This works since the average - * output size for (length,distance) codes is <= 24 bits. - */ - - if (version == Z_NULL || version[0] != my_version[0] || - stream_size != sizeof(z_stream)) { - return Z_VERSION_ERROR; - } - if (strm == Z_NULL) return Z_STREAM_ERROR; - - strm->msg = Z_NULL; - if (strm->zalloc == (alloc_func)0) { -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zalloc = zcalloc; - strm->opaque = (voidpf)0; -#endif - } - if (strm->zfree == (free_func)0) -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zfree = zcfree; -#endif - - if (level == Z_DEFAULT_COMPRESSION) level = 6; - - if (windowBits < 0) { /* suppress zlib wrapper */ - wrap = 0; - windowBits = -windowBits; - } - else if (windowBits > 15) { - wrap = 2; /* write gzip wrapper instead */ - windowBits -= 16; - } - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || - windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || - strategy < 0 || strategy > Z_FIXED) { - return Z_STREAM_ERROR; - } - if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ - s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); - if (s == Z_NULL) return Z_MEM_ERROR; - strm->state = (struct internal_state *)s; - s->strm = strm; - - s->wrap = wrap; - s->gzhead = Z_NULL; - s->w_bits = windowBits; - s->w_size = 1 << s->w_bits; - s->w_mask = s->w_size - 1; - - s->hash_bits = memLevel + 7; - s->hash_size = 1 << s->hash_bits; - s->hash_mask = s->hash_size - 1; - - s->window = (uint8_t *) ZALLOC(strm, s->w_size, 2*sizeof(uint8_t)); - s->prev = (Pos *) ZALLOC(strm, s->w_size, sizeof(Pos)); - s->head = (Pos *) ZALLOC(strm, s->hash_size, sizeof(Pos)); - - s->high_water = 0; /* nothing written to s->window yet */ - - s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ - - overlay = (uint16_t *) ZALLOC(strm, s->lit_bufsize, sizeof(uint16_t)+2); - s->pending_buf = (uint8_t *) overlay; - s->pending_buf_size = (uint64_t)s->lit_bufsize * (sizeof(uint16_t)+2L); - - if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || - s->pending_buf == Z_NULL) { - s->status = FINISH_STATE; - strm->msg = ERR_MSG(Z_MEM_ERROR); - deflateEnd (strm); - return Z_MEM_ERROR; - } - s->d_buf = overlay + s->lit_bufsize/sizeof(uint16_t); - s->l_buf = s->pending_buf + (1+sizeof(uint16_t))*s->lit_bufsize; - - s->level = level; - s->strategy = strategy; - s->method = (uint8_t)method; - - return deflateReset(strm); -} - -/* ========================================================================= */ -int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) - z_streamp strm; - const uint8_t *dictionary; - uint32_t dictLength; -{ - deflate_state *s; - uint32_t str, n; - int wrap; - uint32_t avail; - z_const uint8_t *next; - - if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) - return Z_STREAM_ERROR; - s = strm->state; - wrap = s->wrap; - if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) - return Z_STREAM_ERROR; - - /* when using zlib wrappers, compute Adler-32 for provided dictionary */ - if (wrap == 1) - strm->adler = adler32(strm->adler, dictionary, dictLength); - s->wrap = 0; /* avoid computing Adler-32 in read_buf */ - - /* if dictionary would fill window, just replace the history */ - if (dictLength >= s->w_size) { - if (wrap == 0) { /* already empty otherwise */ - CLEAR_HASH(s); - s->strstart = 0; - s->block_start = 0L; - s->insert = 0; - } - dictionary += dictLength - s->w_size; /* use the tail */ - dictLength = s->w_size; - } - - /* insert dictionary into window and hash */ - avail = strm->avail_in; - next = strm->next_in; - strm->avail_in = dictLength; - strm->next_in = (z_const uint8_t*)dictionary; - fill_window(s); - while (s->lookahead >= ACTUAL_MIN_MATCH) { - str = s->strstart; - n = s->lookahead - (ACTUAL_MIN_MATCH-1); - bulk_insert_str(s, str, n); - s->strstart = str + n; - s->lookahead = ACTUAL_MIN_MATCH-1; - fill_window(s); - } - s->strstart += s->lookahead; - s->block_start = (long)s->strstart; - s->insert = s->lookahead; - s->lookahead = 0; - s->match_length = s->prev_length = ACTUAL_MIN_MATCH-1; - s->match_available = 0; - strm->next_in = next; - strm->avail_in = avail; - s->wrap = wrap; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateResetKeep (strm) - z_streamp strm; -{ - deflate_state *s; - - if (strm == Z_NULL || strm->state == Z_NULL || - strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { - return Z_STREAM_ERROR; - } - - strm->total_in = strm->total_out = 0; - strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ - strm->data_type = Z_UNKNOWN; - - s = (deflate_state *)strm->state; - s->pending = 0; - s->pending_out = s->pending_buf; - - if (s->wrap < 0) { - s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ - } - s->status = s->wrap ? INIT_STATE : BUSY_STATE; - strm->adler = - s->wrap == 2 ? crc32(0L, Z_NULL, 0) : - adler32(0L, Z_NULL, 0); - s->last_flush = Z_NO_FLUSH; - - _tr_init(s); - - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateReset (strm) - z_streamp strm; -{ - int ret; - - ret = deflateResetKeep(strm); - if (ret == Z_OK) - lm_init(strm->state); - return ret; -} - -/* ========================================================================= */ -int ZEXPORT deflateSetHeader (strm, head) - z_streamp strm; - gz_headerp head; -{ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - if (strm->state->wrap != 2) return Z_STREAM_ERROR; - strm->state->gzhead = head; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflatePending (strm, pending, bits) - uint32_t *pending; - int *bits; - z_streamp strm; -{ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - if (pending != Z_NULL) - *pending = strm->state->pending; - if (bits != Z_NULL) - *bits = strm->state->bi_valid; - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflatePrime (strm, bits, value) - z_streamp strm; - int bits; - int value; -{ - deflate_state *s; - int put; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - s = strm->state; - if ((uint8_t *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) - return Z_BUF_ERROR; - do { - put = Buf_size - s->bi_valid; - if (put > bits) - put = bits; - s->bi_buf |= (uint16_t)((value & ((1 << put) - 1)) << s->bi_valid); - s->bi_valid += put; - _tr_flush_bits(s); - value >>= put; - bits -= put; - } while (bits); - return Z_OK; -} - -/* ========================================================================= */ -int ZEXPORT deflateParams(strm, level, strategy) - z_streamp strm; - int level; - int strategy; -{ - deflate_state *s; - compress_func func; - int err = Z_OK; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - s = strm->state; - - if (level == Z_DEFAULT_COMPRESSION) level = 6; - if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { - return Z_STREAM_ERROR; - } - func = configuration_table[s->level].func; - - if ((strategy != s->strategy || func != configuration_table[level].func) && - strm->total_in != 0) { - /* Flush the last buffer: */ - err = deflate(strm, Z_BLOCK); - if (err == Z_BUF_ERROR && s->pending == 0) - err = Z_OK; - } - if (s->level != level) { - s->level = level; - s->max_lazy_match = configuration_table[level].max_lazy; - s->good_match = configuration_table[level].good_length; - s->nice_match = configuration_table[level].nice_length; - s->max_chain_length = configuration_table[level].max_chain; - } - s->strategy = strategy; - return err; -} - -/* ========================================================================= */ -int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) - z_streamp strm; - int good_length; - int max_lazy; - int nice_length; - int max_chain; -{ - deflate_state *s; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - s = strm->state; - s->good_match = good_length; - s->max_lazy_match = max_lazy; - s->nice_match = nice_length; - s->max_chain_length = max_chain; - return Z_OK; -} - -/* ========================================================================= - * For the default windowBits of 15 and memLevel of 8, this function returns - * a close to exact, as well as small, upper bound on the compressed size. - * They are coded as constants here for a reason--if the #define's are - * changed, then this function needs to be changed as well. The return - * value for 15 and 8 only works for those exact settings. - * - * For any setting other than those defaults for windowBits and memLevel, - * the value returned is a conservative worst case for the maximum expansion - * resulting from using fixed blocks instead of stored blocks, which deflate - * can emit on compressed data for some combinations of the parameters. - * - * This function could be more sophisticated to provide closer upper bounds for - * every combination of windowBits and memLevel. But even the conservative - * upper bound of about 14% expansion does not seem onerous for output buffer - * allocation. - */ -uint64_t ZEXPORT deflateBound(strm, sourceLen) - z_streamp strm; - uint64_t sourceLen; -{ - deflate_state *s; - uint64_t complen, wraplen; - uint8_t *str; - - /* conservative upper bound for compressed data */ - complen = sourceLen + - ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; - - /* if can't get parameters, return conservative bound plus zlib wrapper */ - if (strm == Z_NULL || strm->state == Z_NULL) - return complen + 6; - - /* compute wrapper length */ - s = strm->state; - switch (s->wrap) { - case 0: /* raw deflate */ - wraplen = 0; - break; - case 1: /* zlib wrapper */ - wraplen = 6 + (s->strstart ? 4 : 0); - break; - case 2: /* gzip wrapper */ - wraplen = 18; - if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ - if (s->gzhead->extra != Z_NULL) - wraplen += 2 + s->gzhead->extra_len; - str = s->gzhead->name; - if (str != Z_NULL) - do { - wraplen++; - } while (*str++); - str = s->gzhead->comment; - if (str != Z_NULL) - do { - wraplen++; - } while (*str++); - if (s->gzhead->hcrc) - wraplen += 2; - } - break; - default: /* for compiler happiness */ - wraplen = 6; - } - - /* if not default parameters, return conservative bound */ - if (s->w_bits != 15 || s->hash_bits != 8 + 7) - return complen + wraplen; - - /* default settings: return tight bound for that case */ - return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + - (sourceLen >> 25) + 13 - 6 + wraplen; -} - -/* ========================================================================= - * Put a short in the pending buffer. The 16-bit value is put in MSB order. - * IN assertion: the stream state is correct and there is enough room in - * pending_buf. - */ -static void putShortMSB (s, b) - deflate_state *s; - uint32_t b; -{ - put_byte(s, (uint8_t)(b >> 8)); - put_byte(s, (uint8_t)(b & 0xff)); -} - -/* ========================================================================= - * Flush as much pending output as possible. All deflate() output goes - * through this function so some applications may wish to modify it - * to avoid allocating a large strm->next_out buffer and copying into it. - * (See also read_buf()). - */ -static void flush_pending(strm) - z_streamp strm; -{ - uint32_t len; - deflate_state *s = strm->state; - - _tr_flush_bits(s); - len = s->pending; - if (len > strm->avail_out) len = strm->avail_out; - if (len == 0) return; - - zmemcpy(strm->next_out, s->pending_out, len); - strm->next_out += len; - s->pending_out += len; - strm->total_out += len; - strm->avail_out -= len; - s->pending -= len; - if (s->pending == 0) { - s->pending_out = s->pending_buf; - } -} - -/* ========================================================================= */ -int ZEXPORT deflate (strm, flush) - z_streamp strm; - int flush; -{ - int old_flush; /* value of flush param for previous deflate call */ - deflate_state *s; - - if (strm == Z_NULL || strm->state == Z_NULL || - flush > Z_BLOCK || flush < 0) { - return Z_STREAM_ERROR; - } - s = strm->state; - - if (strm->next_out == Z_NULL || - (strm->next_in == Z_NULL && strm->avail_in != 0) || - (s->status == FINISH_STATE && flush != Z_FINISH)) { - ERR_RETURN(strm, Z_STREAM_ERROR); - } - if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); - - s->strm = strm; /* just in case */ - old_flush = s->last_flush; - s->last_flush = flush; - - /* Write the header */ - if (s->status == INIT_STATE) { - if (s->wrap == 2) { - strm->adler = crc32(0L, Z_NULL, 0); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (s->gzhead == Z_NULL) { - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s->status = BUSY_STATE; - } - else { - put_byte(s, (s->gzhead->text ? 1 : 0) + - (s->gzhead->hcrc ? 2 : 0) + - (s->gzhead->extra == Z_NULL ? 0 : 4) + - (s->gzhead->name == Z_NULL ? 0 : 8) + - (s->gzhead->comment == Z_NULL ? 0 : 16) - ); - put_byte(s, (uint8_t)(s->gzhead->time & 0xff)); - put_byte(s, (uint8_t)((s->gzhead->time >> 8) & 0xff)); - put_byte(s, (uint8_t)((s->gzhead->time >> 16) & 0xff)); - put_byte(s, (uint8_t)((s->gzhead->time >> 24) & 0xff)); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, s->gzhead->os & 0xff); - if (s->gzhead->extra != Z_NULL) { - put_byte(s, s->gzhead->extra_len & 0xff); - put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); - } - if (s->gzhead->hcrc) - strm->adler = crc32(strm->adler, s->pending_buf, - s->pending); - s->gzindex = 0; - s->status = EXTRA_STATE; - } - } - else - { - uint32_t header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; - uint32_t level_flags; - - if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) - level_flags = 0; - else if (s->level < 6) - level_flags = 1; - else if (s->level == 6) - level_flags = 2; - else - level_flags = 3; - header |= (level_flags << 6); - if (s->strstart != 0) header |= PRESET_DICT; - header += 31 - (header % 31); - - s->status = BUSY_STATE; - putShortMSB(s, header); - - /* Save the adler32 of the preset dictionary: */ - if (s->strstart != 0) { - putShortMSB(s, (uint32_t)(strm->adler >> 16)); - putShortMSB(s, (uint32_t)(strm->adler & 0xffff)); - } - strm->adler = adler32(0L, Z_NULL, 0); - } - } - if (s->status == EXTRA_STATE) { - if (s->gzhead->extra != Z_NULL) { - uint32_t beg = s->pending; /* start of bytes to update crc */ - - while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) - break; - } - put_byte(s, s->gzhead->extra[s->gzindex]); - s->gzindex++; - } - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (s->gzindex == s->gzhead->extra_len) { - s->gzindex = 0; - s->status = NAME_STATE; - } - } - else - s->status = NAME_STATE; - } - if (s->status == NAME_STATE) { - if (s->gzhead->name != Z_NULL) { - uint32_t beg = s->pending; /* start of bytes to update crc */ - int val; - - do { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; - } - } - val = s->gzhead->name[s->gzindex++]; - put_byte(s, val); - } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (val == 0) { - s->gzindex = 0; - s->status = COMMENT_STATE; - } - } - else - s->status = COMMENT_STATE; - } - if (s->status == COMMENT_STATE) { - if (s->gzhead->comment != Z_NULL) { - uint32_t beg = s->pending; /* start of bytes to update crc */ - int val; - - do { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; - } - } - val = s->gzhead->comment[s->gzindex++]; - put_byte(s, val); - } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, - s->pending - beg); - if (val == 0) - s->status = HCRC_STATE; - } - else - s->status = HCRC_STATE; - } - if (s->status == HCRC_STATE) { - if (s->gzhead->hcrc) { - if (s->pending + 2 > s->pending_buf_size) - flush_pending(strm); - if (s->pending + 2 <= s->pending_buf_size) { - put_byte(s, (uint8_t)(strm->adler & 0xff)); - put_byte(s, (uint8_t)((strm->adler >> 8) & 0xff)); - strm->adler = crc32(0L, Z_NULL, 0); - s->status = BUSY_STATE; - } - } - else - s->status = BUSY_STATE; - } - - /* Flush as much pending output as possible */ - if (s->pending != 0) { - flush_pending(strm); - if (strm->avail_out == 0) { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ - s->last_flush = -1; - return Z_OK; - } - - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Z_FINISH, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && - flush != Z_FINISH) { - ERR_RETURN(strm, Z_BUF_ERROR); - } - - /* User must not provide more input after the first FINISH: */ - if (s->status == FINISH_STATE && strm->avail_in != 0) { - ERR_RETURN(strm, Z_BUF_ERROR); - } - - /* Start a new block or continue the current one. - */ - if (strm->avail_in != 0 || s->lookahead != 0 || - (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { - block_state bstate; - - bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : - (s->strategy == Z_RLE ? deflate_rle(s, flush) : - (*(configuration_table[s->level].func))(s, flush)); - - if (bstate == finish_started || bstate == finish_done) { - s->status = FINISH_STATE; - } - if (bstate == need_more || bstate == finish_started) { - if (strm->avail_out == 0) { - s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ - } - return Z_OK; - /* If flush != Z_NO_FLUSH && avail_out == 0, the next call - * of deflate should use the same flush parameter to make sure - * that the flush is complete. So we don't have to output an - * empty block here, this will be done at next call. This also - * ensures that for a very small output buffer, we emit at most - * one empty block. - */ - } - if (bstate == block_done) { - if (flush == Z_PARTIAL_FLUSH) { - _tr_align(s); - } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ - _tr_stored_block(s, (uint8_t*)0, 0L, 0); - /* For a full flush, this empty block will be recognized - * as a special marker by inflate_sync(). - */ - if (flush == Z_FULL_FLUSH) { - CLEAR_HASH(s); /* forget history */ - if (s->lookahead == 0) { - s->strstart = 0; - s->block_start = 0L; - s->insert = 0; - } - } - } - flush_pending(strm); - if (strm->avail_out == 0) { - s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ - return Z_OK; - } - } - } - Assert(strm->avail_out > 0, "bug2"); - - if (flush != Z_FINISH) return Z_OK; - if (s->wrap <= 0) return Z_STREAM_END; - - /* Write the trailer */ - if (s->wrap == 2) { - put_byte(s, (uint8_t)(strm->adler & 0xff)); - put_byte(s, (uint8_t)((strm->adler >> 8) & 0xff)); - put_byte(s, (uint8_t)((strm->adler >> 16) & 0xff)); - put_byte(s, (uint8_t)((strm->adler >> 24) & 0xff)); - put_byte(s, (uint8_t)(strm->total_in & 0xff)); - put_byte(s, (uint8_t)((strm->total_in >> 8) & 0xff)); - put_byte(s, (uint8_t)((strm->total_in >> 16) & 0xff)); - put_byte(s, (uint8_t)((strm->total_in >> 24) & 0xff)); - } - else - { - putShortMSB(s, (uint32_t)(strm->adler >> 16)); - putShortMSB(s, (uint32_t)(strm->adler & 0xffff)); - } - flush_pending(strm); - /* If avail_out is zero, the application will call deflate again - * to flush the rest. - */ - if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ - return s->pending != 0 ? Z_OK : Z_STREAM_END; -} - -/* ========================================================================= */ -int ZEXPORT deflateEnd (strm) - z_streamp strm; -{ - int status; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - - status = strm->state->status; - if (status != INIT_STATE && - status != EXTRA_STATE && - status != NAME_STATE && - status != COMMENT_STATE && - status != HCRC_STATE && - status != BUSY_STATE && - status != FINISH_STATE) { - return Z_STREAM_ERROR; - } - - /* Deallocate in reverse order of allocations: */ - TRY_FREE(strm, strm->state->pending_buf); - TRY_FREE(strm, strm->state->head); - TRY_FREE(strm, strm->state->prev); - TRY_FREE(strm, strm->state->window); - - ZFREE(strm, strm->state); - strm->state = Z_NULL; - - return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; -} - -/* ========================================================================= - * Copy the source state to the destination state. - * To simplify the source, this is not supported for 16-bit MSDOS (which - * doesn't have enough memory anyway to duplicate compression states). - */ -int ZEXPORT deflateCopy (dest, source) - z_streamp dest; - z_streamp source; -{ - deflate_state *ds; - deflate_state *ss; - uint16_t *overlay; - - - if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { - return Z_STREAM_ERROR; - } - - ss = source->state; - - zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); - - ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); - if (ds == Z_NULL) return Z_MEM_ERROR; - dest->state = (struct internal_state *) ds; - zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); - ds->strm = dest; - - ds->window = (uint8_t *) ZALLOC(dest, ds->w_size, 2*sizeof(uint8_t)); - ds->prev = (Pos *) ZALLOC(dest, ds->w_size, sizeof(Pos)); - ds->head = (Pos *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); - overlay = (uint16_t *) ZALLOC(dest, ds->lit_bufsize, sizeof(uint16_t)+2); - ds->pending_buf = (uint8_t *) overlay; - - if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || - ds->pending_buf == Z_NULL) { - deflateEnd (dest); - return Z_MEM_ERROR; - } - /* following zmemcpy do not work for 16-bit MSDOS */ - zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(uint8_t)); - zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); - zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); - zmemcpy(ds->pending_buf, ss->pending_buf, (uint32_t)ds->pending_buf_size); - - ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); - ds->d_buf = overlay + ds->lit_bufsize/sizeof(uint16_t); - ds->l_buf = ds->pending_buf + (1+sizeof(uint16_t))*ds->lit_bufsize; - - ds->l_desc.dyn_tree = ds->dyn_ltree; - ds->d_desc.dyn_tree = ds->dyn_dtree; - ds->bl_desc.dyn_tree = ds->bl_tree; - - return Z_OK; -} - -/* =========================================================================== - * Read a new buffer from the current input stream, update the adler32 - * and total number of bytes read. All deflate() input goes through - * this function so some applications may wish to modify it to avoid - * allocating a large strm->next_in buffer and copying from it. - * (See also flush_pending()). - */ -static int read_buf(strm, buf, size) - z_streamp strm; - uint8_t *buf; - uint32_t size; -{ - uint32_t len = strm->avail_in; - - if (len > size) len = size; - if (len == 0) return 0; - - strm->avail_in -= len; - - zmemcpy(buf, strm->next_in, len); - if (strm->state->wrap == 1) { - strm->adler = adler32(strm->adler, buf, len); - } - else if (strm->state->wrap == 2) { - strm->adler = crc32(strm->adler, buf, len); - } - strm->next_in += len; - strm->total_in += len; - - return (int)len; -} - -/* =========================================================================== - * Initialize the "longest match" routines for a new zlib stream - */ -static void lm_init (s) - deflate_state *s; -{ - s->window_size = (uint64_t)2L*s->w_size; - - CLEAR_HASH(s); - - /* Set the default configuration parameters: - */ - s->max_lazy_match = configuration_table[s->level].max_lazy; - s->good_match = configuration_table[s->level].good_length; - s->nice_match = configuration_table[s->level].nice_length; - s->max_chain_length = configuration_table[s->level].max_chain; - - s->strstart = 0; - s->block_start = 0L; - s->lookahead = 0; - s->insert = 0; - s->match_length = s->prev_length = ACTUAL_MIN_MATCH-1; - s->match_available = 0; - s->ins_h = 0; -} - -/* longest_match() with minor change to improve performance (in terms of - * execution time). - * - * The pristine longest_match() function is sketched bellow (strip the - * then-clause of the "#ifdef UNALIGNED_OK"-directive) - * - * ------------------------------------------------------------ - * uInt longest_match(...) { - * ... - * do { - * match = s->window + cur_match; //s0 - * if (*(ushf*)(match+best_len-1) != scan_end || //s1 - * *(ushf*)match != scan_start) continue; //s2 - * ... - * - * do { - * } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && - * *(ushf*)(scan+=2) == *(ushf*)(match+=2) && - * *(ushf*)(scan+=2) == *(ushf*)(match+=2) && - * *(ushf*)(scan+=2) == *(ushf*)(match+=2) && - * scan < strend); //s3 - * - * ... - * } while(cond); //s4 - * - * ------------------------------------------------------------- - * - * The change include: - * - * 1) The hottest statements of the function is: s0, s1 and s4. Pull them - * together to form a new loop. The benefit is two-fold: - * - * o. Ease the compiler to yield good code layout: the conditional-branch - * corresponding to s1 and its biased target s4 become very close (likely, - * fit in the same cache-line), hence improving instruction-fetching - * efficiency. - * - * o. Ease the compiler to promote "s->window" into register. "s->window" - * is loop-invariant; it is supposed to be promoted into register and keep - * the value throughout the entire loop. However, there are many such - * loop-invariant, and x86-family has small register file; "s->window" is - * likely to be chosen as register-allocation victim such that its value - * is reloaded from memory in every single iteration. By forming a new loop, - * "s->window" is loop-invariant of that newly created tight loop. It is - * lot easier for compiler to promote this quantity to register and keep - * its value throughout the entire small loop. - * - * 2) Transfrom s3 such that it examines sizeof(long)-byte-match at a time. - * This is done by: - * ------------------------------------------------ - * v1 = load from "scan" by sizeof(long) bytes - * v2 = load from "match" by sizeof(lnog) bytes - * v3 = v1 xor v2 - * match-bit = little-endian-machine(yes-for-x86) ? - * count-trailing-zero(v3) : - * count-leading-zero(v3); - * - * match-byte = match-bit/8 - * - * "scan" and "match" advance if necessary - * ------------------------------------------------- - */ - -static uint32_t longest_match(s, cur_match) - deflate_state *s; - IPos cur_match; /* current match */ -{ - uint32_t chain_length = s->max_chain_length; /* max hash chain length */ - register uint8_t *scan = s->window + s->strstart; /* current string */ - register uint8_t *match; /* matched string */ - register int len; /* length of current match */ - int best_len = s->prev_length; /* best match length so far */ - int nice_match = s->nice_match; /* stop if match long enough */ - IPos limit = s->strstart > (IPos)MAX_DIST(s) ? - s->strstart - (IPos)MAX_DIST(s) : NIL; - /* Stop when cur_match becomes <= limit. To simplify the code, - * we prevent matches with the string of window index 0. - */ - Pos *prev = s->prev; - uint32_t wmask = s->w_mask; - - register uint8_t *strend = s->window + s->strstart + MAX_MATCH; - /* We optimize for a minimal match of four bytes */ - register uint32_t scan_start = *(uint32_t*)scan; - register uint32_t scan_end = *(uint32_t*)(scan+best_len-3); - - /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - * It is easy to get rid of this optimization if necessary. - */ - Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); - - /* Do not waste too much time if we already have a good match: */ - if (s->prev_length >= s->good_match) { - chain_length >>= 2; - } - /* Do not look for matches beyond the end of the input. This is necessary - * to make deflate deterministic. - */ - if ((uint32_t)nice_match > s->lookahead) nice_match = s->lookahead; - - Assert((uint64_t)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); - - do { - int cont ; - Assert(cur_match < s->strstart, "no future"); - - /* Skip to next match if the match length cannot increase - * or if the match length is less than 2. Note that the checks below - * for insufficient lookahead only occur occasionally for performance - * reasons. Therefore uninitialized memory will be accessed, and - * conditional jumps will be made that depend on those values. - * However the length of the match is limited to the lookahead, so - * the output of deflate is not affected by the uninitialized values. - */ - cont = 1; - do { - match = s->window + cur_match; - if (likely(*(uint32_t*)(match+best_len-3) != scan_end) || (*(uint32_t*)match != scan_start)) { - if ((cur_match = prev[cur_match & wmask]) > limit - && --chain_length != 0) { - continue; - } else - cont = 0; - } - break; - } while (1); - - if (!cont) - break; - - scan += 4, match+=4; - do { - uint64_t sv = *(uint64_t*)(void*)scan; - uint64_t mv = *(uint64_t*)(void*)match; - uint64_t xor = sv ^ mv; - if (xor) { - int match_byte = __builtin_ctzl(xor) / 8; - scan += match_byte; - match += match_byte; - break; - } else { - scan += 8; - match += 8; - } - } while (scan < strend); - - if (scan > strend) - scan = strend; - - Assert(scan <= s->window+(uint32_t)(s->window_size-1), "wild scan"); - - len = MAX_MATCH - (int)(strend - scan); - scan = strend - MAX_MATCH; - - if (len > best_len) { - s->match_start = cur_match; - best_len = len; - if (len >= nice_match) break; - scan_end = *(uint32_t*)(scan+best_len-3); - } - } while ((cur_match = prev[cur_match & wmask]) > limit - && --chain_length != 0); - - if ((uint32_t)best_len <= s->lookahead) return (uint32_t)best_len; - return s->lookahead; -} - -#ifdef DEBUG -/* =========================================================================== - * Check that the match at match_start is indeed a match. - */ -static void check_match(s, start, match, length) - deflate_state *s; - IPos start, match; - int length; -{ - /* check that the match is indeed a match */ - if (zmemcmp(s->window + match, - s->window + start, length) != EQUAL) { - fprintf(stderr, " start %u, match %u, length %d\n", - start, match, length); - do { - fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); - } while (--length != 0); - z_error("invalid match"); - } - if (z_verbose > 1) { - fprintf(stderr,"\\[%d,%d]", start-match, length); - do { putc(s->window[start++], stderr); } while (--length != 0); - } -} -#else -# define check_match(s, start, match, length) -#endif /* DEBUG */ - -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ -static void fill_window_default(s) - deflate_state *s; -{ - register unsigned n, m; - register Pos *p; - unsigned more; /* Amount of free space at the end of the window. */ - uInt wsize = s->w_size; - - Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); - - /* Deal with !@#$% 64K limit: */ - if (sizeof(int) <= 2) { - if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - more = wsize; - - } else if (more == (unsigned)(-1)) { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - if (s->strstart >= wsize+MAX_DIST(s)) { - zmemcpy(s->window, s->window+wsize, (unsigned)wsize); - s->match_start -= wsize; - s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ - s->block_start -= (long) wsize; - - /* Slide the hash table (could be avoided with 32 bit values - at the expense of memory usage). We slide even when level == 0 - to keep the hash table consistent if we switch back to level > 0 - later. (Using level 0 permanently is not an optimal usage of - zlib, so we don't care about this pathological case.) - */ - n = s->hash_size; - p = &s->head[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m-wsize : NIL); - } while (--n); - - n = wsize; - p = &s->prev[n]; - do { - m = *--p; - *p = (Pos)(m >= wsize ? m-wsize : NIL); - /* If n is not on any hash chain, prev[n] is garbage but - * its value will never be used. - */ - } while (--n); - more += wsize; - } - if (s->strm->avail_in == 0) break; - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - Assert(more >= 2, "more < 2"); - - n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); - s->lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s->lookahead + s->insert >= MIN_MATCH) { - uInt str = s->strstart - s->insert; - s->ins_h = s->window[str]; - s->ins_h = hash_func(s, s->ins_h, &s->window[str + 1]); - while (s->insert) { - s->ins_h = hash_func(s, s->ins_h, &s->window[str + MIN_MATCH-1]); - s->prev[str & s->w_mask] = s->head[s->ins_h]; - s->head[s->ins_h] = (Pos)str; - str++; - s->insert--; - if (s->lookahead + s->insert < MIN_MATCH) - break; - } - } - /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ - if (s->high_water < s->window_size) { - ulg curr = s->strstart + (ulg)(s->lookahead); - ulg init; - - if (s->high_water < curr) { - /* Previous high water mark below current data -- zero WIN_INIT - * bytes or up to end of window, whichever is less. - */ - init = s->window_size - curr; - if (init > WIN_INIT) - init = WIN_INIT; - zmemzero(s->window + curr, (unsigned)init); - s->high_water = curr + init; - } - else if (s->high_water < (ulg)curr + WIN_INIT) { - /* High water mark at or above current data, but below current data - * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up - * to end of window, whichever is less. - */ - init = (ulg)curr + WIN_INIT - s->high_water; - if (init > s->window_size - s->high_water) - init = s->window_size - s->high_water; - zmemzero(s->window + s->high_water, (unsigned)init); - s->high_water += init; - } - } - - Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "not enough room for search"); -} - -#if defined (__x86_64__) && defined (__linux__) && ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) || (__clang__)) - -/* =========================================================================== - * Fill the window when the lookahead becomes insufficient. - * Updates strstart and lookahead. - * - * IN assertion: lookahead < MIN_LOOKAHEAD - * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - * At least one byte has been read, or avail_in == 0; reads are - * performed for at least two bytes (required for the zip translate_eol - * option -- not supported here). - */ - -static void fill_window_sse42(deflate_state *) __attribute__ ((__target__ ("sse4.2"))); - -static void fill_window_sse42(s) - deflate_state *s; -{ - register uint32_t n; - uint32_t more; /* Amount of free space at the end of the window. */ - uint32_t wsize = s->w_size; - - Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = (unsigned)(s->window_size -(uint64_t)s->lookahead -(ulg)s->strstart); - - /* Deal with !@#$% 64K limit: */ - if (sizeof(int) <= 2) { - if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - more = wsize; - - } else if (more == (unsigned)(-1)) { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - - if (s->strstart >= wsize+MAX_DIST(s)) { - - unsigned int i; - zmemcpy(s->window, s->window+wsize, (unsigned)wsize); - s->match_start -= wsize; - s->strstart -= wsize; - s->block_start -= (int64_t) wsize; - n = s->hash_size; - __m128i W; - __m128i *q; - W = _mm_set1_epi16(wsize); - q = (__m128i*)s->head; - - for(i = 0; i < n/8; ++i) { - _mm_storeu_si128(q, _mm_subs_epu16(_mm_loadu_si128(q), W)); - q++; - } - - n = wsize; - q = (__m128i*)s->prev; - - for(i = 0; i < n/8; ++i) { - _mm_storeu_si128(q, _mm_subs_epu16(_mm_loadu_si128(q), W)); - q++; - } - more += wsize; - } - if (s->strm->avail_in == 0) break; - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - Assert(more >= 2, "more < 2"); - - n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); - s->lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s->lookahead + s->insert >= ACTUAL_MIN_MATCH) { - uint32_t str = s->strstart - s->insert; - uint32_t ins_h = s->window[str]; - while (s->insert) { - ins_h = hash_func(s, ins_h, &s->window[str]); - s->prev[str & s->w_mask] = s->head[ins_h]; - s->head[ins_h] = (Pos)str; - str++; - s->insert--; - if (s->lookahead + s->insert < ACTUAL_MIN_MATCH) - break; - } - s->ins_h = ins_h; - } - /* If the whole input has less than ACTUAL_MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ - if (s->high_water < s->window_size) { - uint64_t curr = s->strstart + (ulg)(s->lookahead); - uint64_t init; - - if (s->high_water < curr) { - /* Previous high water mark below current data -- zero WIN_INIT - * bytes or up to end of window, whichever is less. - */ - init = s->window_size - curr; - if (init > WIN_INIT) - init = WIN_INIT; - zmemzero(s->window + curr, (unsigned)init); - s->high_water = curr + init; - } - else if (s->high_water < (uint64_t)curr + WIN_INIT) { - /* High water mark at or above current data, but below current data - * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up - * to end of window, whichever is less. - */ - init = (uint64_t)curr + WIN_INIT - s->high_water; - if (init > s->window_size - s->high_water) - init = s->window_size - s->high_water; - zmemzero(s->window + s->high_water, (unsigned)init); - s->high_water += init; - } - } - - Assert((uint64_t)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "not enough room for search"); -} - -void *resolve_fill_window(void) -{ - unsigned int eax, ebx, ecx, edx; - if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx)) - return fill_window_default; - /* We need SSE4.2 ISA support */ - if (!(ecx & bit_SSE4_2)) - return fill_window_default; - return fill_window_sse42; -} - -static void fill_window(deflate_state *) __attribute__ ((ifunc ("resolve_fill_window"))); - -#elif defined (__aarch64__) - -static void fill_window_neon(s) - deflate_state *s; -{ - register uint32_t n; - uint32_t more; /* Amount of free space at the end of the window. */ - uint32_t wsize = s->w_size; - - Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); - - do { - more = (unsigned)(s->window_size -(uint64_t)s->lookahead -(ulg)s->strstart); - - /* Deal with !@#$% 64K limit: */ - if (sizeof(int) <= 2) { - if (more == 0 && s->strstart == 0 && s->lookahead == 0) { - more = wsize; - - } else if (more == (unsigned)(-1)) { - /* Very unlikely, but possible on 16 bit machine if - * strstart == 0 && lookahead == 1 (input done a byte at time) - */ - more--; - } - } - - /* If the window is almost full and there is insufficient lookahead, - * move the upper half to the lower one to make room in the upper half. - */ - - if (s->strstart >= wsize+MAX_DIST(s)) { - - unsigned int i; - zmemcpy(s->window, s->window+wsize, (unsigned)wsize); - s->match_start -= wsize; - s->strstart -= wsize; - s->block_start -= (int64_t) wsize; - n = s->hash_size; - uint16x8_t W; - uint16_t *q ; - W = vmovq_n_u16(wsize); - q = (uint16_t*)s->head; - - for(i = 0; i < n/8; ++i) { - vst1q_u16(q, vqsubq_u16(vld1q_u16(q), W)); - q+=8; - } - - n = wsize; - q = (uint16_t*)s->prev; - - for(i = 0; i < n/8; ++i) { - vst1q_u16(q, vqsubq_u16(vld1q_u16(q), W)); - q+=8; - } - more += wsize; - } - if (s->strm->avail_in == 0) break; - - /* If there was no sliding: - * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - * more == window_size - lookahead - strstart - * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - * => more >= window_size - 2*WSIZE + 2 - * In the BIG_MEM or MMAP case (not yet supported), - * window_size == input_size + MIN_LOOKAHEAD && - * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - * Otherwise, window_size == 2*WSIZE so more >= 2. - * If there was sliding, more >= WSIZE. So in all cases, more >= 2. - */ - Assert(more >= 2, "more < 2"); - - n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); - s->lookahead += n; - - /* Initialize the hash value now that we have some input: */ - if (s->lookahead + s->insert >= ACTUAL_MIN_MATCH) { - uint32_t str = s->strstart - s->insert; - uint32_t ins_h = s->window[str]; - while (s->insert) { - ins_h = hash_func(s, ins_h, &s->window[str]); - s->prev[str & s->w_mask] = s->head[ins_h]; - s->head[ins_h] = (Pos)str; - str++; - s->insert--; - if (s->lookahead + s->insert < ACTUAL_MIN_MATCH) - break; - } - s->ins_h = ins_h; - } - /* If the whole input has less than ACTUAL_MIN_MATCH bytes, ins_h is garbage, - * but this is not important since only literal bytes will be emitted. - */ - - } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); - - /* If the WIN_INIT bytes after the end of the current data have never been - * written, then zero those bytes in order to avoid memory check reports of - * the use of uninitialized (or uninitialised as Julian writes) bytes by - * the longest match routines. Update the high water mark for the next - * time through here. WIN_INIT is set to MAX_MATCH since the longest match - * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. - */ - if (s->high_water < s->window_size) { - uint64_t curr = s->strstart + (ulg)(s->lookahead); - uint64_t init; - - if (s->high_water < curr) { - /* Previous high water mark below current data -- zero WIN_INIT - * bytes or up to end of window, whichever is less. - */ - init = s->window_size - curr; - if (init > WIN_INIT) - init = WIN_INIT; - zmemzero(s->window + curr, (unsigned)init); - s->high_water = curr + init; - } - else if (s->high_water < (uint64_t)curr + WIN_INIT) { - /* High water mark at or above current data, but below current data - * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up - * to end of window, whichever is less. - */ - init = (uint64_t)curr + WIN_INIT - s->high_water; - if (init > s->window_size - s->high_water) - init = s->window_size - s->high_water; - zmemzero(s->window + s->high_water, (unsigned)init); - s->high_water += init; - } - } - - Assert((uint64_t)s->strstart <= s->window_size - MIN_LOOKAHEAD, - "not enough room for search"); -} - -void fill_window(deflate_state *s){ - return fill_window_neon(s); -} - -#else - -void fill_window(deflate_state *s){ - return fill_window_default(s); -} - -#endif - -/* =========================================================================== - * Flush the current block, with given end-of-file flag. - * IN assertion: strstart is set to the end of the current match. - */ -#define FLUSH_BLOCK_ONLY(s, last) { \ - _tr_flush_block(s, (s->block_start >= 0L ? \ - (uint8_t *)&s->window[(uint64_t)s->block_start] : \ - (uint8_t *)Z_NULL), \ - (uint64_t)((int64_t)s->strstart - s->block_start), \ - (last)); \ - s->block_start = s->strstart; \ - flush_pending(s->strm); \ - Tracev((stderr,"[FLUSH]")); \ -} - -/* Same but force premature exit if necessary. */ -#define FLUSH_BLOCK(s, last) { \ - FLUSH_BLOCK_ONLY(s, last); \ - if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ -} - -/* =========================================================================== - * Copy without compression as much as possible from the input stream, return - * the current block state. - * This function does not insert new strings in the dictionary since - * uncompressible data is probably not useful. This function is used - * only for the level=0 compression option. - * NOTE: this function should be optimized to avoid extra copying from - * window to pending_buf. - */ -static block_state deflate_stored(s, flush) - deflate_state *s; - int flush; -{ - /* Stored blocks are limited to 0xffff bytes, pending_buf is limited - * to pending_buf_size, and each stored block has a 5 byte header: - */ - uint64_t max_block_size = 0xffff; - uint64_t max_start; - - if (max_block_size > s->pending_buf_size - 5) { - max_block_size = s->pending_buf_size - 5; - } - - /* Copy as much as possible from input to output: */ - for (;;) { - /* Fill the window as much as possible: */ - if (s->lookahead <= 1) { - - Assert(s->strstart < s->w_size+MAX_DIST(s) || - s->block_start >= (int64_t)s->w_size, "slide too late"); - - fill_window(s); - if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; - - if (s->lookahead == 0) break; /* flush the current block */ - } - Assert(s->block_start >= 0L, "block gone"); - - s->strstart += s->lookahead; - s->lookahead = 0; - - /* Emit a stored block if pending_buf will be full: */ - max_start = s->block_start + max_block_size; - if (s->strstart == 0 || (uint64_t)s->strstart >= max_start) { - /* strstart == 0 is possible when wraparound on 16-bit machine */ - s->lookahead = (uint32_t)(s->strstart - max_start); - s->strstart = (uint32_t)max_start; - FLUSH_BLOCK(s, 0); - } - /* Flush if we may have to slide, otherwise block_start may become - * negative and the data will be gone: - */ - if (s->strstart - (uint32_t)s->block_start >= MAX_DIST(s)) { - FLUSH_BLOCK(s, 0); - } - } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if ((int64_t)s->strstart > s->block_start) - FLUSH_BLOCK(s, 0); - return block_done; -} - -/* =========================================================================== - * Compress as much as possible from the input stream, return the current - * block state. - * This function does not perform lazy evaluation of matches and inserts - * new strings in the dictionary only for unmatched strings or for short - * matches. It is used only for the fast compression options. - */ -static block_state deflate_fast(s, flush) - deflate_state *s; - int flush; -{ - IPos hash_head; /* head of the hash chain */ - int bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus ACTUAL_MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s->lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+2] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = NIL; - if (s->lookahead >= ACTUAL_MIN_MATCH) { - hash_head = insert_string(s, s->strstart); - } - - /* Find the longest match, discarding those <= prev_length. - * At this point we have always match_length < ACTUAL_MIN_MATCH - */ - if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s->match_length = longest_match (s, hash_head); - /* longest_match() sets match_start */ - } - if (s->match_length >= ACTUAL_MIN_MATCH) { - check_match(s, s->strstart, s->match_start, s->match_length); - - bflush = _tr_tally_dist(s, s->strstart - s->match_start, - s->match_length - MIN_MATCH); - - s->lookahead -= s->match_length; - - /* Insert new strings in the hash table only if the match length - * is not too large. This saves time but degrades compression. - */ - if (s->match_length <= s->max_insert_length && - s->lookahead >= ACTUAL_MIN_MATCH) { - s->match_length--; /* string at strstart already in table */ - do { - s->strstart++; - hash_head = insert_string(s, s->strstart); - /* strstart never exceeds WSIZE-MAX_MATCH, so there are - * always ACTUAL_MIN_MATCH bytes ahead. - */ - } while (--s->match_length != 0); - s->strstart++; - } else { - s->strstart += s->match_length; - s->match_length = 0; - /* If lookahead < ACTUAL_MIN_MATCH, ins_h is garbage, but it does not - * matter since it will be recomputed at next deflate call. - */ - } - } else { - /* No match, output a literal byte */ - Tracevv((stderr,"%c", s->window[s->strstart])); - bflush = _tr_tally_lit (s, s->window[s->strstart]); - s->lookahead--; - s->strstart++; - } - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = s->strstart < ACTUAL_MIN_MATCH-1 ? s->strstart : ACTUAL_MIN_MATCH-1; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->last_lit) - FLUSH_BLOCK(s, 0); - return block_done; -} - -/* =========================================================================== - * Same as above, but achieves better compression. We use a lazy - * evaluation for matches: a match is finally adopted only if there is - * no better match at the next window position. - */ -static block_state deflate_slow(s, flush) - deflate_state *s; - int flush; -{ - IPos hash_head; /* head of hash chain */ - int bflush; /* set if current block must be flushed */ - - /* Process the input block. */ - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the next match, plus ACTUAL_MIN_MATCH bytes to insert the - * string following the next match. - */ - if (s->lookahead < MIN_LOOKAHEAD) { - fill_window(s); - if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* Insert the string window[strstart .. strstart+3] in the - * dictionary, and set hash_head to the head of the hash chain: - */ - hash_head = NIL; - if (s->lookahead >= ACTUAL_MIN_MATCH) { - hash_head = insert_string(s, s->strstart); - } - - /* Find the longest match, discarding those <= prev_length. - */ - s->prev_length = s->match_length, s->prev_match = s->match_start; - s->match_length = ACTUAL_MIN_MATCH-1; - - if (hash_head != NIL && s->prev_length < s->max_lazy_match && - s->strstart - hash_head <= MAX_DIST(s)) { - /* To simplify the code, we prevent matches with the string - * of window index 0 (in particular we have to avoid a match - * of the string with itself at the start of the input file). - */ - s->match_length = longest_match (s, hash_head); - /* longest_match() sets match_start */ - - if (s->match_length <= 5 && (s->strategy == Z_FILTERED )) { - - /* If prev_match is also ACTUAL_MIN_MATCH, match_start is garbage - * but we will ignore the current match anyway. - */ - s->match_length = ACTUAL_MIN_MATCH-1; - } - } - /* If there was a match at the previous step and the current - * match is not better, output the previous match: - */ - if (s->prev_length >= ACTUAL_MIN_MATCH && s->match_length <= s->prev_length) { - uint32_t mov_fwd ; - uint32_t insert_cnt ; - - uint32_t max_insert = s->strstart + s->lookahead - ACTUAL_MIN_MATCH; - /* Do not insert strings in hash table beyond this. */ - - check_match(s, s->strstart-1, s->prev_match, s->prev_length); - - bflush = _tr_tally_dist(s, s->strstart -1 - s->prev_match, - s->prev_length - MIN_MATCH); - - /* Insert in hash table all strings up to the end of the match. - * strstart-1 and strstart are already inserted. If there is not - * enough lookahead, the last two strings are not inserted in - * the hash table. - */ - s->lookahead -= s->prev_length-1; - - mov_fwd = s->prev_length - 2; - insert_cnt = mov_fwd; - if (unlikely(insert_cnt > max_insert - s->strstart)) - insert_cnt = max_insert - s->strstart; - - bulk_insert_str(s, s->strstart + 1, insert_cnt); - s->prev_length = 0; - s->match_available = 0; - s->match_length = ACTUAL_MIN_MATCH-1; - s->strstart += mov_fwd + 1; - - if (bflush) FLUSH_BLOCK(s, 0); - - } else if (s->match_available) { - /* If there was no match at the previous position, output a - * single literal. If there was a match but the current match - * is longer, truncate the previous match to a single literal. - */ - Tracevv((stderr,"%c", s->window[s->strstart-1])); - bflush = _tr_tally_lit(s, s->window[s->strstart-1]); - if (bflush) { - FLUSH_BLOCK_ONLY(s, 0); - } - s->strstart++; - s->lookahead--; - if (s->strm->avail_out == 0) return need_more; - } else { - /* There is no previous match to compare with, wait for - * the next step to decide. - */ - s->match_available = 1; - s->strstart++; - s->lookahead--; - } - } - Assert (flush != Z_NO_FLUSH, "no flush?"); - if (s->match_available) { - Tracevv((stderr,"%c", s->window[s->strstart-1])); - bflush = _tr_tally_lit(s, s->window[s->strstart-1]); - s->match_available = 0; - } - s->insert = s->strstart < ACTUAL_MIN_MATCH-1 ? s->strstart : ACTUAL_MIN_MATCH-1; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->last_lit) - FLUSH_BLOCK(s, 0); - return block_done; -} - -/* =========================================================================== - * For Z_RLE, simply look for runs of bytes, generate matches only of distance - * one. Do not maintain a hash table. (It will be regenerated if this run of - * deflate switches away from Z_RLE.) - */ -static block_state deflate_rle(s, flush) - deflate_state *s; - int flush; -{ - int bflush; /* set if current block must be flushed */ - uint32_t prev; /* byte at distance one to match */ - uint8_t *scan, *strend; /* scan goes up to strend for length of run */ - - for (;;) { - /* Make sure that we always have enough lookahead, except - * at the end of the input file. We need MAX_MATCH bytes - * for the longest run, plus one for the unrolled loop. - */ - if (s->lookahead <= MAX_MATCH) { - fill_window(s); - if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { - return need_more; - } - if (s->lookahead == 0) break; /* flush the current block */ - } - - /* See how many times the previous byte repeats */ - s->match_length = 0; - if (s->lookahead >= ACTUAL_MIN_MATCH && s->strstart > 0) { - scan = s->window + s->strstart - 1; - prev = *scan; - if (prev == *++scan && prev == *++scan && prev == *++scan) { - strend = s->window + s->strstart + MAX_MATCH; - do { - } while (prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - prev == *++scan && prev == *++scan && - scan < strend); - s->match_length = MAX_MATCH - (int)(strend - scan); - if (s->match_length > s->lookahead) - s->match_length = s->lookahead; - } - Assert(scan <= s->window+(uint32_t)(s->window_size-1), "wild scan"); - } - - /* Emit match if have run of ACTUAL_MIN_MATCH or longer, else emit literal */ - if (s->match_length >= ACTUAL_MIN_MATCH) { - check_match(s, s->strstart, s->strstart - 1, s->match_length); - - bflush = _tr_tally_dist(s, 1, s->match_length - MIN_MATCH); - - s->lookahead -= s->match_length; - s->strstart += s->match_length; - s->match_length = 0; - } else { - /* No match, output a literal byte */ - Tracevv((stderr,"%c", s->window[s->strstart])); - bflush = _tr_tally_lit (s, s->window[s->strstart]); - s->lookahead--; - s->strstart++; - } - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->last_lit) - FLUSH_BLOCK(s, 0); - return block_done; -} - -/* =========================================================================== - * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. - * (It will be regenerated if this run of deflate switches away from Huffman.) - */ -static block_state deflate_huff(s, flush) - deflate_state *s; - int flush; -{ - int bflush; /* set if current block must be flushed */ - - for (;;) { - /* Make sure that we have a literal to write. */ - if (s->lookahead == 0) { - fill_window(s); - if (s->lookahead == 0) { - if (flush == Z_NO_FLUSH) - return need_more; - break; /* flush the current block */ - } - } - - /* Output a literal byte */ - s->match_length = 0; - Tracevv((stderr,"%c", s->window[s->strstart])); - bflush = _tr_tally_lit (s, s->window[s->strstart]); - s->lookahead--; - s->strstart++; - if (bflush) FLUSH_BLOCK(s, 0); - } - s->insert = 0; - if (flush == Z_FINISH) { - FLUSH_BLOCK(s, 1); - return finish_done; - } - if (s->last_lit) - FLUSH_BLOCK(s, 0); - return block_done; -} diff --git a/builtins/zlib/deflate_cf.h b/builtins/zlib/deflate_cf.h deleted file mode 100644 index 696792b3cd660..0000000000000 --- a/builtins/zlib/deflate_cf.h +++ /dev/null @@ -1,333 +0,0 @@ -/* deflate.h -- internal compression state - * Copyright (C) 1995-2012 Jean-loup Gailly - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* @(#) $Id$ */ - -#ifndef DEFLATE_H -#define DEFLATE_H - -#include "zutil.h" - -/* =========================================================================== - * Internal compression state. - */ - -#define LENGTH_CODES 29 -/* number of length codes, not counting the special END_BLOCK code */ - -#define LITERALS 256 -/* number of literal bytes 0..255 */ - -#define L_CODES (LITERALS+1+LENGTH_CODES) -/* number of Literal or Length codes, including the END_BLOCK code */ - -#define D_CODES 30 -/* number of distance codes */ - -#define BL_CODES 19 -/* number of codes used to transfer the bit lengths */ - -#define HEAP_SIZE (2*L_CODES+1) -/* maximum heap size */ - -#define MAX_BITS 15 -/* All codes must not exceed MAX_BITS bits */ - -#define Buf_size 64 -/* size of bit buffer in bi_buf */ - -#define INIT_STATE 42 -#define EXTRA_STATE 69 -#define NAME_STATE 73 -#define COMMENT_STATE 91 -#define HCRC_STATE 103 -#define BUSY_STATE 113 -#define FINISH_STATE 666 -/* Stream status */ - - -/* Data structure describing a single value and its code string. */ -typedef struct ct_data_s { - union { - uint16_t freq; /* frequency count */ - uint16_t code; /* bit string */ - } fc; - union { - uint16_t dad; /* father node in Huffman tree */ - uint16_t len; /* length of bit string */ - } dl; -} ct_data; - -#define Freq fc.freq -#define Code fc.code -#define Dad dl.dad -#define Len dl.len - -typedef struct static_tree_desc_s static_tree_desc; - -typedef struct tree_desc_s { - ct_data *dyn_tree; /* the dynamic tree */ - int max_code; /* largest code with non zero frequency */ - static_tree_desc *stat_desc; /* the corresponding static tree */ -} tree_desc; - -typedef uint16_t Pos; -typedef uint32_t IPos; - -/* A Pos is an index in the character window. We use short instead of int to - * save space in the various tables. IPos is used only for parameter passing. - */ - -typedef struct internal_state { - z_streamp strm; /* pointer back to this zlib stream */ - int status; /* as the name implies */ - uint8_t *pending_buf; /* output still pending */ - uint64_t pending_buf_size; /* size of pending_buf */ - uint8_t *pending_out; /* next pending byte to output to the stream */ - uint32_t pending; /* nb of bytes in the pending buffer */ - int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ - gz_headerp gzhead; /* gzip header information to write */ - uint32_t gzindex; /* where in extra, name, or comment */ - uint8_t method; /* can only be DEFLATED */ - int last_flush; /* value of flush param for previous deflate call */ - - /* used by deflate.c: */ - uint32_t w_size; /* LZ77 window size (32K by default) */ - uint32_t w_bits; /* log2(w_size) (8..16) */ - uint32_t w_mask; /* w_size - 1 */ - - uint8_t *window; - /* Sliding window. Input bytes are read into the second half of the window, - * and move to the first half later to keep a dictionary of at least wSize - * bytes. With this organization, matches are limited to a distance of - * wSize-MAX_MATCH bytes, but this ensures that IO is always - * performed with a length multiple of the block size. Also, it limits - * the window size to 64K, which is quite useful on MSDOS. - * To do: use the user input buffer as sliding window. - */ - - uint32_t window_size; - /* Actual size of window: 2*wSize, except when the user input buffer - * is directly used as sliding window. - */ - - Pos *prev; - /* Link to older string with same hash index. To limit the size of this - * array to 64K, this link is maintained only for the last 32K strings. - * An index in this array is thus a window index modulo 32K. - */ - - Pos *head; /* Heads of the hash chains or NIL. */ - - uint32_t ins_h; /* hash index of string to be inserted */ - uint32_t hash_size; /* number of elements in hash table */ - uint32_t hash_bits; /* log2(hash_size) */ - uint32_t hash_mask; /* hash_size-1 */ - - uint32_t hash_shift; - /* Number of bits by which ins_h must be shifted at each input - * step. It must be such that after MIN_MATCH steps, the oldest - * byte no longer takes part in the hash key, that is: - * hash_shift * MIN_MATCH >= hash_bits - */ - - long block_start; - /* Window position at the beginning of the current output block. Gets - * negative when the window is moved backwards. - */ - - uint32_t match_length; /* length of best match */ - IPos prev_match; /* previous match */ - int match_available; /* set if previous match exists */ - uint32_t strstart; /* start of string to insert */ - uint32_t match_start; /* start of matching string */ - uint32_t lookahead; /* number of valid bytes ahead in window */ - - uint32_t prev_length; - /* Length of the best match at previous step. Matches not greater than this - * are discarded. This is used in the lazy match evaluation. - */ - - uint32_t max_chain_length; - /* To speed up deflation, hash chains are never searched beyond this - * length. A higher limit improves compression ratio but degrades the - * speed. - */ - - uint32_t max_lazy_match; - /* Attempt to find a better match only when the current match is strictly - * smaller than this value. This mechanism is used only for compression - * levels >= 4. - */ -# define max_insert_length max_lazy_match - /* Insert new strings in the hash table only if the match length is not - * greater than this length. This saves time but degrades compression. - * max_insert_length is used only for compression levels <= 3. - */ - - int level; /* compression level (1..9) */ - int strategy; /* favor or force Huffman coding*/ - - uint32_t good_match; - /* Use a faster search when the previous match is longer than this */ - - int nice_match; /* Stop searching when current match exceeds this */ - - /* used by trees.c: */ - /* Didn't use ct_data typedef below to suppress compiler warning */ - struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ - struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ - struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ - - struct tree_desc_s l_desc; /* desc. for literal tree */ - struct tree_desc_s d_desc; /* desc. for distance tree */ - struct tree_desc_s bl_desc; /* desc. for bit length tree */ - - uint16_t bl_count[MAX_BITS+1]; - /* number of codes at each bit length for an optimal tree */ - - int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ - int heap_len; /* number of elements in the heap */ - int heap_max; /* element of largest frequency */ - /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - * The same heap array is used to build all trees. - */ - - uint8_t depth[2*L_CODES+1]; - /* Depth of each subtree used as tie breaker for trees of equal frequency - */ - - uint8_t *l_buf; /* buffer for literals or lengths */ - - uint32_t lit_bufsize; - /* Size of match buffer for literals/lengths. There are 4 reasons for - * limiting lit_bufsize to 64K: - * - frequencies can be kept in 16 bit counters - * - if compression is not successful for the first block, all input - * data is still in the window so we can still emit a stored block even - * when input comes from standard input. (This can also be done for - * all blocks if lit_bufsize is not greater than 32K.) - * - if compression is not successful for a file smaller than 64K, we can - * even emit a stored file instead of a stored block (saving 5 bytes). - * This is applicable only for zip (not gzip or zlib). - * - creating new Huffman trees less frequently may not provide fast - * adaptation to changes in the input data statistics. (Take for - * example a binary file with poorly compressible code followed by - * a highly compressible string table.) Smaller buffer sizes give - * fast adaptation but have of course the overhead of transmitting - * trees more frequently. - * - I can't count above 4 - */ - - uint32_t last_lit; /* running index in l_buf */ - - uint16_t *d_buf; - /* Buffer for distances. To simplify the code, d_buf and l_buf have - * the same number of elements. To use different lengths, an extra flag - * array would be necessary. - */ - - uint64_t opt_len; /* bit length of current block with optimal trees */ - uint64_t static_len; /* bit length of current block with static trees */ - uint32_t matches; /* number of string matches in current block */ - uint32_t insert; /* bytes at end of window left to insert */ - -#ifdef DEBUG - uint64_t compressed_len; /* total bit length of compressed file mod 2^32 */ - uint64_t bits_sent; /* bit length of compressed data sent mod 2^32 */ -#endif - - uint64_t bi_buf; - /* Output buffer. bits are inserted starting at the bottom (least - * significant bits). - */ - int bi_valid; - /* Number of valid bits in bi_buf. All bits above the last valid bit - * are always zero. - */ - - uint64_t high_water; - /* High water mark offset in window for initialized bytes -- bytes above - * this are set to zero in order to avoid memory check warnings when - * longest match routines access bytes past the input. This is then - * updated to the new high water mark. - */ - -} deflate_state; - -/* Output a byte on the stream. - * IN assertion: there is enough room in pending_buf. - */ -#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} - -/* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ - -#define put_short(s, w) { \ - s->pending += 2; \ - *(ush*)(&s->pending_buf[s->pending - 2]) = (w) ; \ -} - -#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) -/* Minimum amount of lookahead, except at the end of the input file. - * See deflate.c for comments about the MIN_MATCH+1. - */ - -#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) -/* In order to simplify the code, particularly on 16 bit machines, match - * distances are limited to MAX_DIST instead of WSIZE. - */ - -#define WIN_INIT MAX_MATCH -/* Number of bytes after end of data in window to initialize in order to avoid - memory checker errors from longest match routines */ - - /* in trees.c */ -void ZLIB_INTERNAL _tr_init(deflate_state *s); -int ZLIB_INTERNAL _tr_tally(deflate_state *s, uint32_t dist, unsigned lc); -void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, uint8_t *buf, - uint64_t stored_len, int last); -void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s); -void ZLIB_INTERNAL _tr_align(deflate_state *s); -void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, uint8_t *buf, - uint64_t stored_len, int last); - -#define d_code(dist) \ - ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) -/* Mapping from a distance to a distance code. dist is the distance - 1 and - * must not have side effects. _dist_code[256] and _dist_code[257] are never - * used. - */ - -extern const uint8_t ZLIB_INTERNAL _length_code[]; -extern const uint8_t ZLIB_INTERNAL _dist_code[]; - -#ifdef _MSC_VER - -/* MSC doesn't have __builtin_expect. Just ignore likely/unlikely and - hope the compiler optimizes for the best. -*/ -#define likely(x) (x) -#define unlikely(x) (x) - -int __inline __builtin_ctzl(unsigned long mask) -{ - unsigned long index ; - - return _BitScanForward(&index, mask) == 0 ? 32 : ((int)index) ; -} -#else -#define likely(x) __builtin_expect((x),1) -#define unlikely(x) __builtin_expect((x),0) -#endif - -#endif /* DEFLATE_H */ diff --git a/builtins/zlib/gzclose.c b/builtins/zlib/gzclose.c deleted file mode 100644 index 023d2a650135b..0000000000000 --- a/builtins/zlib/gzclose.c +++ /dev/null @@ -1,24 +0,0 @@ -/* gzclose.c -- zlib gzclose() function - * Copyright (C) 2004, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "gzguts.h" - -/* gzclose() is in a separate file so that it is linked in only if it is used. - That way the other gzclose functions can be used instead to avoid linking in - unneeded compression or decompression routines. */ -int ZEXPORT gzclose(gzFile file) -{ -#ifndef NO_GZCOMPRESS - gz_statep state; - - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); -#else - return gzclose_r(file); -#endif -} diff --git a/builtins/zlib/gzguts.h b/builtins/zlib/gzguts.h deleted file mode 100644 index d87659d0319fa..0000000000000 --- a/builtins/zlib/gzguts.h +++ /dev/null @@ -1,209 +0,0 @@ -/* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#ifdef _LARGEFILE64_SOURCE -# ifndef _LARGEFILE_SOURCE -# define _LARGEFILE_SOURCE 1 -# endif -# ifdef _FILE_OFFSET_BITS -# undef _FILE_OFFSET_BITS -# endif -#endif - -#ifdef HAVE_HIDDEN -# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) -#else -# define ZLIB_INTERNAL -#endif - -#include -#include "zlib.h" -#ifdef STDC -# include -# include -# include -#endif -#include - -#ifdef _WIN32 -# include -#endif - -#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) -# include -#endif - -#ifdef WINAPI_FAMILY -# define open _open -# define read _read -# define write _write -# define close _close -#endif - -#ifdef NO_DEFLATE /* for compatibility with old definition */ -# define NO_GZCOMPRESS -#endif - -#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif - -#if defined(__CYGWIN__) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif - -#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif - -#ifndef HAVE_VSNPRINTF -# ifdef MSDOS -/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), - but for now we just assume it doesn't. */ -# define NO_vsnprintf -# endif -# ifdef __TURBOC__ -# define NO_vsnprintf -# endif -# ifdef WIN32 -/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ -# if !defined(vsnprintf) && !defined(NO_vsnprintf) -# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) -# define vsnprintf _vsnprintf -# endif -# endif -# endif -# ifdef __SASC -# define NO_vsnprintf -# endif -# ifdef VMS -# define NO_vsnprintf -# endif -# ifdef __OS400__ -# define NO_vsnprintf -# endif -# ifdef __MVS__ -# define NO_vsnprintf -# endif -#endif - -/* unlike snprintf (which is required in C99, yet still not supported by - Microsoft more than a decade later!), _snprintf does not guarantee null - termination of the result -- however this is only used in gzlib.c where - the result is assured to fit in the space provided */ -#ifdef _MSC_VER -# define snprintf _snprintf -#endif - -#ifndef local -# define local static -#endif -/* compile with -Dlocal if your debugger can't find static symbols */ - -/* gz* functions always use library allocation functions */ -#ifndef STDC - extern voidp malloc OF((uInt size)); - extern void free OF((voidpf ptr)); -#endif - -/* get errno and strerror definition */ -#if defined UNDER_CE -# include -# define zstrerror() gz_strwinerror((DWORD)GetLastError()) -#else -# ifndef NO_STRERROR -# include -# define zstrerror() strerror(errno) -# else -# define zstrerror() "stdio error (consult errno)" -# endif -#endif - -/* provide prototypes for these when building zlib without LFS */ -#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); - ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); -#endif - -/* default memLevel */ -#if MAX_MEM_LEVEL >= 8 -# define DEF_MEM_LEVEL 8 -#else -# define DEF_MEM_LEVEL MAX_MEM_LEVEL -#endif - -/* default i/o buffer size -- double this for output when reading (this and - twice this must be able to fit in an unsigned type) */ -#define GZBUFSIZE 8192 - -/* gzip modes, also provide a little integrity check on the passed structure */ -#define GZ_NONE 0 -#define GZ_READ 7247 -#define GZ_WRITE 31153 -#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ - -/* values for gz_state how */ -#define LOOK 0 /* look for a gzip header */ -#define COPY 1 /* copy input directly */ -#define GZIP 2 /* decompress a gzip stream */ - -/* internal gzip file state data structure */ -typedef struct { - /* exposed contents for gzgetc() macro */ - struct gzFile_s x; /* "x" for exposed */ - /* x.have: number of bytes available at x.next */ - /* x.next: next output data to deliver or write */ - /* x.pos: current position in uncompressed data */ - /* used for both reading and writing */ - int mode; /* see gzip modes above */ - int fd; /* file descriptor */ - char *path; /* path or fd for error messages */ - unsigned size; /* buffer size, zero if not allocated yet */ - unsigned want; /* requested buffer size, default is GZBUFSIZE */ - unsigned char *in; /* input buffer */ - unsigned char *out; /* output buffer (double-sized when reading) */ - int direct; /* 0 if processing gzip, 1 if transparent */ - /* just for reading */ - int how; /* 0: get header, 1: copy, 2: decompress */ - z_off64_t start; /* where the gzip data started, for rewinding */ - int eof; /* true if end of input file reached */ - int past; /* true if read requested past end */ - /* just for writing */ - int level; /* compression level */ - int strategy; /* compression strategy */ - /* seek request */ - z_off64_t skip; /* amount to skip (already rewound if backwards) */ - int seek; /* true if seek request pending */ - /* error information */ - int err; /* error code */ - char *msg; /* error message */ - /* zlib inflate or deflate stream */ - z_stream strm; /* stream structure in-place (not a pointer) */ -} gz_state; -typedef gz_state FAR *gz_statep; - -/* shared functions */ -void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); -#if defined UNDER_CE -char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); -#endif - -/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t - value -- needed when comparing unsigned to z_off64_t, which is signed - (possible z_off64_t types off_t, off64_t, and long are all signed) */ -#ifdef INT_MAX -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) -#else -unsigned ZLIB_INTERNAL gz_intmax OF((void)); -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) -#endif diff --git a/builtins/zlib/gzlib.c b/builtins/zlib/gzlib.c deleted file mode 100644 index 71740b60f56fa..0000000000000 --- a/builtins/zlib/gzlib.c +++ /dev/null @@ -1,605 +0,0 @@ -/* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004, 2010, 2011, 2012, 2013 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#ifndef _WIN32 -#include -#endif - -#include "gzguts.h" - -#if defined(_WIN32) && !defined(__BORLANDC__) -# define LSEEK _lseeki64 -#else -#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 -# define LSEEK lseek64 -#else -# define LSEEK lseek -#endif -#endif - -/* Local functions */ -local void gz_reset OF((gz_statep)); -local gzFile gz_open OF((const void *, int, const char *)); - -#if defined UNDER_CE - -/* Map the Windows error number in ERROR to a locale-dependent error message - string and return a pointer to it. Typically, the values for ERROR come - from GetLastError. - - The string pointed to shall not be modified by the application, but may be - overwritten by a subsequent call to gz_strwinerror - - The gz_strwinerror function does not change the current setting of - GetLastError. */ -char ZLIB_INTERNAL *gz_strwinerror(DWORD error) -{ - static char buf[1024]; - - wchar_t *msgbuf; - DWORD lasterr = GetLastError(); - DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_ALLOCATE_BUFFER, - NULL, - error, - 0, /* Default language */ - (LPVOID)&msgbuf, - 0, - NULL); - if (chars != 0) { - /* If there is an \r\n appended, zap it. */ - if (chars >= 2 - && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { - chars -= 2; - msgbuf[chars] = 0; - } - - if (chars > sizeof (buf) - 1) { - chars = sizeof (buf) - 1; - msgbuf[chars] = 0; - } - - wcstombs(buf, msgbuf, chars + 1); - LocalFree(msgbuf); - } - else { - sprintf(buf, "unknown win32 error (%ld)", error); - } - - SetLastError(lasterr); - return buf; -} - -#endif /* UNDER_CE */ - -/* Reset gzip file state */ -local void gz_reset(gz_statep state) -{ - state->x.have = 0; /* no output data available */ - if (state->mode == GZ_READ) { /* for reading ... */ - state->eof = 0; /* not at end of file */ - state->past = 0; /* have not read past end yet */ - state->how = LOOK; /* look for gzip header */ - } - state->seek = 0; /* no seek request pending */ - gz_error(state, Z_OK, NULL); /* clear error */ - state->x.pos = 0; /* no uncompressed data yet */ - state->strm.avail_in = 0; /* no input data yet */ -} - -/* Open a gzip file either by name or file descriptor. */ -local gzFile gz_open(const void *path, int fd, const char *mode) -{ - gz_statep state; - size_t len; - int oflag; -#ifdef O_CLOEXEC - int cloexec = 0; -#endif -#ifdef O_EXCL - int exclusive = 0; -#endif - - /* check input */ - if (path == NULL) - return NULL; - - /* allocate gzFile structure to return */ - state = (gz_statep)malloc(sizeof(gz_state)); - if (state == NULL) - return NULL; - state->size = 0; /* no buffers allocated yet */ - state->want = GZBUFSIZE; /* requested buffer size */ - state->msg = NULL; /* no error message yet */ - - /* interpret mode */ - state->mode = GZ_NONE; - state->level = Z_DEFAULT_COMPRESSION; - state->strategy = Z_DEFAULT_STRATEGY; - state->direct = 0; - while (*mode) { - if (*mode >= '0' && *mode <= '9') - state->level = *mode - '0'; - else - switch (*mode) { - case 'r': - state->mode = GZ_READ; - break; -#ifndef NO_GZCOMPRESS - case 'w': - state->mode = GZ_WRITE; - break; - case 'a': - state->mode = GZ_APPEND; - break; -#endif - case '+': /* can't read and write at the same time */ - free(state); - return NULL; - case 'b': /* ignore -- will request binary anyway */ - break; -#ifdef O_CLOEXEC - case 'e': - cloexec = 1; - break; -#endif -#ifdef O_EXCL - case 'x': - exclusive = 1; - break; -#endif - case 'f': - state->strategy = Z_FILTERED; - break; - case 'h': - state->strategy = Z_HUFFMAN_ONLY; - break; - case 'R': - state->strategy = Z_RLE; - break; - case 'F': - state->strategy = Z_FIXED; - break; - case 'T': - state->direct = 1; - break; - default: /* could consider as an error, but just ignore */ - ; - } - mode++; - } - - /* must provide an "r", "w", or "a" */ - if (state->mode == GZ_NONE) { - free(state); - return NULL; - } - - /* can't force transparent read */ - if (state->mode == GZ_READ) { - if (state->direct) { - free(state); - return NULL; - } - state->direct = 1; /* for empty file */ - } - - /* save the path name for error messages */ -#ifdef _WIN32 - if (fd == -2) { - len = wcstombs(NULL, path, 0); - if (len == (size_t)-1) - len = 0; - } - else -#endif - len = strlen((const char *)path); - state->path = (char *)malloc(len + 1); - if (state->path == NULL) { - free(state); - return NULL; - } -#ifdef _WIN32 - if (fd == -2) - if (len) - wcstombs(state->path, path, len + 1); - else - *(state->path) = 0; - else -#endif -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(state->path, len + 1, "%s", (const char *)path); -#else - strcpy(state->path, path); -#endif - - /* compute the flags for open() */ - oflag = -#ifdef O_LARGEFILE - O_LARGEFILE | -#endif -#ifdef O_BINARY - O_BINARY | -#endif -#ifdef O_CLOEXEC - (cloexec ? O_CLOEXEC : 0) | -#endif - (state->mode == GZ_READ ? - O_RDONLY : - (O_WRONLY | O_CREAT | -#ifdef O_EXCL - (exclusive ? O_EXCL : 0) | -#endif - (state->mode == GZ_WRITE ? - O_TRUNC : - O_APPEND))); - - /* open the file with the appropriate flags (or just use fd) */ - state->fd = fd > -1 ? fd : ( -#ifdef _WIN32 - fd == -2 ? _wopen(path, oflag, 0666) : -#endif - open((const char *)path, oflag, 0666)); - if (state->fd == -1) { - free(state->path); - free(state); - return NULL; - } - if (state->mode == GZ_APPEND) - state->mode = GZ_WRITE; /* simplify later checks */ - - /* save the current position for rewinding (only if reading) */ - if (state->mode == GZ_READ) { - state->start = LSEEK(state->fd, 0, SEEK_CUR); - if (state->start == -1) state->start = 0; - } - - /* initialize stream */ - gz_reset(state); - - /* return stream */ - return (gzFile)state; -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzopen(const char *path, const char *mode) -{ - return gz_open(path, -1, mode); -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzopen64(const char *path, const char *mode) -{ - return gz_open(path, -1, mode); -} - -/* -- see zlib.h -- */ -gzFile ZEXPORT gzdopen(int fd, const char *mode) -{ - char *path; /* identifier for error messages */ - gzFile gz; - - if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) - return NULL; -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(path, 7 + 3 * sizeof(int), "", fd); /* for debugging */ -#else - sprintf(path, "", fd); /* for debugging */ -#endif - gz = gz_open(path, fd, mode); - free(path); - return gz; -} - -/* -- see zlib.h -- */ -#ifdef _WIN32 -gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode) -{ - return gz_open(path, -2, mode); -} -#endif - -/* -- see zlib.h -- */ -int ZEXPORT gzbuffer(gzFile file, unsigned size) -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* make sure we haven't already allocated memory */ - if (state->size != 0) - return -1; - - /* check and set requested size */ - if (size < 2) - size = 2; /* need two bytes to check magic header */ - state->want = size; - return 0; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzrewind(gzFile file) -{ - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* back up and start over */ - if (LSEEK(state->fd, state->start, SEEK_SET) == -1) - return -1; - gz_reset(state); - return 0; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) -{ - unsigned n; - z_off64_t ret; - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* check that there's no error */ - if (state->err != Z_OK && state->err != Z_BUF_ERROR) - return -1; - - /* can only seek from start or relative to current position */ - if (whence != SEEK_SET && whence != SEEK_CUR) - return -1; - - /* normalize offset to a SEEK_CUR specification */ - if (whence == SEEK_SET) - offset -= state->x.pos; - else if (state->seek) - offset += state->skip; - state->seek = 0; - - /* if within raw area while reading, just go there */ - if (state->mode == GZ_READ && state->how == COPY && - state->x.pos + offset >= 0) { - ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); - if (ret == -1) - return -1; - state->x.have = 0; - state->eof = 0; - state->past = 0; - state->seek = 0; - gz_error(state, Z_OK, NULL); - state->strm.avail_in = 0; - state->x.pos += offset; - return state->x.pos; - } - - /* calculate skip amount, rewinding if needed for back seek when reading */ - if (offset < 0) { - if (state->mode != GZ_READ) /* writing -- can't go backwards */ - return -1; - offset += state->x.pos; - if (offset < 0) /* before start of file! */ - return -1; - if (gzrewind(file) == -1) /* rewind, then skip to offset */ - return -1; - } - - /* if reading, skip what's in output buffer (one less gzgetc() check) */ - if (state->mode == GZ_READ) { - n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? - (unsigned)offset : state->x.have; - state->x.have -= n; - state->x.next += n; - state->x.pos += n; - offset -= n; - } - - /* request skip (if not zero) */ - if (offset) { - state->seek = 1; - state->skip = offset; - } - return state->x.pos + offset; -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence) -{ - z_off64_t ret; - - ret = gzseek64(file, (z_off64_t)offset, whence); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gztell64(gzFile file) -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* return position */ - return state->x.pos + (state->seek ? state->skip : 0); -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gztell(gzFile file) -{ - z_off64_t ret; - - ret = gztell64(file); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -z_off64_t ZEXPORT gzoffset64(gzFile file) -{ - z_off64_t offset; - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return -1; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return -1; - - /* compute and return effective offset in file */ - offset = LSEEK(state->fd, 0, SEEK_CUR); - if (offset == -1) - return -1; - if (state->mode == GZ_READ) /* reading */ - offset -= state->strm.avail_in; /* don't count buffered input */ - return offset; -} - -/* -- see zlib.h -- */ -z_off_t ZEXPORT gzoffset(gzFile file) -{ - z_off64_t ret; - - ret = gzoffset64(file); - return ret == (z_off_t)ret ? (z_off_t)ret : -1; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzeof(gzFile file) -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return 0; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return 0; - - /* return end-of-file state */ - return state->mode == GZ_READ ? state->past : 0; -} - -/* -- see zlib.h -- */ -const char * ZEXPORT gzerror(gzFile file, int *errnum) -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return NULL; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return NULL; - - /* return error information */ - if (errnum != NULL) - *errnum = state->err; - return state->err == Z_MEM_ERROR ? "out of memory" : - (state->msg == NULL ? "" : state->msg); -} - -/* -- see zlib.h -- */ -void ZEXPORT gzclearerr(gzFile file) -{ - gz_statep state; - - /* get internal structure and check integrity */ - if (file == NULL) - return; - state = (gz_statep)file; - if (state->mode != GZ_READ && state->mode != GZ_WRITE) - return; - - /* clear error and end-of-file */ - if (state->mode == GZ_READ) { - state->eof = 0; - state->past = 0; - } - gz_error(state, Z_OK, NULL); -} - -/* Create an error message in allocated memory and set state->err and - state->msg accordingly. Free any previous error message already there. Do - not try to free or allocate space if the error is Z_MEM_ERROR (out of - memory). Simply save the error message as a static string. If there is an - allocation failure constructing the error message, then convert the error to - out of memory. */ -void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) -{ - /* free previously allocated message and clear */ - if (state->msg != NULL) { - if (state->err != Z_MEM_ERROR) - free(state->msg); - state->msg = NULL; - } - - /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ - if (err != Z_OK && err != Z_BUF_ERROR) - state->x.have = 0; - - /* set error code, and if no message, then done */ - state->err = err; - if (msg == NULL) - return; - - /* for an out of memory error, return literal string when requested */ - if (err == Z_MEM_ERROR) - return; - - /* construct error message with path */ - if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == - NULL) { - state->err = Z_MEM_ERROR; - return; - } -#if !defined(NO_snprintf) && !defined(NO_vsnprintf) - snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, - "%s%s%s", state->path, ": ", msg); -#else - strcpy(state->msg, state->path); - strcat(state->msg, ": "); - strcat(state->msg, msg); -#endif - return; -} - -#ifndef INT_MAX -/* portably return maximum value for an int (when limits.h presumed not - available) -- we need to do this to cover cases where 2's complement not - used, since C standard permits 1's complement and sign-bit representations, - otherwise we could just use ((unsigned)-1) >> 1 */ -unsigned ZLIB_INTERNAL gz_intmax() -{ - unsigned p, q; - - p = 1; - do { - q = p; - p <<= 1; - p++; - } while (p > q); - return q >> 1; -} -#endif diff --git a/builtins/zlib/gzread.c b/builtins/zlib/gzread.c deleted file mode 100644 index 0fffaea6b2f88..0000000000000 --- a/builtins/zlib/gzread.c +++ /dev/null @@ -1,577 +0,0 @@ -/* gzread.c -- zlib functions for reading gzip files - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#ifndef _WIN32 -#include -#include -#endif - -#include "gzguts.h" - -/* Local functions */ -local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); -local int gz_avail OF((gz_statep)); -local int gz_look OF((gz_statep)); -local int gz_decomp OF((gz_statep)); -local int gz_fetch OF((gz_statep)); -local int gz_skip OF((gz_statep, z_off64_t)); - -/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from - state->fd, and update state->eof, state->err, and state->msg as appropriate. - This function needs to loop on read(), since read() is not guaranteed to - read the number of bytes requested, depending on the type of descriptor. */ -local int gz_load(gz_statep state, unsigned char *buf, unsigned len, unsigned *have) -{ - int ret; - - *have = 0; - do { - ret = read(state->fd, buf + *have, len - *have); - if (ret <= 0) - break; - *have += ret; - } while (*have < len); - if (ret < 0) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; - } - if (ret == 0) - state->eof = 1; - return 0; -} - -/* Load up input buffer and set eof flag if last data loaded -- return -1 on - error, 0 otherwise. Note that the eof flag is set when the end of the input - file is reached, even though there may be unused data in the buffer. Once - that data has been used, no more attempts will be made to read the file. - If strm->avail_in != 0, then the current data is moved to the beginning of - the input buffer, and then the remainder of the buffer is loaded with the - available data from the input file. */ -local int gz_avail(gz_statep state) -{ - unsigned got; - z_streamp strm = &(state->strm); - - if (state->err != Z_OK && state->err != Z_BUF_ERROR) - return -1; - if (state->eof == 0) { - if (strm->avail_in) { /* copy what's there to the start */ - unsigned char *p = state->in; - unsigned const char *q = strm->next_in; - unsigned n = strm->avail_in; - do { - *p++ = *q++; - } while (--n); - } - if (gz_load(state, state->in + strm->avail_in, - state->size - strm->avail_in, &got) == -1) - return -1; - strm->avail_in += got; - strm->next_in = state->in; - } - return 0; -} - -/* Look for gzip header, set up for inflate or copy. state->x.have must be 0. - If this is the first time in, allocate required memory. state->how will be - left unchanged if there is no more input data available, will be set to COPY - if there is no gzip header and direct copying will be performed, or it will - be set to GZIP for decompression. If direct copying, then leftover input - data from the input buffer will be copied to the output buffer. In that - case, all further file reads will be directly to either the output buffer or - a user buffer. If decompressing, the inflate state will be initialized. - gz_look() will return 0 on success or -1 on failure. */ -local int gz_look(gz_statep state) -{ - z_streamp strm = &(state->strm); - - /* allocate read buffers and inflate memory */ - if (state->size == 0) { - /* allocate buffers */ - state->in = (unsigned char *)malloc(state->want); - state->out = (unsigned char *)malloc(state->want << 1); - if (state->in == NULL || state->out == NULL) { - if (state->out != NULL) - free(state->out); - if (state->in != NULL) - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - state->size = state->want; - - /* allocate inflate memory */ - state->strm.zalloc = Z_NULL; - state->strm.zfree = Z_NULL; - state->strm.opaque = Z_NULL; - state->strm.avail_in = 0; - state->strm.next_in = Z_NULL; - if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ - free(state->out); - free(state->in); - state->size = 0; - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - } - - /* get at least the magic bytes in the input buffer */ - if (strm->avail_in < 2) { - if (gz_avail(state) == -1) - return -1; - if (strm->avail_in == 0) - return 0; - } - - /* look for gzip magic bytes -- if there, do gzip decoding (note: there is - a logical dilemma here when considering the case of a partially written - gzip file, to wit, if a single 31 byte is written, then we cannot tell - whether this is a single-byte file, or just a partially written gzip - file -- for here we assume that if a gzip file is being written, then - the header will be written in a single operation, so that reading a - single byte is sufficient indication that it is not a gzip file) */ - if (strm->avail_in > 1 && - strm->next_in[0] == 31 && strm->next_in[1] == 139) { - inflateReset(strm); - state->how = GZIP; - state->direct = 0; - return 0; - } - - /* no gzip header -- if we were decoding gzip before, then this is trailing - garbage. Ignore the trailing garbage and finish. */ - if (state->direct == 0) { - strm->avail_in = 0; - state->eof = 1; - state->x.have = 0; - return 0; - } - - /* doing raw i/o, copy any leftover input to output -- this assumes that - the output buffer is larger than the input buffer, which also assures - space for gzungetc() */ - state->x.next = state->out; - if (strm->avail_in) { - memcpy(state->x.next, strm->next_in, strm->avail_in); - state->x.have = strm->avail_in; - strm->avail_in = 0; - } - state->how = COPY; - state->direct = 1; - return 0; -} - -/* Decompress from input to the provided next_out and avail_out in the state. - On return, state->x.have and state->x.next point to the just decompressed - data. If the gzip stream completes, state->how is reset to LOOK to look for - the next gzip stream or raw data, once state->x.have is depleted. Returns 0 - on success, -1 on failure. */ -local int gz_decomp(gz_statep state) -{ - int ret = Z_OK; - unsigned had; - z_streamp strm = &(state->strm); - - /* fill output buffer up to end of deflate stream */ - had = strm->avail_out; - do { - /* get more input for inflate() */ - if (strm->avail_in == 0 && gz_avail(state) == -1) - return -1; - if (strm->avail_in == 0) { - gz_error(state, Z_BUF_ERROR, "unexpected end of file"); - break; - } - - /* decompress and handle errors */ - ret = inflate(strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { - gz_error(state, Z_STREAM_ERROR, - "internal error: inflate stream corrupt"); - return -1; - } - if (ret == Z_MEM_ERROR) { - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ - gz_error(state, Z_DATA_ERROR, - strm->msg == NULL ? "compressed data error" : strm->msg); - return -1; - } - } while (strm->avail_out && ret != Z_STREAM_END); - - /* update available output */ - state->x.have = had - strm->avail_out; - state->x.next = strm->next_out - state->x.have; - - /* if the gzip stream completed successfully, look for another */ - if (ret == Z_STREAM_END) - state->how = LOOK; - - /* good decompression */ - return 0; -} - -/* Fetch data and put it in the output buffer. Assumes state->x.have is 0. - Data is either copied from the input file or decompressed from the input - file depending on state->how. If state->how is LOOK, then a gzip header is - looked for to determine whether to copy or decompress. Returns -1 on error, - otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the - end of the input file has been reached and all data has been processed. */ -local int gz_fetch(gz_statep state) -{ - z_streamp strm = &(state->strm); - - do { - switch(state->how) { - case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ - if (gz_look(state) == -1) - return -1; - if (state->how == LOOK) - return 0; - break; - case COPY: /* -> COPY */ - if (gz_load(state, state->out, state->size << 1, &(state->x.have)) - == -1) - return -1; - state->x.next = state->out; - return 0; - case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ - strm->avail_out = state->size << 1; - strm->next_out = state->out; - if (gz_decomp(state) == -1) - return -1; - } - } while (state->x.have == 0 && (!state->eof || strm->avail_in)); - return 0; -} - -/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ -local int gz_skip(gz_statep state, z_off64_t len) -{ - unsigned n; - - /* skip over len bytes or reach end-of-file, whichever comes first */ - while (len) - /* skip over whatever is in output buffer */ - if (state->x.have) { - n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ? - (unsigned)len : state->x.have; - state->x.have -= n; - state->x.next += n; - state->x.pos += n; - len -= n; - } - - /* output buffer empty -- return if we're at the end of the input */ - else if (state->eof && state->strm.avail_in == 0) - break; - - /* need more data to skip -- load up output buffer */ - else { - /* get more output, looking for header if required */ - if (gz_fetch(state) == -1) - return -1; - } - return 0; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzread(gzFile file, voidp buf, unsigned len) -{ - unsigned got, n; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return -1; - } - - /* if len is zero, avoid unnecessary operations */ - if (len == 0) - return 0; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return -1; - } - - /* get len bytes to buf, or less than len if at the end */ - got = 0; - do { - /* first just try copying data from the output buffer */ - if (state->x.have) { - n = state->x.have > len ? len : state->x.have; - memcpy(buf, state->x.next, n); - state->x.next += n; - state->x.have -= n; - } - - /* output buffer empty -- return if we're at the end of the input */ - else if (state->eof && strm->avail_in == 0) { - state->past = 1; /* tried to read past end */ - break; - } - - /* need output data -- for small len or new stream load up our output - buffer */ - else if (state->how == LOOK || len < (state->size << 1)) { - /* get more output, looking for header if required */ - if (gz_fetch(state) == -1) - return -1; - continue; /* no progress yet -- go back to copy above */ - /* the copy above assures that we will leave with space in the - output buffer, allowing at least one gzungetc() to succeed */ - } - - /* large len -- read directly into user buffer */ - else if (state->how == COPY) { /* read directly */ - if (gz_load(state, (unsigned char *)buf, len, &n) == -1) - return -1; - } - - /* large len -- decompress directly into user buffer */ - else { /* state->how == GZIP */ - strm->avail_out = len; - strm->next_out = (unsigned char *)buf; - if (gz_decomp(state) == -1) - return -1; - n = state->x.have; - state->x.have = 0; - } - - /* update progress */ - len -= n; - buf = (char *)buf + n; - got += n; - state->x.pos += n; - } while (len); - - /* return number of bytes read into user buffer (will fit in int) */ - return (int)got; -} - -/* -- see zlib.h -- */ -#ifdef Z_PREFIX_SET -# undef z_gzgetc -#else -# undef gzgetc -#endif -int ZEXPORT gzgetc(gzFile file) -{ - int ret; - unsigned char buf[1]; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* try output buffer (no need to check for skip request) */ - if (state->x.have) { - state->x.have--; - state->x.pos++; - return *(state->x.next)++; - } - - /* nothing there -- try gzread() */ - ret = gzread(file, buf, 1); - return ret < 1 ? -1 : buf[0]; -} - -int ZEXPORT gzgetc_(gzFile file) -{ - return gzgetc(file); -} - -/* -- see zlib.h -- */ -int ZEXPORT gzungetc(int c, gzFile file) -{ - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return -1; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return -1; - } - - /* can't push EOF */ - if (c < 0) - return -1; - - /* if output buffer empty, put byte at end (allows more pushing) */ - if (state->x.have == 0) { - state->x.have = 1; - state->x.next = state->out + (state->size << 1) - 1; - state->x.next[0] = c; - state->x.pos--; - state->past = 0; - return c; - } - - /* if no room, give up (must have already done a gzungetc()) */ - if (state->x.have == (state->size << 1)) { - gz_error(state, Z_DATA_ERROR, "out of room to push characters"); - return -1; - } - - /* slide output data if needed and insert byte before existing data */ - if (state->x.next == state->out) { - unsigned char *src = state->out + state->x.have; - unsigned char *dest = state->out + (state->size << 1); - while (src > state->out) - *--dest = *--src; - state->x.next = dest; - } - state->x.have++; - state->x.next--; - state->x.next[0] = c; - state->x.pos--; - state->past = 0; - return c; -} - -/* -- see zlib.h -- */ -char * ZEXPORT gzgets(gzFile file, char *buf, int len) -{ - unsigned left, n; - char *str; - unsigned char *eol; - gz_statep state; - - /* check parameters and get internal structure */ - if (file == NULL || buf == NULL || len < 1) - return NULL; - state = (gz_statep)file; - - /* check that we're reading and that there's no (serious) error */ - if (state->mode != GZ_READ || - (state->err != Z_OK && state->err != Z_BUF_ERROR)) - return NULL; - - /* process a skip request */ - if (state->seek) { - state->seek = 0; - if (gz_skip(state, state->skip) == -1) - return NULL; - } - - /* copy output bytes up to new line or len - 1, whichever comes first -- - append a terminating zero to the string (we don't check for a zero in - the contents, let the user worry about that) */ - str = buf; - left = (unsigned)len - 1; - if (left) do { - /* assure that something is in the output buffer */ - if (state->x.have == 0 && gz_fetch(state) == -1) - return NULL; /* error */ - if (state->x.have == 0) { /* end of file */ - state->past = 1; /* read past end */ - break; /* return what we have */ - } - - /* look for end-of-line in current output buffer */ - n = state->x.have > left ? left : state->x.have; - eol = (unsigned char *)memchr(state->x.next, '\n', n); - if (eol != NULL) - n = (unsigned)(eol - state->x.next) + 1; - - /* copy through end-of-line, or remainder if not found */ - memcpy(buf, state->x.next, n); - state->x.have -= n; - state->x.next += n; - state->x.pos += n; - left -= n; - buf += n; - } while (left && eol == NULL); - - /* return terminated string, or if nothing, end of file */ - if (buf == str) - return NULL; - buf[0] = 0; - return str; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzdirect(gzFile file) -{ - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - - /* if the state is not known, but we can find out, then do so (this is - mainly for right after a gzopen() or gzdopen()) */ - if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) - (void)gz_look(state); - - /* return 1 if transparent, 0 if processing a gzip stream */ - return state->direct; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzclose_r(gzFile file) -{ - int ret, err; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - /* check that we're reading */ - if (state->mode != GZ_READ) - return Z_STREAM_ERROR; - - /* free memory and close file */ - if (state->size) { - inflateEnd(&(state->strm)); - free(state->out); - free(state->in); - } - err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; - gz_error(state, Z_OK, NULL); - free(state->path); - ret = close(state->fd); - free(state); - return ret ? Z_ERRNO : err; -} diff --git a/builtins/zlib/gzwrite.c b/builtins/zlib/gzwrite.c deleted file mode 100644 index dad29e425a11c..0000000000000 --- a/builtins/zlib/gzwrite.c +++ /dev/null @@ -1,564 +0,0 @@ -/* gzwrite.c -- zlib functions for writing gzip files - * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#ifndef _WIN32 -#include -#include -#endif - -#include "gzguts.h" - -/* Local functions */ -local int gz_init OF((gz_statep)); -local int gz_comp OF((gz_statep, int)); -local int gz_zero OF((gz_statep, z_off64_t)); - -/* Initialize state for writing a gzip file. Mark initialization by setting - state->size to non-zero. Return -1 on failure or 0 on success. */ -local int gz_init(gz_statep state) -{ - int ret; - z_streamp strm = &(state->strm); - - /* allocate input buffer */ - state->in = (unsigned char *)malloc(state->want); - if (state->in == NULL) { - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - - /* only need output buffer and deflate state if compressing */ - if (!state->direct) { - /* allocate output buffer */ - state->out = (unsigned char *)malloc(state->want); - if (state->out == NULL) { - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - - /* allocate deflate memory, set up for gzip compression */ - strm->zalloc = Z_NULL; - strm->zfree = Z_NULL; - strm->opaque = Z_NULL; - ret = deflateInit2(strm, state->level, Z_DEFLATED, - MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); - if (ret != Z_OK) { - free(state->out); - free(state->in); - gz_error(state, Z_MEM_ERROR, "out of memory"); - return -1; - } - } - - /* mark state as initialized */ - state->size = state->want; - - /* initialize write buffer if compressing */ - if (!state->direct) { - strm->avail_out = state->size; - strm->next_out = state->out; - state->x.next = strm->next_out; - } - return 0; -} - -/* Compress whatever is at avail_in and next_in and write to the output file. - Return -1 if there is an error writing to the output file, otherwise 0. - flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, - then the deflate() state is reset to start a new gzip stream. If gz->direct - is true, then simply write to the output file without compressing, and - ignore flush. */ -local int gz_comp(gz_statep state, int flush) -{ - int ret, got; - unsigned have; - z_streamp strm = &(state->strm); - - /* allocate memory if this is the first time through */ - if (state->size == 0 && gz_init(state) == -1) - return -1; - - /* write directly if requested */ - if (state->direct) { - got = write(state->fd, strm->next_in, strm->avail_in); - if (got < 0 || (unsigned)got != strm->avail_in) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; - } - strm->avail_in = 0; - return 0; - } - - /* run deflate() on provided input until it produces no more output */ - ret = Z_OK; - do { - /* write out current buffer contents if full, or if flushing, but if - doing Z_FINISH then don't write until we get to Z_STREAM_END */ - if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && - (flush != Z_FINISH || ret == Z_STREAM_END))) { - have = (unsigned)(strm->next_out - state->x.next); - if (have && ((got = write(state->fd, state->x.next, have)) < 0 || - (unsigned)got != have)) { - gz_error(state, Z_ERRNO, zstrerror()); - return -1; - } - if (strm->avail_out == 0) { - strm->avail_out = state->size; - strm->next_out = state->out; - } - state->x.next = strm->next_out; - } - - /* compress */ - have = strm->avail_out; - ret = deflate(strm, flush); - if (ret == Z_STREAM_ERROR) { - gz_error(state, Z_STREAM_ERROR, - "internal error: deflate stream corrupt"); - return -1; - } - have -= strm->avail_out; - } while (have); - - /* if that completed a deflate stream, allow another to start */ - if (flush == Z_FINISH) - deflateReset(strm); - - /* all done, no errors */ - return 0; -} - -/* Compress len zeros to output. Return -1 on error, 0 on success. */ -local int gz_zero(gz_statep state, z_off64_t len) -{ - int first; - unsigned n; - z_streamp strm = &(state->strm); - - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return -1; - - /* compress len zeros (len guaranteed > 0) */ - first = 1; - while (len) { - n = GT_OFF(state->size) || (z_off64_t)state->size > len ? - (unsigned)len : state->size; - if (first) { - memset(state->in, 0, n); - first = 0; - } - strm->avail_in = n; - strm->next_in = state->in; - state->x.pos += n; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return -1; - len -= n; - } - return 0; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len) -{ - unsigned put = len; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return 0; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* since an int is returned, make sure len fits in one, otherwise return - with an error (this avoids the flaw in the interface) */ - if ((int)len < 0) { - gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); - return 0; - } - - /* if len is zero, avoid unnecessary operations */ - if (len == 0) - return 0; - - /* allocate memory if this is the first time through */ - if (state->size == 0 && gz_init(state) == -1) - return 0; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return 0; - } - - /* for small len, copy to input buffer, otherwise compress directly */ - if (len < state->size) { - /* copy to input buffer, compress when full */ - do { - unsigned have, copy; - - if (strm->avail_in == 0) - strm->next_in = state->in; - have = (unsigned)((strm->next_in + strm->avail_in) - state->in); - copy = state->size - have; - if (copy > len) - copy = len; - memcpy(state->in + have, buf, copy); - strm->avail_in += copy; - state->x.pos += copy; - buf = (const char *)buf + copy; - len -= copy; - if (len && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - } while (len); - } - else { - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* directly compress user buffer to file */ - strm->avail_in = len; - strm->next_in = (z_const Bytef *)buf; - state->x.pos += len; - if (gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - } - - /* input was all buffered or compressed (put will fit in int) */ - return (int)put; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzputc(gzFile file, int c) -{ - unsigned have; - unsigned char buf[1]; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return -1; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return -1; - } - - /* try writing to input buffer for speed (state->size == 0 if buffer not - initialized) */ - if (state->size) { - if (strm->avail_in == 0) - strm->next_in = state->in; - have = (unsigned)((strm->next_in + strm->avail_in) - state->in); - if (have < state->size) { - state->in[have] = c; - strm->avail_in++; - state->x.pos++; - return c & 0xff; - } - } - - /* no room in buffer or not initialized, use gz_write() */ - buf[0] = c; - if (gzwrite(file, buf, 1) != 1) - return -1; - return c & 0xff; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzputs(gzFile file, const char *str) -{ - int ret; - unsigned len; - - /* write string */ - len = (unsigned)strlen(str); - ret = gzwrite(file, str, len); - return ret == 0 && len != 0 ? -1 : ret; -} - -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -#include - -/* -- see zlib.h -- */ -int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) -{ - int size, len; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* make sure we have some buffer space */ - if (state->size == 0 && gz_init(state) == -1) - return 0; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return 0; - } - - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state->size); - state->in[size - 1] = 0; -#ifdef NO_vsnprintf -# ifdef HAS_vsprintf_void - (void)vsprintf((char *)(state->in), format, va); - for (len = 0; len < size; len++) - if (state->in[len] == 0) break; -# else - len = vsprintf((char *)(state->in), format, va); -# endif -#else -# ifdef HAS_vsnprintf_void - (void)vsnprintf((char *)(state->in), size, format, va); - len = strlen((char *)(state->in)); -# else - len = vsnprintf((char *)(state->in), size, format, va); -# endif -#endif - - /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) - return 0; - - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state->in; - state->x.pos += len; - return len; -} - -int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) -{ - va_list va; - int ret; - - va_start(va, format); - ret = gzvprintf(file, format, va); - va_end(va); - return ret; -} - -#else /* !STDC && !Z_HAVE_STDARG_H */ - -/* -- see zlib.h -- */ -int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) - gzFile file; - const char *format; - int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, - a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; -{ - int size, len; - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that can really pass pointer in ints */ - if (sizeof(int) != sizeof(void *)) - return 0; - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return 0; - - /* make sure we have some buffer space */ - if (state->size == 0 && gz_init(state) == -1) - return 0; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return 0; - } - - /* consume whatever's left in the input buffer */ - if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) - return 0; - - /* do the printf() into the input buffer, put length in len */ - size = (int)(state->size); - state->in[size - 1] = 0; -#ifdef NO_snprintf -# ifdef HAS_sprintf_void - sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); - for (len = 0; len < size; len++) - if (state->in[len] == 0) break; -# else - len = sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); -# endif -#else -# ifdef HAS_snprintf_void - snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, - a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); - len = strlen((char *)(state->in)); -# else - len = snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, - a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, - a19, a20); -# endif -#endif - - /* check that printf() results fit in buffer */ - if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) - return 0; - - /* update buffer and position, defer compression until needed */ - strm->avail_in = (unsigned)len; - strm->next_in = state->in; - state->x.pos += len; - return len; -} - -#endif - -/* -- see zlib.h -- */ -int ZEXPORT gzflush(gzFile file, int flush) -{ - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return -1; - state = (gz_statep)file; - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return Z_STREAM_ERROR; - - /* check flush parameter */ - if (flush < 0 || flush > Z_FINISH) - return Z_STREAM_ERROR; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return -1; - } - - /* compress remaining data with requested flush */ - gz_comp(state, flush); - return state->err; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzsetparams(gzFile file, int level, int strategy) -{ - gz_statep state; - z_streamp strm; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - strm = &(state->strm); - - /* check that we're writing and that there's no error */ - if (state->mode != GZ_WRITE || state->err != Z_OK) - return Z_STREAM_ERROR; - - /* if no change is requested, then do nothing */ - if (level == state->level && strategy == state->strategy) - return Z_OK; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - return -1; - } - - /* change compression parameters for subsequent input */ - if (state->size) { - /* flush previous input with previous parameters before changing */ - if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) - return state->err; - deflateParams(strm, level, strategy); - } - state->level = level; - state->strategy = strategy; - return Z_OK; -} - -/* -- see zlib.h -- */ -int ZEXPORT gzclose_w(gzFile file) -{ - int ret = Z_OK; - gz_statep state; - - /* get internal structure */ - if (file == NULL) - return Z_STREAM_ERROR; - state = (gz_statep)file; - - /* check that we're writing */ - if (state->mode != GZ_WRITE) - return Z_STREAM_ERROR; - - /* check for seek request */ - if (state->seek) { - state->seek = 0; - if (gz_zero(state, state->skip) == -1) - ret = state->err; - } - - /* flush, free memory, and close file */ - if (gz_comp(state, Z_FINISH) == -1) - ret = state->err; - if (state->size) { - if (!state->direct) { - (void)deflateEnd(&(state->strm)); - free(state->out); - } - free(state->in); - } - gz_error(state, Z_OK, NULL); - free(state->path); - if (close(state->fd) == -1) - ret = Z_ERRNO; - free(state); - return ret; -} diff --git a/builtins/zlib/infback.c b/builtins/zlib/infback.c deleted file mode 100644 index d7e5360c45758..0000000000000 --- a/builtins/zlib/infback.c +++ /dev/null @@ -1,628 +0,0 @@ -/* infback.c -- inflate using a call-back interface - * Copyright (C) 1995-2011 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - This code is largely copied from inflate.c. Normally either infback.o or - inflate.o would be linked into an application--not both. The interface - with inffast.c is retained so that optimized assembler-coded versions of - inflate_fast() can be used with either inflate.c or infback.c. - */ - -#include "zutil.h" -#include "inftrees.h" -#include "inflate.h" -#include "inffast.h" - -/* function prototypes */ -local void fixedtables OF((struct inflate_state FAR *state)); - -/* - strm provides memory allocation functions in zalloc and zfree, or - Z_NULL to use the library memory allocation functions. - - windowBits is in the range 8..15, and window is a user-supplied - window and output buffer that is 2**windowBits bytes. - */ -int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size) -{ - struct inflate_state FAR *state; - - if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || - stream_size != (int)(sizeof(z_stream))) - return Z_VERSION_ERROR; - if (strm == Z_NULL || window == Z_NULL || - windowBits < 8 || windowBits > 15) - return Z_STREAM_ERROR; - strm->msg = Z_NULL; /* in case we return an error */ - if (strm->zalloc == (alloc_func)0) { -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zalloc = zcalloc; - strm->opaque = (voidpf)0; -#endif - } - if (strm->zfree == (free_func)0) -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zfree = zcfree; -#endif - state = (struct inflate_state FAR *)ZALLOC(strm, 1, - sizeof(struct inflate_state)); - if (state == Z_NULL) return Z_MEM_ERROR; - Tracev((stderr, "inflate: allocated\n")); - strm->state = (struct internal_state FAR *)state; - state->dmax = 32768U; - state->wbits = windowBits; - state->wsize = 1U << windowBits; - state->window = window; - state->wnext = 0; - state->whave = 0; - return Z_OK; -} - -/* - Return state with length and distance decoding tables and index sizes set to - fixed code decoding. Normally this returns fixed tables from inffixed.h. - If BUILDFIXED is defined, then instead this routine builds the tables the - first time it's called, and returns those tables the first time and - thereafter. This reduces the size of the code by about 2K bytes, in - exchange for a little execution time. However, BUILDFIXED should not be - used for threaded applications, since the rewriting of the tables and virgin - may not be thread-safe. - */ -local void fixedtables(struct inflate_state FAR *state) -{ -#ifdef BUILDFIXED - static int virgin = 1; - static code *lenfix, *distfix; - static code fixed[544]; - - /* build fixed huffman tables if first call (may not be thread safe) */ - if (virgin) { - unsigned sym, bits; - static code *next; - - /* literal/length table */ - sym = 0; - while (sym < 144) state->lens[sym++] = 8; - while (sym < 256) state->lens[sym++] = 9; - while (sym < 280) state->lens[sym++] = 7; - while (sym < 288) state->lens[sym++] = 8; - next = fixed; - lenfix = next; - bits = 9; - inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); - - /* distance table */ - sym = 0; - while (sym < 32) state->lens[sym++] = 5; - distfix = next; - bits = 5; - inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); - - /* do this just once */ - virgin = 0; - } -#else /* !BUILDFIXED */ -# include "inffixed.h" -#endif /* BUILDFIXED */ - state->lencode = lenfix; - state->lenbits = 9; - state->distcode = distfix; - state->distbits = 5; -} - -/* Macros for inflateBack(): */ - -/* Load returned state from inflate_fast() */ -#define LOAD() \ - do { \ - put = strm->next_out; \ - left = strm->avail_out; \ - next = strm->next_in; \ - have = strm->avail_in; \ - hold = state->hold; \ - bits = state->bits; \ - } while (0) - -/* Set state from registers for inflate_fast() */ -#define RESTORE() \ - do { \ - strm->next_out = put; \ - strm->avail_out = left; \ - strm->next_in = next; \ - strm->avail_in = have; \ - state->hold = hold; \ - state->bits = bits; \ - } while (0) - -/* Clear the input bit accumulator */ -#define INITBITS() \ - do { \ - hold = 0; \ - bits = 0; \ - } while (0) - -/* Assure that some input is available. If input is requested, but denied, - then return a Z_BUF_ERROR from inflateBack(). */ -#define PULL() \ - do { \ - if (have == 0) { \ - have = in(in_desc, &next); \ - if (have == 0) { \ - next = Z_NULL; \ - ret = Z_BUF_ERROR; \ - goto inf_leave; \ - } \ - } \ - } while (0) - -/* Get a byte of input into the bit accumulator, or return from inflateBack() - with an error if there is no input available. */ -#define PULLBYTE() \ - do { \ - PULL(); \ - have--; \ - hold += (unsigned long)(*next++) << bits; \ - bits += 8; \ - } while (0) - -/* Assure that there are at least n bits in the bit accumulator. If there is - not enough available input to do that, then return from inflateBack() with - an error. */ -#define NEEDBITS(n) \ - do { \ - while (bits < (unsigned)(n)) \ - PULLBYTE(); \ - } while (0) - -/* Return the low n bits of the bit accumulator (n < 16) */ -#define BITS(n) \ - ((unsigned)hold & ((1U << (n)) - 1)) - -/* Remove n bits from the bit accumulator */ -#define DROPBITS(n) \ - do { \ - hold >>= (n); \ - bits -= (unsigned)(n); \ - } while (0) - -/* Remove zero to seven bits as needed to go to a byte boundary */ -#define BYTEBITS() \ - do { \ - hold >>= bits & 7; \ - bits -= bits & 7; \ - } while (0) - -/* Assure that some output space is available, by writing out the window - if it's full. If the write fails, return from inflateBack() with a - Z_BUF_ERROR. */ -#define ROOM() \ - do { \ - if (left == 0) { \ - put = state->window; \ - left = state->wsize; \ - state->whave = left; \ - if (out(out_desc, put, left)) { \ - ret = Z_BUF_ERROR; \ - goto inf_leave; \ - } \ - } \ - } while (0) - -/* - strm provides the memory allocation functions and window buffer on input, - and provides information on the unused input on return. For Z_DATA_ERROR - returns, strm will also provide an error message. - - in() and out() are the call-back input and output functions. When - inflateBack() needs more input, it calls in(). When inflateBack() has - filled the window with output, or when it completes with data in the - window, it calls out() to write out the data. The application must not - change the provided input until in() is called again or inflateBack() - returns. The application must not change the window/output buffer until - inflateBack() returns. - - in() and out() are called with a descriptor parameter provided in the - inflateBack() call. This parameter can be a structure that provides the - information required to do the read or write, as well as accumulated - information on the input and output such as totals and check values. - - in() should return zero on failure. out() should return non-zero on - failure. If either in() or out() fails, than inflateBack() returns a - Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it - was in() or out() that caused in the error. Otherwise, inflateBack() - returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format - error, or Z_MEM_ERROR if it could not allocate memory for the state. - inflateBack() can also return Z_STREAM_ERROR if the input parameters - are not correct, i.e. strm is Z_NULL or the state was not initialized. - */ -int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc) -{ - struct inflate_state FAR *state; - z_const unsigned char FAR *next; /* next input */ - unsigned char FAR *put; /* next output */ - unsigned have, left; /* available input and output */ - unsigned long hold; /* bit buffer */ - unsigned bits; /* bits in bit buffer */ - unsigned copy; /* number of stored or match bytes to copy */ - unsigned char FAR *from; /* where to copy match bytes from */ - code here; /* current decoding table entry */ - code last; /* parent table entry */ - unsigned len; /* length to copy for repeats, bits to drop */ - int ret; /* return code */ - static const unsigned short order[19] = /* permutation of code lengths */ - {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - - /* Check that the strm exists and that the state was initialized */ - if (strm == Z_NULL || strm->state == Z_NULL) - return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - - /* Reset the state */ - strm->msg = Z_NULL; - state->mode = TYPE; - state->last = 0; - state->whave = 0; - next = strm->next_in; - have = next != Z_NULL ? strm->avail_in : 0; - hold = 0; - bits = 0; - put = state->window; - left = state->wsize; - - /* Inflate until end of block marked as last */ - for (;;) - switch (state->mode) { - case TYPE: - /* determine and dispatch block type */ - if (state->last) { - BYTEBITS(); - state->mode = DONE; - break; - } - NEEDBITS(3); - state->last = BITS(1); - DROPBITS(1); - switch (BITS(2)) { - case 0: /* stored block */ - Tracev((stderr, "inflate: stored block%s\n", - state->last ? " (last)" : "")); - state->mode = STORED; - break; - case 1: /* fixed block */ - fixedtables(state); - Tracev((stderr, "inflate: fixed codes block%s\n", - state->last ? " (last)" : "")); - state->mode = LEN; /* decode codes */ - break; - case 2: /* dynamic block */ - Tracev((stderr, "inflate: dynamic codes block%s\n", - state->last ? " (last)" : "")); - state->mode = TABLE; - break; - case 3: - strm->msg = (char *)"invalid block type"; - state->mode = BAD; - } - DROPBITS(2); - break; - - case STORED: - /* get and verify stored block length */ - BYTEBITS(); /* go to byte boundary */ - NEEDBITS(32); - if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { - strm->msg = (char *)"invalid stored block lengths"; - state->mode = BAD; - break; - } - state->length = (unsigned)hold & 0xffff; - Tracev((stderr, "inflate: stored length %u\n", - state->length)); - INITBITS(); - - /* copy stored block from input to output */ - while (state->length != 0) { - copy = state->length; - PULL(); - ROOM(); - if (copy > have) copy = have; - if (copy > left) copy = left; - zmemcpy(put, next, copy); - have -= copy; - next += copy; - left -= copy; - put += copy; - state->length -= copy; - } - Tracev((stderr, "inflate: stored end\n")); - state->mode = TYPE; - break; - - case TABLE: - /* get dynamic table entries descriptor */ - NEEDBITS(14); - state->nlen = BITS(5) + 257; - DROPBITS(5); - state->ndist = BITS(5) + 1; - DROPBITS(5); - state->ncode = BITS(4) + 4; - DROPBITS(4); -#ifndef PKZIP_BUG_WORKAROUND - if (state->nlen > 286 || state->ndist > 30) { - strm->msg = (char *)"too many length or distance symbols"; - state->mode = BAD; - break; - } -#endif - Tracev((stderr, "inflate: table sizes ok\n")); - - /* get code length code lengths (not a typo) */ - state->have = 0; - while (state->have < state->ncode) { - NEEDBITS(3); - state->lens[order[state->have++]] = (unsigned short)BITS(3); - DROPBITS(3); - } - while (state->have < 19) - state->lens[order[state->have++]] = 0; - state->next = state->codes; - state->lencode = (code const FAR *)(state->next); - state->lenbits = 7; - ret = inflate_table(CODES, state->lens, 19, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid code lengths set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: code lengths ok\n")); - - /* get length and distance code code lengths */ - state->have = 0; - while (state->have < state->nlen + state->ndist) { - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.val < 16) { - DROPBITS(here.bits); - state->lens[state->have++] = here.val; - } - else { - if (here.val == 16) { - NEEDBITS(here.bits + 2); - DROPBITS(here.bits); - if (state->have == 0) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - len = (unsigned)(state->lens[state->have - 1]); - copy = 3 + BITS(2); - DROPBITS(2); - } - else if (here.val == 17) { - NEEDBITS(here.bits + 3); - DROPBITS(here.bits); - len = 0; - copy = 3 + BITS(3); - DROPBITS(3); - } - else { - NEEDBITS(here.bits + 7); - DROPBITS(here.bits); - len = 0; - copy = 11 + BITS(7); - DROPBITS(7); - } - if (state->have + copy > state->nlen + state->ndist) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - while (copy--) - state->lens[state->have++] = (unsigned short)len; - } - } - - /* handle error breaks in while */ - if (state->mode == BAD) break; - - /* check for end-of-block code (better have one) */ - if (state->lens[256] == 0) { - strm->msg = (char *)"invalid code -- missing end-of-block"; - state->mode = BAD; - break; - } - - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.h - concerning the ENOUGH constants, which depend on those values */ - state->next = state->codes; - state->lencode = (code const FAR *)(state->next); - state->lenbits = 9; - ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid literal/lengths set"; - state->mode = BAD; - break; - } - state->distcode = (code const FAR *)(state->next); - state->distbits = 6; - ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, - &(state->next), &(state->distbits), state->work); - if (ret) { - strm->msg = (char *)"invalid distances set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: codes ok\n")); - state->mode = LEN; - - case LEN: - /* use inflate_fast() if we have enough input and output */ - if (have >= 6 && left >= 258) { - RESTORE(); - if (state->whave < state->wsize) - state->whave = state->wsize - left; - inflate_fast(strm, state->wsize); - LOAD(); - break; - } - - /* get a literal, length, or end-of-block code */ - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.op && (here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->lencode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - } - DROPBITS(here.bits); - state->length = (unsigned)here.val; - - /* process literal */ - if (here.op == 0) { - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - ROOM(); - *put++ = (unsigned char)(state->length); - left--; - state->mode = LEN; - break; - } - - /* process end of block */ - if (here.op & 32) { - Tracevv((stderr, "inflate: end of block\n")); - state->mode = TYPE; - break; - } - - /* invalid code */ - if (here.op & 64) { - strm->msg = (char *)"invalid literal/length code"; - state->mode = BAD; - break; - } - - /* length code -- get extra bits, if any */ - state->extra = (unsigned)(here.op) & 15; - if (state->extra != 0) { - NEEDBITS(state->extra); - state->length += BITS(state->extra); - DROPBITS(state->extra); - } - Tracevv((stderr, "inflate: length %u\n", state->length)); - - /* get distance code */ - for (;;) { - here = state->distcode[BITS(state->distbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if ((here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->distcode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - } - DROPBITS(here.bits); - if (here.op & 64) { - strm->msg = (char *)"invalid distance code"; - state->mode = BAD; - break; - } - state->offset = (unsigned)here.val; - - /* get distance extra bits, if any */ - state->extra = (unsigned)(here.op) & 15; - if (state->extra != 0) { - NEEDBITS(state->extra); - state->offset += BITS(state->extra); - DROPBITS(state->extra); - } - if (state->offset > state->wsize - (state->whave < state->wsize ? - left : 0)) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } - Tracevv((stderr, "inflate: distance %u\n", state->offset)); - - /* copy match from window to output */ - do { - ROOM(); - copy = state->wsize - state->offset; - if (copy < left) { - from = put + copy; - copy = left - copy; - } - else { - from = put - state->offset; - copy = left; - } - if (copy > state->length) copy = state->length; - state->length -= copy; - left -= copy; - do { - *put++ = *from++; - } while (--copy); - } while (state->length != 0); - break; - - case DONE: - /* inflate stream terminated properly -- write leftover output */ - ret = Z_STREAM_END; - if (left < state->wsize) { - if (out(out_desc, state->window, state->wsize - left)) - ret = Z_BUF_ERROR; - } - goto inf_leave; - - case BAD: - ret = Z_DATA_ERROR; - goto inf_leave; - - default: /* can't happen, but makes compilers happy */ - ret = Z_STREAM_ERROR; - goto inf_leave; - } - - /* Return unused input */ - inf_leave: - strm->next_in = next; - strm->avail_in = have; - return ret; -} - -int ZEXPORT inflateBackEnd(z_streamp strm) -{ - if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) - return Z_STREAM_ERROR; - ZFREE(strm, strm->state); - strm->state = Z_NULL; - Tracev((stderr, "inflate: end\n")); - return Z_OK; -} diff --git a/builtins/zlib/inffast.c b/builtins/zlib/inffast.c deleted file mode 100644 index 282275116695a..0000000000000 --- a/builtins/zlib/inffast.c +++ /dev/null @@ -1,319 +0,0 @@ -/* inffast.c -- fast decoding - * Copyright (C) 1995-2008, 2010, 2013 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "zutil.h" -#include "inftrees.h" -#include "inflate.h" -#include "inffast.h" - -#ifndef ASMINF - -/* - Decode literal, length, and distance codes and write out the resulting - literal and match bytes until either not enough input or output is - available, an end-of-block is encountered, or a data error is encountered. - When large enough input and output buffers are supplied to inflate(), for - example, a 16K input buffer and a 64K output buffer, more than 95% of the - inflate execution time is spent in this routine. - - Entry assumptions: - - state->mode == LEN - strm->avail_in >= 6 - strm->avail_out >= 258 - start >= strm->avail_out - state->bits < 8 - - On return, state->mode is one of: - - LEN -- ran out of enough output space or enough available input - TYPE -- reached end of block code, inflate() to interpret next block - BAD -- error in block data - - Notes: - - - The maximum input bits used by a length/distance pair is 15 bits for the - length code, 5 bits for the length extra, 15 bits for the distance code, - and 13 bits for the distance extra. This totals 48 bits, or six bytes. - Therefore if strm->avail_in >= 6, then there is enough input to avoid - checking for available input while decoding. - - - The maximum bytes that a single length/distance pair can output is 258 - bytes, which is the maximum length that can be coded. inflate_fast() - requires strm->avail_out >= 258 for each loop to avoid checking for - output space. - */ -void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) /* inflate()'s starting value for strm->avail_out */ -{ - struct inflate_state FAR *state; - z_const unsigned char FAR *in; /* local strm->next_in */ - z_const unsigned char FAR *last; /* have enough input while in < last */ - unsigned char FAR *out; /* local strm->next_out */ - unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ - unsigned char FAR *end; /* while out < end, enough space available */ -#ifdef INFLATE_STRICT - unsigned dmax; /* maximum distance from zlib header */ -#endif - unsigned wsize; /* window size or zero if not using window */ - unsigned whave; /* valid bytes in the window */ - unsigned wnext; /* window write index */ - unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ - unsigned long hold; /* local strm->hold */ - unsigned bits; /* local strm->bits */ - code const FAR *lcode; /* local strm->lencode */ - code const FAR *dcode; /* local strm->distcode */ - unsigned lmask; /* mask for first level of length codes */ - unsigned dmask; /* mask for first level of distance codes */ - code here; /* retrieved table entry */ - unsigned op; /* code bits, operation, extra bits, or */ - /* window position, window bytes to copy */ - unsigned len; /* match length, unused bytes */ - unsigned dist; /* match distance */ - unsigned char FAR *from; /* where to copy match from */ - - /* copy state to local variables */ - state = (struct inflate_state FAR *)strm->state; - in = strm->next_in; - last = in + (strm->avail_in - 5); - out = strm->next_out; - beg = out - (start - strm->avail_out); - end = out + (strm->avail_out - 257); -#ifdef INFLATE_STRICT - dmax = state->dmax; -#endif - wsize = state->wsize; - whave = state->whave; - wnext = state->wnext; - window = state->window; - hold = state->hold; - bits = state->bits; - lcode = state->lencode; - dcode = state->distcode; - lmask = (1U << state->lenbits) - 1; - dmask = (1U << state->distbits) - 1; - - /* decode literals and length/distances until end-of-block or not enough - input data or output space */ - do { - if (bits < 15) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - hold += (unsigned long)(*in++) << bits; - bits += 8; - } - here = lcode[hold & lmask]; - dolen: - op = (unsigned)(here.bits); - hold >>= op; - bits -= op; - op = (unsigned)(here.op); - if (op == 0) { /* literal */ - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - *out++ = (unsigned char)(here.val); - } - else if (op & 16) { /* length base */ - len = (unsigned)(here.val); - op &= 15; /* number of extra bits */ - if (op) { - if (bits < op) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - } - len += (unsigned)hold & ((1U << op) - 1); - hold >>= op; - bits -= op; - } - Tracevv((stderr, "inflate: length %u\n", len)); - if (bits < 15) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - hold += (unsigned long)(*in++) << bits; - bits += 8; - } - here = dcode[hold & dmask]; - dodist: - op = (unsigned)(here.bits); - hold >>= op; - bits -= op; - op = (unsigned)(here.op); - if (op & 16) { /* distance base */ - dist = (unsigned)(here.val); - op &= 15; /* number of extra bits */ - if (bits < op) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - if (bits < op) { - hold += (unsigned long)(*in++) << bits; - bits += 8; - } - } - dist += (unsigned)hold & ((1U << op) - 1); -#ifdef INFLATE_STRICT - if (dist > dmax) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } -#endif - hold >>= op; - bits -= op; - Tracevv((stderr, "inflate: distance %u\n", dist)); - op = (unsigned)(out - beg); /* max distance in output */ - if (dist > op) { /* see if copy from window */ - op = dist - op; /* distance back in window */ - if (op > whave) { - if (state->sane) { - strm->msg = - (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } -#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR - if (len <= op - whave) { - do { - *out++ = 0; - } while (--len); - continue; - } - len -= op - whave; - do { - *out++ = 0; - } while (--op > whave); - if (op == 0) { - from = out - dist; - do { - *out++ = *from++; - } while (--len); - continue; - } -#endif - } - from = window; - if (wnext == 0) { /* very common case */ - from += wsize - op; - if (op < len) { /* some from window */ - len -= op; - do { - *out++ = *from++; - } while (--op); - from = out - dist; /* rest from output */ - } - } - else if (wnext < op) { /* wrap around window */ - from += wsize + wnext - op; - op -= wnext; - if (op < len) { /* some from end of window */ - len -= op; - do { - *out++ = *from++; - } while (--op); - from = window; - if (wnext < len) { /* some from start of window */ - op = wnext; - len -= op; - do { - *out++ = *from++; - } while (--op); - from = out - dist; /* rest from output */ - } - } - } - else { /* contiguous in window */ - from += wnext - op; - if (op < len) { /* some from window */ - len -= op; - do { - *out++ = *from++; - } while (--op); - from = out - dist; /* rest from output */ - } - } - while (len > 2) { - *out++ = *from++; - *out++ = *from++; - *out++ = *from++; - len -= 3; - } - if (len) { - *out++ = *from++; - if (len > 1) - *out++ = *from++; - } - } - else { - from = out - dist; /* copy direct from output */ - do { /* minimum length is three */ - *out++ = *from++; - *out++ = *from++; - *out++ = *from++; - len -= 3; - } while (len > 2); - if (len) { - *out++ = *from++; - if (len > 1) - *out++ = *from++; - } - } - } - else if ((op & 64) == 0) { /* 2nd level distance code */ - here = dcode[here.val + (hold & ((1U << op) - 1))]; - goto dodist; - } - else { - strm->msg = (char *)"invalid distance code"; - state->mode = BAD; - break; - } - } - else if ((op & 64) == 0) { /* 2nd level length code */ - here = lcode[here.val + (hold & ((1U << op) - 1))]; - goto dolen; - } - else if (op & 32) { /* end-of-block */ - Tracevv((stderr, "inflate: end of block\n")); - state->mode = TYPE; - break; - } - else { - strm->msg = (char *)"invalid literal/length code"; - state->mode = BAD; - break; - } - } while (in < last && out < end); - - /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ - len = bits >> 3; - in -= len; - bits -= len << 3; - hold &= (1U << bits) - 1; - - /* update state and return */ - strm->next_in = in; - strm->next_out = out; - strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); - strm->avail_out = (unsigned)(out < end ? - 257 + (end - out) : 257 - (out - end)); - state->hold = hold; - state->bits = bits; - return; -} - -/* - inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): - - Using bit fields for code structure - - Different op definition to avoid & for extra bits (do & for table bits) - - Three separate decoding do-loops for direct, window, and wnext == 0 - - Special case for distance > 1 copies to do overlapped load and store copy - - Explicit branch predictions (based on measured branch probabilities) - - Deferring match copy and interspersed it with decoding subsequent codes - - Swapping literal/length else - - Swapping window/direct else - - Larger unrolled copy loops (three is about right) - - Moving len -= 3 statement into middle of loop - */ - -#endif /* !ASMINF */ diff --git a/builtins/zlib/inffast.h b/builtins/zlib/inffast.h deleted file mode 100644 index e5c1aa4ca8cd5..0000000000000 --- a/builtins/zlib/inffast.h +++ /dev/null @@ -1,11 +0,0 @@ -/* inffast.h -- header to use inffast.c - * Copyright (C) 1995-2003, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/builtins/zlib/inffixed.h b/builtins/zlib/inffixed.h deleted file mode 100644 index d628327769480..0000000000000 --- a/builtins/zlib/inffixed.h +++ /dev/null @@ -1,94 +0,0 @@ - /* inffixed.h -- table for decoding fixed codes - * Generated automatically by makefixed(). - */ - - /* WARNING: this file should *not* be used by applications. - It is part of the implementation of this library and is - subject to change. Applications should only use zlib.h. - */ - - static const code lenfix[512] = { - {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, - {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, - {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, - {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, - {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, - {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, - {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, - {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, - {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, - {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, - {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, - {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, - {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, - {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, - {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, - {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, - {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, - {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, - {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, - {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, - {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, - {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, - {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, - {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, - {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, - {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, - {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, - {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, - {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, - {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, - {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, - {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, - {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, - {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, - {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, - {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, - {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, - {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, - {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, - {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, - {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, - {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, - {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, - {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, - {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, - {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, - {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, - {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, - {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, - {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, - {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, - {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, - {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, - {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, - {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, - {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, - {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, - {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, - {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, - {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, - {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, - {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, - {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, - {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, - {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, - {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, - {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, - {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, - {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, - {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, - {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, - {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, - {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, - {0,9,255} - }; - - static const code distfix[32] = { - {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, - {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, - {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, - {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, - {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, - {22,5,193},{64,5,0} - }; diff --git a/builtins/zlib/inflate.c b/builtins/zlib/inflate.c deleted file mode 100644 index 224af7e3a64e7..0000000000000 --- a/builtins/zlib/inflate.c +++ /dev/null @@ -1,1473 +0,0 @@ -/* inflate.c -- zlib decompression - * Copyright (C) 1995-2012 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * Change history: - * - * 1.2.beta0 24 Nov 2002 - * - First version -- complete rewrite of inflate to simplify code, avoid - * creation of window when not needed, minimize use of window when it is - * needed, make inffast.c even faster, implement gzip decoding, and to - * improve code readability and style over the previous zlib inflate code - * - * 1.2.beta1 25 Nov 2002 - * - Use pointers for available input and output checking in inffast.c - * - Remove input and output counters in inffast.c - * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 - * - Remove unnecessary second byte pull from length extra in inffast.c - * - Unroll direct copy to three copies per loop in inffast.c - * - * 1.2.beta2 4 Dec 2002 - * - Change external routine names to reduce potential conflicts - * - Correct filename to inffixed.h for fixed tables in inflate.c - * - Make hbuf[] unsigned char to match parameter type in inflate.c - * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) - * to avoid negation problem on Alphas (64 bit) in inflate.c - * - * 1.2.beta3 22 Dec 2002 - * - Add comments on state->bits assertion in inffast.c - * - Add comments on op field in inftrees.h - * - Fix bug in reuse of allocated window after inflateReset() - * - Remove bit fields--back to byte structure for speed - * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths - * - Change post-increments to pre-increments in inflate_fast(), PPC biased? - * - Add compile time option, POSTINC, to use post-increments instead (Intel?) - * - Make MATCH copy in inflate() much faster for when inflate_fast() not used - * - Use local copies of stream next and avail values, as well as local bit - * buffer and bit count in inflate()--for speed when inflate_fast() not used - * - * 1.2.beta4 1 Jan 2003 - * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings - * - Move a comment on output buffer sizes from inffast.c to inflate.c - * - Add comments in inffast.c to introduce the inflate_fast() routine - * - Rearrange window copies in inflate_fast() for speed and simplification - * - Unroll last copy for window match in inflate_fast() - * - Use local copies of window variables in inflate_fast() for speed - * - Pull out common wnext == 0 case for speed in inflate_fast() - * - Make op and len in inflate_fast() unsigned for consistency - * - Add FAR to lcode and dcode declarations in inflate_fast() - * - Simplified bad distance check in inflate_fast() - * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new - * source file infback.c to provide a call-back interface to inflate for - * programs like gzip and unzip -- uses window as output buffer to avoid - * window copying - * - * 1.2.beta5 1 Jan 2003 - * - Improved inflateBack() interface to allow the caller to provide initial - * input in strm. - * - Fixed stored blocks bug in inflateBack() - * - * 1.2.beta6 4 Jan 2003 - * - Added comments in inffast.c on effectiveness of POSTINC - * - Typecasting all around to reduce compiler warnings - * - Changed loops from while (1) or do {} while (1) to for (;;), again to - * make compilers happy - * - Changed type of window in inflateBackInit() to unsigned char * - * - * 1.2.beta7 27 Jan 2003 - * - Changed many types to unsigned or unsigned short to avoid warnings - * - Added inflateCopy() function - * - * 1.2.0 9 Mar 2003 - * - Changed inflateBack() interface to provide separate opaque descriptors - * for the in() and out() functions - * - Changed inflateBack() argument and in_func typedef to swap the length - * and buffer address return values for the input function - * - Check next_in and next_out for Z_NULL on entry to inflate() - * - * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. - */ - -#include "zutil.h" -#include "inftrees.h" -#include "inflate.h" -#include "inffast.h" - -#ifdef MAKEFIXED -# ifndef BUILDFIXED -# define BUILDFIXED -# endif -#endif - -/* function prototypes */ -local void fixedtables OF((struct inflate_state FAR *state)); -local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, - unsigned copy)); -#ifdef BUILDFIXED - void makefixed OF((void)); -#endif -local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, - unsigned len)); - -int ZEXPORT inflateResetKeep(z_streamp strm) -{ - struct inflate_state FAR *state; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - strm->total_in = strm->total_out = state->total = 0; - strm->msg = Z_NULL; - if (state->wrap) /* to support ill-conceived Java test suite */ - strm->adler = state->wrap & 1; - state->mode = HEAD; - state->last = 0; - state->havedict = 0; - state->dmax = 32768U; - state->head = Z_NULL; - state->hold = 0; - state->bits = 0; - state->lencode = state->distcode = state->next = state->codes; - state->sane = 1; - state->back = -1; - Tracev((stderr, "inflate: reset\n")); - return Z_OK; -} - -int ZEXPORT inflateReset(z_streamp strm) -{ - struct inflate_state FAR *state; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - state->wsize = 0; - state->whave = 0; - state->wnext = 0; - return inflateResetKeep(strm); -} - -int ZEXPORT inflateReset2(z_streamp strm, int windowBits) -{ - int wrap; - struct inflate_state FAR *state; - - /* get the state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - - /* extract wrap request from windowBits parameter */ - if (windowBits < 0) { - wrap = 0; - windowBits = -windowBits; - } - else { - wrap = (windowBits >> 4) + 1; -#ifdef GUNZIP - if (windowBits < 48) - windowBits &= 15; -#endif - } - - /* set number of window bits, free window if different */ - if (windowBits && (windowBits < 8 || windowBits > 15)) - return Z_STREAM_ERROR; - if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { - ZFREE(strm, state->window); - state->window = Z_NULL; - } - - /* update state and reset the rest of it */ - state->wrap = wrap; - state->wbits = (unsigned)windowBits; - return inflateReset(strm); -} - -int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, const char *version, int stream_size) -{ - int ret; - struct inflate_state FAR *state; - - if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || - stream_size != (int)(sizeof(z_stream))) - return Z_VERSION_ERROR; - if (strm == Z_NULL) return Z_STREAM_ERROR; - strm->msg = Z_NULL; /* in case we return an error */ - if (strm->zalloc == (alloc_func)0) { -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zalloc = zcalloc; - strm->opaque = (voidpf)0; -#endif - } - if (strm->zfree == (free_func)0) -#ifdef Z_SOLO - return Z_STREAM_ERROR; -#else - strm->zfree = zcfree; -#endif - state = (struct inflate_state FAR *) - ZALLOC(strm, 1, sizeof(struct inflate_state)); - if (state == Z_NULL) return Z_MEM_ERROR; - Tracev((stderr, "inflate: allocated\n")); - strm->state = (struct internal_state FAR *)state; - state->window = Z_NULL; - ret = inflateReset2(strm, windowBits); - if (ret != Z_OK) { - ZFREE(strm, state); - strm->state = Z_NULL; - } - return ret; -} - -int ZEXPORT inflateInit_(z_streamp strm, const char *version, int stream_size) -{ - return inflateInit2_(strm, DEF_WBITS, version, stream_size); -} - -int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) -{ - struct inflate_state FAR *state; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if (bits < 0) { - state->hold = 0; - state->bits = 0; - return Z_OK; - } - if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; - value &= (1L << bits) - 1; - state->hold += value << state->bits; - state->bits += bits; - return Z_OK; -} - -/* - Return state with length and distance decoding tables and index sizes set to - fixed code decoding. Normally this returns fixed tables from inffixed.h. - If BUILDFIXED is defined, then instead this routine builds the tables the - first time it's called, and returns those tables the first time and - thereafter. This reduces the size of the code by about 2K bytes, in - exchange for a little execution time. However, BUILDFIXED should not be - used for threaded applications, since the rewriting of the tables and virgin - may not be thread-safe. - */ -local void fixedtables(struct inflate_state FAR *state) -{ -#ifdef BUILDFIXED - static int virgin = 1; - static code *lenfix, *distfix; - static code fixed[544]; - - /* build fixed huffman tables if first call (may not be thread safe) */ - if (virgin) { - unsigned sym, bits; - static code *next; - - /* literal/length table */ - sym = 0; - while (sym < 144) state->lens[sym++] = 8; - while (sym < 256) state->lens[sym++] = 9; - while (sym < 280) state->lens[sym++] = 7; - while (sym < 288) state->lens[sym++] = 8; - next = fixed; - lenfix = next; - bits = 9; - inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); - - /* distance table */ - sym = 0; - while (sym < 32) state->lens[sym++] = 5; - distfix = next; - bits = 5; - inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); - - /* do this just once */ - virgin = 0; - } -#else /* !BUILDFIXED */ -# include "inffixed.h" -#endif /* BUILDFIXED */ - state->lencode = lenfix; - state->lenbits = 9; - state->distcode = distfix; - state->distbits = 5; -} - -#ifdef MAKEFIXED -#include - -/* - Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also - defines BUILDFIXED, so the tables are built on the fly. makefixed() writes - those tables to stdout, which would be piped to inffixed.h. A small program - can simply call makefixed to do this: - - void makefixed(void); - - int main(void) - { - makefixed(); - return 0; - } - - Then that can be linked with zlib built with MAKEFIXED defined and run: - - a.out > inffixed.h - */ -void makefixed() -{ - unsigned low, size; - struct inflate_state state; - - fixedtables(&state); - puts(" /* inffixed.h -- table for decoding fixed codes"); - puts(" * Generated automatically by makefixed()."); - puts(" */"); - puts(""); - puts(" /* WARNING: this file should *not* be used by applications."); - puts(" It is part of the implementation of this library and is"); - puts(" subject to change. Applications should only use zlib.h."); - puts(" */"); - puts(""); - size = 1U << 9; - printf(" static const code lenfix[%u] = {", size); - low = 0; - for (;;) { - if ((low % 7) == 0) printf("\n "); - printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, - state.lencode[low].bits, state.lencode[low].val); - if (++low == size) break; - putchar(','); - } - puts("\n };"); - size = 1U << 5; - printf("\n static const code distfix[%u] = {", size); - low = 0; - for (;;) { - if ((low % 6) == 0) printf("\n "); - printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, - state.distcode[low].val); - if (++low == size) break; - putchar(','); - } - puts("\n };"); -} -#endif /* MAKEFIXED */ - -/* - Update the window with the last wsize (normally 32K) bytes written before - returning. If window does not exist yet, create it. This is only called - when a window is already in use, or when output has been written during this - inflate call, but the end of the deflate stream has not been reached yet. - It is also called to create a window for dictionary data when a dictionary - is loaded. - - Providing output buffers larger than 32K to inflate() should provide a speed - advantage, since only the last 32K of output is copied to the sliding window - upon return from inflate(), and since all distances after the first 32K of - output will fall in the output data, making match copies simpler and faster. - The advantage may be dependent on the size of the processor's data caches. - */ -local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) -{ - struct inflate_state FAR *state; - unsigned dist; - - state = (struct inflate_state FAR *)strm->state; - - /* if it hasn't been done already, allocate space for the window */ - if (state->window == Z_NULL) { - state->window = (unsigned char FAR *) - ZALLOC(strm, 1U << state->wbits, - sizeof(unsigned char)); - if (state->window == Z_NULL) return 1; - } - - /* if window not in use yet, initialize */ - if (state->wsize == 0) { - state->wsize = 1U << state->wbits; - state->wnext = 0; - state->whave = 0; - } - - /* copy state->wsize or less output bytes into the circular window */ - if (copy >= state->wsize) { - zmemcpy(state->window, end - state->wsize, state->wsize); - state->wnext = 0; - state->whave = state->wsize; - } - else { - dist = state->wsize - state->wnext; - if (dist > copy) dist = copy; - zmemcpy(state->window + state->wnext, end - copy, dist); - copy -= dist; - if (copy) { - zmemcpy(state->window, end - copy, copy); - state->wnext = copy; - state->whave = state->wsize; - } - else { - state->wnext += dist; - if (state->wnext == state->wsize) state->wnext = 0; - if (state->whave < state->wsize) state->whave += dist; - } - } - return 0; -} - -/* Macros for inflate(): */ - -/* check function to use adler32() for zlib or crc32() for gzip */ -#ifdef GUNZIP -# define UPDATE(check, buf, len) \ - (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) -#else -# define UPDATE(check, buf, len) adler32(check, buf, len) -#endif - -/* check macros for header crc */ -#ifdef GUNZIP -# define CRC2(check, word) \ - do { \ - hbuf[0] = (unsigned char)(word); \ - hbuf[1] = (unsigned char)((word) >> 8); \ - check = crc32(check, hbuf, 2); \ - } while (0) - -# define CRC4(check, word) \ - do { \ - hbuf[0] = (unsigned char)(word); \ - hbuf[1] = (unsigned char)((word) >> 8); \ - hbuf[2] = (unsigned char)((word) >> 16); \ - hbuf[3] = (unsigned char)((word) >> 24); \ - check = crc32(check, hbuf, 4); \ - } while (0) -#endif - -/* Load registers with state in inflate() for speed */ -#define LOAD() \ - do { \ - put = strm->next_out; \ - left = strm->avail_out; \ - next = strm->next_in; \ - have = strm->avail_in; \ - hold = state->hold; \ - bits = state->bits; \ - } while (0) - -/* Restore state from registers in inflate() */ -#define RESTORE() \ - do { \ - strm->next_out = put; \ - strm->avail_out = left; \ - strm->next_in = next; \ - strm->avail_in = have; \ - state->hold = hold; \ - state->bits = bits; \ - } while (0) - -/* Clear the input bit accumulator */ -#define INITBITS() \ - do { \ - hold = 0; \ - bits = 0; \ - } while (0) - -/* Get a byte of input into the bit accumulator, or return from inflate() - if there is no input available. */ -#define PULLBYTE() \ - do { \ - if (have == 0) goto inf_leave; \ - have--; \ - hold += (unsigned long)(*next++) << bits; \ - bits += 8; \ - } while (0) - -/* Assure that there are at least n bits in the bit accumulator. If there is - not enough available input to do that, then return from inflate(). */ -#define NEEDBITS(n) \ - do { \ - while (bits < (unsigned)(n)) \ - PULLBYTE(); \ - } while (0) - -/* Return the low n bits of the bit accumulator (n < 16) */ -#define BITS(n) \ - ((unsigned)hold & ((1U << (n)) - 1)) - -/* Remove n bits from the bit accumulator */ -#define DROPBITS(n) \ - do { \ - hold >>= (n); \ - bits -= (unsigned)(n); \ - } while (0) - -/* Remove zero to seven bits as needed to go to a byte boundary */ -#define BYTEBITS() \ - do { \ - hold >>= bits & 7; \ - bits -= bits & 7; \ - } while (0) - -/* - inflate() uses a state machine to process as much input data and generate as - much output data as possible before returning. The state machine is - structured roughly as follows: - - for (;;) switch (state) { - ... - case STATEn: - if (not enough input data or output space to make progress) - return; - ... make progress ... - state = STATEm; - break; - ... - } - - so when inflate() is called again, the same case is attempted again, and - if the appropriate resources are provided, the machine proceeds to the - next state. The NEEDBITS() macro is usually the way the state evaluates - whether it can proceed or should return. NEEDBITS() does the return if - the requested bits are not available. The typical use of the BITS macros - is: - - NEEDBITS(n); - ... do something with BITS(n) ... - DROPBITS(n); - - where NEEDBITS(n) either returns from inflate() if there isn't enough - input left to load n bits into the accumulator, or it continues. BITS(n) - gives the low n bits in the accumulator. When done, DROPBITS(n) drops - the low n bits off the accumulator. INITBITS() clears the accumulator - and sets the number of available bits to zero. BYTEBITS() discards just - enough bits to put the accumulator on a byte boundary. After BYTEBITS() - and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. - - NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return - if there is no input available. The decoding of variable length codes uses - PULLBYTE() directly in order to pull just enough bytes to decode the next - code, and no more. - - Some states loop until they get enough input, making sure that enough - state information is maintained to continue the loop where it left off - if NEEDBITS() returns in the loop. For example, want, need, and keep - would all have to actually be part of the saved state in case NEEDBITS() - returns: - - case STATEw: - while (want < need) { - NEEDBITS(n); - keep[want++] = BITS(n); - DROPBITS(n); - } - state = STATEx; - case STATEx: - - As shown above, if the next state is also the next case, then the break - is omitted. - - A state may also return if there is not enough output space available to - complete that state. Those states are copying stored data, writing a - literal byte, and copying a matching string. - - When returning, a "goto inf_leave" is used to update the total counters, - update the check value, and determine whether any progress has been made - during that inflate() call in order to return the proper return code. - Progress is defined as a change in either strm->avail_in or strm->avail_out. - When there is a window, goto inf_leave will update the window with the last - output written. If a goto inf_leave occurs in the middle of decompression - and there is no window currently, goto inf_leave will create one and copy - output to the window for the next call of inflate(). - - In this implementation, the flush parameter of inflate() only affects the - return code (per zlib.h). inflate() always writes as much as possible to - strm->next_out, given the space available and the provided input--the effect - documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers - the allocation of and copying into a sliding window until necessary, which - provides the effect documented in zlib.h for Z_FINISH when the entire input - stream available. So the only thing the flush parameter actually does is: - when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it - will return Z_BUF_ERROR if it has not reached the end of the stream. - */ - -int ZEXPORT inflate(z_streamp strm, int flush) -{ - struct inflate_state FAR *state; - z_const unsigned char FAR *next; /* next input */ - unsigned char FAR *put; /* next output */ - unsigned have, left; /* available input and output */ - unsigned long hold; /* bit buffer */ - unsigned bits; /* bits in bit buffer */ - unsigned in, out; /* save starting available input and output */ - unsigned copy; /* number of stored or match bytes to copy */ - unsigned char FAR *from; /* where to copy match bytes from */ - code here; /* current decoding table entry */ - code last; /* parent table entry */ - unsigned len; /* length to copy for repeats, bits to drop */ - int ret; /* return code */ -#ifdef GUNZIP - unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ -#endif - static const unsigned short order[19] = /* permutation of code lengths */ - {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - - if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || - (strm->next_in == Z_NULL && strm->avail_in != 0)) - return Z_STREAM_ERROR; - - state = (struct inflate_state FAR *)strm->state; - if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ - LOAD(); - in = have; - out = left; - ret = Z_OK; - for (;;) - switch (state->mode) { - case HEAD: - if (state->wrap == 0) { - state->mode = TYPEDO; - break; - } - NEEDBITS(16); -#ifdef GUNZIP - if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ - state->check = crc32(0L, Z_NULL, 0); - CRC2(state->check, hold); - INITBITS(); - state->mode = FLAGS; - break; - } - state->flags = 0; /* expect zlib header */ - if (state->head != Z_NULL) - state->head->done = -1; - if (!(state->wrap & 1) || /* check if zlib header allowed */ -#else - if ( -#endif - ((BITS(8) << 8) + (hold >> 8)) % 31) { - strm->msg = (char *)"incorrect header check"; - state->mode = BAD; - break; - } - if (BITS(4) != Z_DEFLATED) { - strm->msg = (char *)"unknown compression method"; - state->mode = BAD; - break; - } - DROPBITS(4); - len = BITS(4) + 8; - if (state->wbits == 0) - state->wbits = len; - else if (len > state->wbits) { - strm->msg = (char *)"invalid window size"; - state->mode = BAD; - break; - } - state->dmax = 1U << len; - Tracev((stderr, "inflate: zlib header ok\n")); - strm->adler = state->check = adler32(0L, Z_NULL, 0); - state->mode = hold & 0x200 ? DICTID : TYPE; - INITBITS(); - break; -#ifdef GUNZIP - case FLAGS: - NEEDBITS(16); - state->flags = (int)(hold); - if ((state->flags & 0xff) != Z_DEFLATED) { - strm->msg = (char *)"unknown compression method"; - state->mode = BAD; - break; - } - if (state->flags & 0xe000) { - strm->msg = (char *)"unknown header flags set"; - state->mode = BAD; - break; - } - if (state->head != Z_NULL) - state->head->text = (int)((hold >> 8) & 1); - if (state->flags & 0x0200) CRC2(state->check, hold); - INITBITS(); - state->mode = TIME; - case TIME: - NEEDBITS(32); - if (state->head != Z_NULL) - state->head->time = hold; - if (state->flags & 0x0200) CRC4(state->check, hold); - INITBITS(); - state->mode = OS; - case OS: - NEEDBITS(16); - if (state->head != Z_NULL) { - state->head->xflags = (int)(hold & 0xff); - state->head->os = (int)(hold >> 8); - } - if (state->flags & 0x0200) CRC2(state->check, hold); - INITBITS(); - state->mode = EXLEN; - case EXLEN: - if (state->flags & 0x0400) { - NEEDBITS(16); - state->length = (unsigned)(hold); - if (state->head != Z_NULL) - state->head->extra_len = (unsigned)hold; - if (state->flags & 0x0200) CRC2(state->check, hold); - INITBITS(); - } - else if (state->head != Z_NULL) - state->head->extra = Z_NULL; - state->mode = EXTRA; - case EXTRA: - if (state->flags & 0x0400) { - copy = state->length; - if (copy > have) copy = have; - if (copy) { - if (state->head != Z_NULL && - state->head->extra != Z_NULL) { - len = state->head->extra_len - state->length; - zmemcpy(state->head->extra + len, next, - len + copy > state->head->extra_max ? - state->head->extra_max - len : copy); - } - if (state->flags & 0x0200) - state->check = crc32(state->check, next, copy); - have -= copy; - next += copy; - state->length -= copy; - } - if (state->length) goto inf_leave; - } - state->length = 0; - state->mode = NAME; - case NAME: - if (state->flags & 0x0800) { - if (have == 0) goto inf_leave; - copy = 0; - do { - len = (unsigned)(next[copy++]); - if (state->head != Z_NULL && - state->head->name != Z_NULL && - state->length < state->head->name_max) - state->head->name[state->length++] = len; - } while (len && copy < have); - if (state->flags & 0x0200) - state->check = crc32(state->check, next, copy); - have -= copy; - next += copy; - if (len) goto inf_leave; - } - else if (state->head != Z_NULL) - state->head->name = Z_NULL; - state->length = 0; - state->mode = COMMENT; - case COMMENT: - if (state->flags & 0x1000) { - if (have == 0) goto inf_leave; - copy = 0; - do { - len = (unsigned)(next[copy++]); - if (state->head != Z_NULL && - state->head->comment != Z_NULL && - state->length < state->head->comm_max) - state->head->comment[state->length++] = len; - } while (len && copy < have); - if (state->flags & 0x0200) - state->check = crc32(state->check, next, copy); - have -= copy; - next += copy; - if (len) goto inf_leave; - } - else if (state->head != Z_NULL) - state->head->comment = Z_NULL; - state->mode = HCRC; - case HCRC: - if (state->flags & 0x0200) { - NEEDBITS(16); - if (hold != (state->check & 0xffff)) { - strm->msg = (char *)"header crc mismatch"; - state->mode = BAD; - break; - } - INITBITS(); - } - if (state->head != Z_NULL) { - state->head->hcrc = (int)((state->flags >> 9) & 1); - state->head->done = 1; - } - strm->adler = state->check = crc32(0L, Z_NULL, 0); - state->mode = TYPE; - break; -#endif - case DICTID: - NEEDBITS(32); - strm->adler = state->check = ZSWAP32(hold); - INITBITS(); - state->mode = DICT; - case DICT: - if (state->havedict == 0) { - RESTORE(); - return Z_NEED_DICT; - } - strm->adler = state->check = adler32(0L, Z_NULL, 0); - state->mode = TYPE; - case TYPE: - if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; - case TYPEDO: - if (state->last) { - BYTEBITS(); - state->mode = CHECK; - break; - } - NEEDBITS(3); - state->last = BITS(1); - DROPBITS(1); - switch (BITS(2)) { - case 0: /* stored block */ - Tracev((stderr, "inflate: stored block%s\n", - state->last ? " (last)" : "")); - state->mode = STORED; - break; - case 1: /* fixed block */ - fixedtables(state); - Tracev((stderr, "inflate: fixed codes block%s\n", - state->last ? " (last)" : "")); - state->mode = LEN_; /* decode codes */ - if (flush == Z_TREES) { - DROPBITS(2); - goto inf_leave; - } - break; - case 2: /* dynamic block */ - Tracev((stderr, "inflate: dynamic codes block%s\n", - state->last ? " (last)" : "")); - state->mode = TABLE; - break; - case 3: - strm->msg = (char *)"invalid block type"; - state->mode = BAD; - } - DROPBITS(2); - break; - case STORED: - BYTEBITS(); /* go to byte boundary */ - NEEDBITS(32); - if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { - strm->msg = (char *)"invalid stored block lengths"; - state->mode = BAD; - break; - } - state->length = (unsigned)hold & 0xffff; - Tracev((stderr, "inflate: stored length %u\n", - state->length)); - INITBITS(); - state->mode = COPY_; - if (flush == Z_TREES) goto inf_leave; - case COPY_: - state->mode = COPY; - case COPY: - copy = state->length; - if (copy) { - if (copy > have) copy = have; - if (copy > left) copy = left; - if (copy == 0) goto inf_leave; - zmemcpy(put, next, copy); - have -= copy; - next += copy; - left -= copy; - put += copy; - state->length -= copy; - break; - } - Tracev((stderr, "inflate: stored end\n")); - state->mode = TYPE; - break; - case TABLE: - NEEDBITS(14); - state->nlen = BITS(5) + 257; - DROPBITS(5); - state->ndist = BITS(5) + 1; - DROPBITS(5); - state->ncode = BITS(4) + 4; - DROPBITS(4); -#ifndef PKZIP_BUG_WORKAROUND - if (state->nlen > 286 || state->ndist > 30) { - strm->msg = (char *)"too many length or distance symbols"; - state->mode = BAD; - break; - } -#endif - Tracev((stderr, "inflate: table sizes ok\n")); - state->have = 0; - state->mode = LENLENS; - case LENLENS: - while (state->have < state->ncode) { - NEEDBITS(3); - state->lens[order[state->have++]] = (unsigned short)BITS(3); - DROPBITS(3); - } - while (state->have < 19) - state->lens[order[state->have++]] = 0; - state->next = state->codes; - state->lencode = (const code FAR *)(state->next); - state->lenbits = 7; - ret = inflate_table(CODES, state->lens, 19, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid code lengths set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: code lengths ok\n")); - state->have = 0; - state->mode = CODELENS; - case CODELENS: - while (state->have < state->nlen + state->ndist) { - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.val < 16) { - DROPBITS(here.bits); - state->lens[state->have++] = here.val; - } - else { - if (here.val == 16) { - NEEDBITS(here.bits + 2); - DROPBITS(here.bits); - if (state->have == 0) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - len = state->lens[state->have - 1]; - copy = 3 + BITS(2); - DROPBITS(2); - } - else if (here.val == 17) { - NEEDBITS(here.bits + 3); - DROPBITS(here.bits); - len = 0; - copy = 3 + BITS(3); - DROPBITS(3); - } - else { - NEEDBITS(here.bits + 7); - DROPBITS(here.bits); - len = 0; - copy = 11 + BITS(7); - DROPBITS(7); - } - if (state->have + copy > state->nlen + state->ndist) { - strm->msg = (char *)"invalid bit length repeat"; - state->mode = BAD; - break; - } - while (copy--) - state->lens[state->have++] = (unsigned short)len; - } - } - - /* handle error breaks in while */ - if (state->mode == BAD) break; - - /* check for end-of-block code (better have one) */ - if (state->lens[256] == 0) { - strm->msg = (char *)"invalid code -- missing end-of-block"; - state->mode = BAD; - break; - } - - /* build code tables -- note: do not change the lenbits or distbits - values here (9 and 6) without reading the comments in inftrees.h - concerning the ENOUGH constants, which depend on those values */ - state->next = state->codes; - state->lencode = (const code FAR *)(state->next); - state->lenbits = 9; - ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), - &(state->lenbits), state->work); - if (ret) { - strm->msg = (char *)"invalid literal/lengths set"; - state->mode = BAD; - break; - } - state->distcode = (const code FAR *)(state->next); - state->distbits = 6; - ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, - &(state->next), &(state->distbits), state->work); - if (ret) { - strm->msg = (char *)"invalid distances set"; - state->mode = BAD; - break; - } - Tracev((stderr, "inflate: codes ok\n")); - state->mode = LEN_; - if (flush == Z_TREES) goto inf_leave; - case LEN_: - state->mode = LEN; - case LEN: - if (have >= 6 && left >= 258) { - RESTORE(); - inflate_fast(strm, out); - LOAD(); - if (state->mode == TYPE) - state->back = -1; - break; - } - state->back = 0; - for (;;) { - here = state->lencode[BITS(state->lenbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if (here.op && (here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->lencode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - state->back += last.bits; - } - DROPBITS(here.bits); - state->back += here.bits; - state->length = (unsigned)here.val; - if ((int)(here.op) == 0) { - Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? - "inflate: literal '%c'\n" : - "inflate: literal 0x%02x\n", here.val)); - state->mode = LIT; - break; - } - if (here.op & 32) { - Tracevv((stderr, "inflate: end of block\n")); - state->back = -1; - state->mode = TYPE; - break; - } - if (here.op & 64) { - strm->msg = (char *)"invalid literal/length code"; - state->mode = BAD; - break; - } - state->extra = (unsigned)(here.op) & 15; - state->mode = LENEXT; - case LENEXT: - if (state->extra) { - NEEDBITS(state->extra); - state->length += BITS(state->extra); - DROPBITS(state->extra); - state->back += state->extra; - } - Tracevv((stderr, "inflate: length %u\n", state->length)); - state->was = state->length; - state->mode = DIST; - case DIST: - for (;;) { - here = state->distcode[BITS(state->distbits)]; - if ((unsigned)(here.bits) <= bits) break; - PULLBYTE(); - } - if ((here.op & 0xf0) == 0) { - last = here; - for (;;) { - here = state->distcode[last.val + - (BITS(last.bits + last.op) >> last.bits)]; - if ((unsigned)(last.bits + here.bits) <= bits) break; - PULLBYTE(); - } - DROPBITS(last.bits); - state->back += last.bits; - } - DROPBITS(here.bits); - state->back += here.bits; - if (here.op & 64) { - strm->msg = (char *)"invalid distance code"; - state->mode = BAD; - break; - } - state->offset = (unsigned)here.val; - state->extra = (unsigned)(here.op) & 15; - state->mode = DISTEXT; - case DISTEXT: - if (state->extra) { - NEEDBITS(state->extra); - state->offset += BITS(state->extra); - DROPBITS(state->extra); - state->back += state->extra; - } -#ifdef INFLATE_STRICT - if (state->offset > state->dmax) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } -#endif - Tracevv((stderr, "inflate: distance %u\n", state->offset)); - state->mode = MATCH; - case MATCH: - if (left == 0) goto inf_leave; - copy = out - left; - if (state->offset > copy) { /* copy from window */ - copy = state->offset - copy; - if (copy > state->whave) { - if (state->sane) { - strm->msg = (char *)"invalid distance too far back"; - state->mode = BAD; - break; - } -#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR - Trace((stderr, "inflate.c too far\n")); - copy -= state->whave; - if (copy > state->length) copy = state->length; - if (copy > left) copy = left; - left -= copy; - state->length -= copy; - do { - *put++ = 0; - } while (--copy); - if (state->length == 0) state->mode = LEN; - break; -#endif - } - if (copy > state->wnext) { - copy -= state->wnext; - from = state->window + (state->wsize - copy); - } - else - from = state->window + (state->wnext - copy); - if (copy > state->length) copy = state->length; - } - else { /* copy from output */ - from = put - state->offset; - copy = state->length; - } - if (copy > left) copy = left; - left -= copy; - state->length -= copy; - do { - *put++ = *from++; - } while (--copy); - if (state->length == 0) state->mode = LEN; - break; - case LIT: - if (left == 0) goto inf_leave; - *put++ = (unsigned char)(state->length); - left--; - state->mode = LEN; - break; - case CHECK: - if (state->wrap) { - NEEDBITS(32); - out -= left; - strm->total_out += out; - state->total += out; - if (out) - strm->adler = state->check = - UPDATE(state->check, put - out, out); - out = left; - if (( -#ifdef GUNZIP - state->flags ? hold : -#endif - ZSWAP32(hold)) != state->check) { - strm->msg = (char *)"incorrect data check"; - state->mode = BAD; - break; - } - INITBITS(); - Tracev((stderr, "inflate: check matches trailer\n")); - } -#ifdef GUNZIP - state->mode = LENGTH; - case LENGTH: - if (state->wrap && state->flags) { - NEEDBITS(32); - if (hold != (state->total & 0xffffffffUL)) { - strm->msg = (char *)"incorrect length check"; - state->mode = BAD; - break; - } - INITBITS(); - Tracev((stderr, "inflate: length matches trailer\n")); - } -#endif - state->mode = DONE; - case DONE: - ret = Z_STREAM_END; - goto inf_leave; - case BAD: - ret = Z_DATA_ERROR; - goto inf_leave; - case MEM: - return Z_MEM_ERROR; - case SYNC: - default: - return Z_STREAM_ERROR; - } - - /* - Return from inflate(), updating the total counts and the check value. - If there was no progress during the inflate() call, return a buffer - error. Call updatewindow() to create and/or update the window state. - Note: a memory error from inflate() is non-recoverable. - */ - inf_leave: - RESTORE(); - if (state->wsize || (out != strm->avail_out && state->mode < BAD && - (state->mode < CHECK || flush != Z_FINISH))) - if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { - state->mode = MEM; - return Z_MEM_ERROR; - } - in -= strm->avail_in; - out -= strm->avail_out; - strm->total_in += in; - strm->total_out += out; - state->total += out; - if (state->wrap && out) - strm->adler = state->check = - UPDATE(state->check, strm->next_out - out, out); - strm->data_type = state->bits + (state->last ? 64 : 0) + - (state->mode == TYPE ? 128 : 0) + - (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); - if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) - ret = Z_BUF_ERROR; - return ret; -} - -int ZEXPORT inflateEnd(z_streamp strm) -{ - struct inflate_state FAR *state; - if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) - return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if (state->window != Z_NULL) ZFREE(strm, state->window); - ZFREE(strm, strm->state); - strm->state = Z_NULL; - Tracev((stderr, "inflate: end\n")); - return Z_OK; -} - -int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, uInt *dictLength) -{ - struct inflate_state FAR *state; - - /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - - /* copy dictionary */ - if (state->whave && dictionary != Z_NULL) { - zmemcpy(dictionary, state->window + state->wnext, - state->whave - state->wnext); - zmemcpy(dictionary + state->whave - state->wnext, - state->window, state->wnext); - } - if (dictLength != Z_NULL) - *dictLength = state->whave; - return Z_OK; -} - -int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, uInt dictLength) -{ - struct inflate_state FAR *state; - unsigned long dictid; - int ret; - - /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if (state->wrap != 0 && state->mode != DICT) - return Z_STREAM_ERROR; - - /* check for correct dictionary identifier */ - if (state->mode == DICT) { - dictid = adler32(0L, Z_NULL, 0); - dictid = adler32(dictid, dictionary, dictLength); - if (dictid != state->check) - return Z_DATA_ERROR; - } - - /* copy dictionary to window using updatewindow(), which will amend the - existing dictionary if appropriate */ - ret = updatewindow(strm, dictionary + dictLength, dictLength); - if (ret) { - state->mode = MEM; - return Z_MEM_ERROR; - } - state->havedict = 1; - Tracev((stderr, "inflate: dictionary set\n")); - return Z_OK; -} - -int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) -{ - struct inflate_state FAR *state; - - /* check state */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; - - /* save header structure */ - state->head = head; - head->done = 0; - return Z_OK; -} - -/* - Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found - or when out of input. When called, *have is the number of pattern bytes - found in order so far, in 0..3. On return *have is updated to the new - state. If on return *have equals four, then the pattern was found and the - return value is how many bytes were read including the last byte of the - pattern. If *have is less than four, then the pattern has not been found - yet and the return value is len. In the latter case, syncsearch() can be - called again with more data and the *have state. *have is initialized to - zero for the first call. - */ -local unsigned syncsearch(unsigned FAR *have, const unsigned char FAR *buf, unsigned len) -{ - unsigned got; - unsigned next; - - got = *have; - next = 0; - while (next < len && got < 4) { - if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) - got++; - else if (buf[next]) - got = 0; - else - got = 4 - got; - next++; - } - *have = got; - return next; -} - -int ZEXPORT inflateSync(z_streamp strm) -{ - unsigned len; /* number of bytes to look at or looked at */ - unsigned long in, out; /* temporary to save total_in and total_out */ - unsigned char buf[4]; /* to restore bit buffer to byte string */ - struct inflate_state FAR *state; - - /* check parameters */ - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; - - /* if first time, start search in bit buffer */ - if (state->mode != SYNC) { - state->mode = SYNC; - state->hold <<= state->bits & 7; - state->bits -= state->bits & 7; - len = 0; - while (state->bits >= 8) { - buf[len++] = (unsigned char)(state->hold); - state->hold >>= 8; - state->bits -= 8; - } - state->have = 0; - syncsearch(&(state->have), buf, len); - } - - /* search available input */ - len = syncsearch(&(state->have), strm->next_in, strm->avail_in); - strm->avail_in -= len; - strm->next_in += len; - strm->total_in += len; - - /* return no joy or set up to restart inflate() on a new block */ - if (state->have != 4) return Z_DATA_ERROR; - in = strm->total_in; out = strm->total_out; - inflateReset(strm); - strm->total_in = in; strm->total_out = out; - state->mode = TYPE; - return Z_OK; -} - -/* - Returns true if inflate is currently at the end of a block generated by - Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP - implementation to provide an additional safety check. PPP uses - Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored - block. When decompressing, PPP checks that at the end of input packet, - inflate is waiting for these length bytes. - */ -int ZEXPORT inflateSyncPoint(z_streamp strm) -{ - struct inflate_state FAR *state; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - return state->mode == STORED && state->bits == 0; -} - -int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) -{ - struct inflate_state FAR *state; - struct inflate_state FAR *copy; - unsigned char FAR *window; - unsigned wsize; - - /* check input */ - if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || - source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) - return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)source->state; - - /* allocate space */ - copy = (struct inflate_state FAR *) - ZALLOC(source, 1, sizeof(struct inflate_state)); - if (copy == Z_NULL) return Z_MEM_ERROR; - window = Z_NULL; - if (state->window != Z_NULL) { - window = (unsigned char FAR *) - ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); - if (window == Z_NULL) { - ZFREE(source, copy); - return Z_MEM_ERROR; - } - } - - /* copy state */ - zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); - zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); - if (state->lencode >= state->codes && - state->lencode <= state->codes + ENOUGH - 1) { - copy->lencode = copy->codes + (state->lencode - state->codes); - copy->distcode = copy->codes + (state->distcode - state->codes); - } - copy->next = copy->codes + (state->next - state->codes); - if (window != Z_NULL) { - wsize = 1U << state->wbits; - zmemcpy(window, state->window, wsize); - } - copy->window = window; - dest->state = (struct internal_state FAR *)copy; - return Z_OK; -} - -int ZEXPORT inflateUndermine(z_streamp strm, int subvert) -{ - struct inflate_state FAR *state; - - if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - state = (struct inflate_state FAR *)strm->state; - state->sane = !subvert; -#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR - return Z_OK; -#else - state->sane = 1; - return Z_DATA_ERROR; -#endif -} - -long ZEXPORT inflateMark(z_streamp strm) -{ - struct inflate_state FAR *state; - - if (strm == Z_NULL || strm->state == Z_NULL) return ULONG_MAX << 16; - state = (struct inflate_state FAR *)strm->state; - return ((long)(state->back) << 16) + - (state->mode == COPY ? state->length : - (state->mode == MATCH ? state->was - state->length : 0)); -} diff --git a/builtins/zlib/inflate.h b/builtins/zlib/inflate.h deleted file mode 100644 index 95f4986d40022..0000000000000 --- a/builtins/zlib/inflate.h +++ /dev/null @@ -1,122 +0,0 @@ -/* inflate.h -- internal inflate state definition - * Copyright (C) 1995-2009 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* define NO_GZIP when compiling if you want to disable gzip header and - trailer decoding by inflate(). NO_GZIP would be used to avoid linking in - the crc code when it is not needed. For shared libraries, gzip decoding - should be left enabled. */ -#ifndef NO_GZIP -# define GUNZIP -#endif - -/* Possible inflate modes between inflate() calls */ -typedef enum { - HEAD, /* i: waiting for magic header */ - FLAGS, /* i: waiting for method and flags (gzip) */ - TIME, /* i: waiting for modification time (gzip) */ - OS, /* i: waiting for extra flags and operating system (gzip) */ - EXLEN, /* i: waiting for extra length (gzip) */ - EXTRA, /* i: waiting for extra bytes (gzip) */ - NAME, /* i: waiting for end of file name (gzip) */ - COMMENT, /* i: waiting for end of comment (gzip) */ - HCRC, /* i: waiting for header crc (gzip) */ - DICTID, /* i: waiting for dictionary check value */ - DICT, /* waiting for inflateSetDictionary() call */ - TYPE, /* i: waiting for type bits, including last-flag bit */ - TYPEDO, /* i: same, but skip check to exit inflate on new block */ - STORED, /* i: waiting for stored size (length and complement) */ - COPY_, /* i/o: same as COPY below, but only first time in */ - COPY, /* i/o: waiting for input or output to copy stored block */ - TABLE, /* i: waiting for dynamic block table lengths */ - LENLENS, /* i: waiting for code length code lengths */ - CODELENS, /* i: waiting for length/lit and distance code lengths */ - LEN_, /* i: same as LEN below, but only first time in */ - LEN, /* i: waiting for length/lit/eob code */ - LENEXT, /* i: waiting for length extra bits */ - DIST, /* i: waiting for distance code */ - DISTEXT, /* i: waiting for distance extra bits */ - MATCH, /* o: waiting for output space to copy string */ - LIT, /* o: waiting for output space to write literal */ - CHECK, /* i: waiting for 32-bit check value */ - LENGTH, /* i: waiting for 32-bit length (gzip) */ - DONE, /* finished check, done -- remain here until reset */ - BAD, /* got a data error -- remain here until reset */ - MEM, /* got an inflate() memory error -- remain here until reset */ - SYNC /* looking for synchronization bytes to restart inflate() */ -} inflate_mode; - -/* - State transitions between above modes - - - (most modes can go to BAD or MEM on error -- not shown for clarity) - - Process header: - HEAD -> (gzip) or (zlib) or (raw) - (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> - HCRC -> TYPE - (zlib) -> DICTID or TYPE - DICTID -> DICT -> TYPE - (raw) -> TYPEDO - Read deflate blocks: - TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK - STORED -> COPY_ -> COPY -> TYPE - TABLE -> LENLENS -> CODELENS -> LEN_ - LEN_ -> LEN - Read deflate codes in fixed or dynamic block: - LEN -> LENEXT or LIT or TYPE - LENEXT -> DIST -> DISTEXT -> MATCH -> LEN - LIT -> LEN - Process trailer: - CHECK -> LENGTH -> DONE - */ - -/* state maintained between inflate() calls. Approximately 10K bytes. */ -struct inflate_state { - inflate_mode mode; /* current inflate mode */ - int last; /* true if processing last block */ - int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ - int havedict; /* true if dictionary provided */ - int flags; /* gzip header method and flags (0 if zlib) */ - unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ - unsigned long check; /* protected copy of check value */ - unsigned long total; /* protected copy of output count */ - gz_headerp head; /* where to save gzip header information */ - /* sliding window */ - unsigned wbits; /* log base 2 of requested window size */ - unsigned wsize; /* window size or zero if not using window */ - unsigned whave; /* valid bytes in the window */ - unsigned wnext; /* window write index */ - unsigned char FAR *window; /* allocated sliding window, if needed */ - /* bit accumulator */ - unsigned long hold; /* input bit accumulator */ - unsigned bits; /* number of bits in "in" */ - /* for string and stored block copying */ - unsigned length; /* literal or length of data to copy */ - unsigned offset; /* distance back to copy string from */ - /* for table and code decoding */ - unsigned extra; /* extra bits needed */ - /* fixed and dynamic code tables */ - code const FAR *lencode; /* starting table for length/literal codes */ - code const FAR *distcode; /* starting table for distance codes */ - unsigned lenbits; /* index bits for lencode */ - unsigned distbits; /* index bits for distcode */ - /* dynamic table building */ - unsigned ncode; /* number of code length code lengths */ - unsigned nlen; /* number of length code lengths */ - unsigned ndist; /* number of distance code lengths */ - unsigned have; /* number of code lengths in lens[] */ - code FAR *next; /* next available space in codes[] */ - unsigned short lens[320]; /* temporary storage for code lengths */ - unsigned short work[288]; /* work area for code table building */ - code codes[ENOUGH]; /* space for code tables */ - int sane; /* if false, allow invalid distance too far */ - int back; /* bits back of last unprocessed length/lit */ - unsigned was; /* initial length of match */ -}; diff --git a/builtins/zlib/inftrees.c b/builtins/zlib/inftrees.c deleted file mode 100644 index c3f587e34afa2..0000000000000 --- a/builtins/zlib/inftrees.c +++ /dev/null @@ -1,298 +0,0 @@ -/* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2013 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include "zutil.h" -#include "inftrees.h" - -#define MAXBITS 15 - -const char inflate_copyright[] = - " inflate 1.2.8.1 Copyright 1995-2013 Mark Adler "; -/* - If you use the zlib library in a product, an acknowledgment is welcome - in the documentation of your product. If for some reason you cannot - include such an acknowledgment, I would appreciate that you keep this - copyright string in the executable of your product. - */ - -/* - Build a set of tables to decode the provided canonical Huffman code. - The code lengths are lens[0..codes-1]. The result starts at *table, - whose indices are 0..2^bits-1. work is a writable array of at least - lens shorts, which is used as a work area. type is the type of code - to be generated, CODES, LENS, or DISTS. On return, zero is success, - -1 is an invalid code, and +1 means that ENOUGH isn't enough. table - on return points to the next available entry's address. bits is the - requested root table index bits, and on return it is the actual root - table index bits. It will differ if the request is greater than the - longest code or if it is less than the shortest code. - */ -int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, unsigned codes, code FAR * FAR *table, unsigned FAR *bits,unsigned short FAR *work) -{ - unsigned len; /* a code's length in bits */ - unsigned sym; /* index of code symbols */ - unsigned min, max; /* minimum and maximum code lengths */ - unsigned root; /* number of index bits for root table */ - unsigned curr; /* number of index bits for current table */ - unsigned drop; /* code bits to drop for sub-table */ - int left; /* number of prefix codes available */ - unsigned used; /* code entries in table used */ - unsigned huff; /* Huffman code */ - unsigned incr; /* for incrementing code, index */ - unsigned fill; /* index for replicating entries */ - unsigned low; /* low bits for current root entry */ - unsigned mask; /* mask for low root bits */ - code here; /* table entry for duplication */ - code FAR *next; /* next available space in table */ - const unsigned short FAR *base; /* base value table to use */ - const unsigned short FAR *extra; /* extra bits table to use */ - unsigned match; /* use base and extra for symbol >= match */ - unsigned short count[MAXBITS+1]; /* number of codes of each length */ - unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ - static const unsigned short lbase[31] = { /* Length codes 257..285 base */ - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, - 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; - static const unsigned short lext[31] = { /* Length codes 257..285 extra */ - 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 198}; - static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577, 0, 0}; - static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ - 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, - 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, - 28, 28, 29, 29, 64, 64}; - - /* - Process a set of code lengths to create a canonical Huffman code. The - code lengths are lens[0..codes-1]. Each length corresponds to the - symbols 0..codes-1. The Huffman code is generated by first sorting the - symbols by length from short to long, and retaining the symbol order - for codes with equal lengths. Then the code starts with all zero bits - for the first code of the shortest length, and the codes are integer - increments for the same length, and zeros are appended as the length - increases. For the deflate format, these bits are stored backwards - from their more natural integer increment ordering, and so when the - decoding tables are built in the large loop below, the integer codes - are incremented backwards. - - This routine assumes, but does not check, that all of the entries in - lens[] are in the range 0..MAXBITS. The caller must assure this. - 1..MAXBITS is interpreted as that code length. zero means that that - symbol does not occur in this code. - - The codes are sorted by computing a count of codes for each length, - creating from that a table of starting indices for each length in the - sorted table, and then entering the symbols in order in the sorted - table. The sorted table is work[], with that space being provided by - the caller. - - The length counts are used for other purposes as well, i.e. finding - the minimum and maximum length codes, determining if there are any - codes at all, checking for a valid set of lengths, and looking ahead - at length counts to determine sub-table sizes when building the - decoding tables. - */ - - /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ - for (len = 0; len <= MAXBITS; len++) - count[len] = 0; - for (sym = 0; sym < codes; sym++) - count[lens[sym]]++; - - /* bound code lengths, force root to be within code lengths */ - root = *bits; - for (max = MAXBITS; max >= 1; max--) - if (count[max] != 0) break; - if (root > max) root = max; - if (max == 0) { /* no symbols to code at all */ - here.op = (unsigned char)64; /* invalid code marker */ - here.bits = (unsigned char)1; - here.val = (unsigned short)0; - *(*table)++ = here; /* make a table to force an error */ - *(*table)++ = here; - *bits = 1; - return 0; /* no symbols, but wait for decoding to report error */ - } - for (min = 1; min < max; min++) - if (count[min] != 0) break; - if (root < min) root = min; - - /* check for an over-subscribed or incomplete set of lengths */ - left = 1; - for (len = 1; len <= MAXBITS; len++) { - left <<= 1; - left -= count[len]; - if (left < 0) return -1; /* over-subscribed */ - } - if (left > 0 && (type == CODES || max != 1)) - return -1; /* incomplete set */ - - /* generate offsets into symbol table for each length for sorting */ - offs[1] = 0; - for (len = 1; len < MAXBITS; len++) - offs[len + 1] = offs[len] + count[len]; - - /* sort symbols by length, by symbol order within each length */ - for (sym = 0; sym < codes; sym++) - if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; - - /* - Create and fill in decoding tables. In this loop, the table being - filled is at next and has curr index bits. The code being used is huff - with length len. That code is converted to an index by dropping drop - bits off of the bottom. For codes where len is less than drop + curr, - those top drop + curr - len bits are incremented through all values to - fill the table with replicated entries. - - root is the number of index bits for the root table. When len exceeds - root, sub-tables are created pointed to by the root entry with an index - of the low root bits of huff. This is saved in low to check for when a - new sub-table should be started. drop is zero when the root table is - being filled, and drop is root when sub-tables are being filled. - - When a new sub-table is needed, it is necessary to look ahead in the - code lengths to determine what size sub-table is needed. The length - counts are used for this, and so count[] is decremented as codes are - entered in the tables. - - used keeps track of how many table entries have been allocated from the - provided *table space. It is checked for LENS and DIST tables against - the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in - the initial root table size constants. See the comments in inftrees.h - for more information. - - sym increments through all symbols, and the loop terminates when - all codes of length max, i.e. all codes, have been processed. This - routine permits incomplete codes, so another loop after this one fills - in the rest of the decoding tables with invalid code markers. - */ - - /* set up for code type */ - switch (type) { - case CODES: - base = extra = work; /* dummy value--not used */ - match = 20; - break; - case LENS: - base = lbase; - extra = lext; - match = 257; - break; - default: /* DISTS */ - base = dbase; - extra = dext; - match = 0; - } - - /* initialize state for loop */ - huff = 0; /* starting code */ - sym = 0; /* starting code symbol */ - len = min; /* starting code length */ - next = *table; /* current table to fill in */ - curr = root; /* current table index bits */ - drop = 0; /* current bits to drop from code for index */ - low = (unsigned)(-1); /* trigger new sub-table when len > root */ - used = 1U << root; /* use root table entries */ - mask = used - 1; /* mask for comparing low */ - - /* check available table space */ - if ((type == LENS && used > ENOUGH_LENS) || - (type == DISTS && used > ENOUGH_DISTS)) - return 1; - - /* process all codes and make table entries */ - for (;;) { - /* create table entry */ - here.bits = (unsigned char)(len - drop); - if (work[sym] + 1 < match) { - here.op = (unsigned char)0; - here.val = work[sym]; - } - else if (work[sym] >= match) { - here.op = (unsigned char)(extra[work[sym] - match]); - here.val = base[work[sym] - match]; - } - else { - here.op = (unsigned char)(32 + 64); /* end of block */ - here.val = 0; - } - - /* replicate for those indices with low len bits equal to huff */ - incr = 1U << (len - drop); - fill = 1U << curr; - min = fill; /* save offset to next table */ - do { - fill -= incr; - next[(huff >> drop) + fill] = here; - } while (fill != 0); - - /* backwards increment the len-bit code huff */ - incr = 1U << (len - 1); - while (huff & incr) - incr >>= 1; - if (incr != 0) { - huff &= incr - 1; - huff += incr; - } - else - huff = 0; - - /* go to next symbol, update count, len */ - sym++; - if (--(count[len]) == 0) { - if (len == max) break; - len = lens[work[sym]]; - } - - /* create new sub-table if needed */ - if (len > root && (huff & mask) != low) { - /* if first time, transition to sub-tables */ - if (drop == 0) - drop = root; - - /* increment past last table */ - next += min; /* here min is 1 << curr */ - - /* determine length of next table */ - curr = len - drop; - left = (int)(1 << curr); - while (curr + drop < max) { - left -= count[curr + drop]; - if (left <= 0) break; - curr++; - left <<= 1; - } - - /* check for enough space */ - used += 1U << curr; - if ((type == LENS && used > ENOUGH_LENS) || - (type == DISTS && used > ENOUGH_DISTS)) - return 1; - - /* point entry in root table to sub-table */ - low = huff & mask; - (*table)[low].op = (unsigned char)curr; - (*table)[low].bits = (unsigned char)root; - (*table)[low].val = (unsigned short)(next - *table); - } - } - - /* fill in remaining table entry if code is incomplete (guaranteed to have - at most one remaining entry, since if the code is incomplete, the - maximum code length that was allowed to get this far is one bit) */ - if (huff != 0) { - here.op = (unsigned char)64; /* invalid code marker */ - here.bits = (unsigned char)(len - drop); - here.val = (unsigned short)0; - next[huff] = here; - } - - /* set return parameters */ - *table += used; - *bits = root; - return 0; -} diff --git a/builtins/zlib/inftrees.h b/builtins/zlib/inftrees.h deleted file mode 100644 index baa53a0b1a199..0000000000000 --- a/builtins/zlib/inftrees.h +++ /dev/null @@ -1,62 +0,0 @@ -/* inftrees.h -- header to use inftrees.c - * Copyright (C) 1995-2005, 2010 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* Structure for decoding tables. Each entry provides either the - information needed to do the operation requested by the code that - indexed that table entry, or it provides a pointer to another - table that indexes more bits of the code. op indicates whether - the entry is a pointer to another table, a literal, a length or - distance, an end-of-block, or an invalid code. For a table - pointer, the low four bits of op is the number of index bits of - that table. For a length or distance, the low four bits of op - is the number of extra bits to get after the code. bits is - the number of bits in this code or part of the code to drop off - of the bit buffer. val is the actual byte to output in the case - of a literal, the base length or distance, or the offset from - the current table to the next table. Each entry is four bytes. */ -typedef struct { - unsigned char op; /* operation, extra bits, table bits */ - unsigned char bits; /* bits in this part of the code */ - unsigned short val; /* offset in table or code value */ -} code; - -/* op values as set by inflate_table(): - 00000000 - literal - 0000tttt - table link, tttt != 0 is the number of table index bits - 0001eeee - length or distance, eeee is the number of extra bits - 01100000 - end of block - 01000000 - invalid code - */ - -/* Maximum size of the dynamic table. The maximum number of code structures is - 1444, which is the sum of 852 for literal/length codes and 592 for distance - codes. These values were found by exhaustive searches using the program - examples/enough.c found in the zlib distribtution. The arguments to that - program are the number of symbols, the initial root table size, and the - maximum bit length of a code. "enough 286 9 15" for literal/length codes - returns returns 852, and "enough 30 6 15" for distance codes returns 592. - The initial root table size (9 or 6) is found in the fifth argument of the - inflate_table() calls in inflate.c and infback.c. If the root table size is - changed, then these maximum sizes would be need to be recalculated and - updated. */ -#define ENOUGH_LENS 852 -#define ENOUGH_DISTS 592 -#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) - -/* Type of code to build for inflate_table() */ -typedef enum { - CODES, - LENS, - DISTS -} codetype; - -int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, - unsigned codes, code FAR * FAR *table, - unsigned FAR *bits, unsigned short FAR *work)); diff --git a/builtins/zlib/trees.c b/builtins/zlib/trees.c deleted file mode 100644 index 645c0fac4fd44..0000000000000 --- a/builtins/zlib/trees.c +++ /dev/null @@ -1,1216 +0,0 @@ -/* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2012 Jean-loup Gailly - * detect_data_type() function provided freely by Cosmin Truta, 2006 - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * ALGORITHM - * - * The "deflation" process uses several Huffman trees. The more - * common source values are represented by shorter bit sequences. - * - * Each code tree is stored in a compressed form which is itself - * a Huffman encoding of the lengths of all the code strings (in - * ascending order by source values). The actual code strings are - * reconstructed from the lengths in the inflate process, as described - * in the deflate specification. - * - * REFERENCES - * - * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". - * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc - * - * Storer, James A. - * Data Compression: Methods and Theory, pp. 49-50. - * Computer Science Press, 1988. ISBN 0-7167-8156-5. - * - * Sedgewick, R. - * Algorithms, p290. - * Addison-Wesley, 1983. ISBN 0-201-06672-6. - */ - -/* @(#) $Id$ */ - -/* #define GEN_TREES_H */ - -#include "deflate.h" - -#ifdef DEBUG -# include -#endif - -/* =========================================================================== - * Constants - */ - -#define MAX_BL_BITS 7 -/* Bit length codes must not exceed MAX_BL_BITS bits */ - -#define END_BLOCK 256 -/* end of block literal code */ - -#define REP_3_6 16 -/* repeat previous bit length 3-6 times (2 bits of repeat count) */ - -#define REPZ_3_10 17 -/* repeat a zero length 3-10 times (3 bits of repeat count) */ - -#define REPZ_11_138 18 -/* repeat a zero length 11-138 times (7 bits of repeat count) */ - -local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ - = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; - -local const int extra_dbits[D_CODES] /* extra bits for each distance code */ - = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ - = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; - -local const uch bl_order[BL_CODES] - = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; -/* The lengths of the bit length codes are sent in order of decreasing - * probability, to avoid transmitting the lengths for unused bit length codes. - */ - -/* =========================================================================== - * Local data. These are initialized only once. - */ - -#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ - -#if defined(GEN_TREES_H) || !defined(STDC) -/* non ANSI compilers may not accept trees.h */ - -local ct_data static_ltree[L_CODES+2]; -/* The static literal tree. Since the bit lengths are imposed, there is no - * need for the L_CODES extra codes used during heap construction. However - * The codes 286 and 287 are needed to build a canonical tree (see _tr_init - * below). - */ - -local ct_data static_dtree[D_CODES]; -/* The static distance tree. (Actually a trivial tree since all codes use - * 5 bits.) - */ - -uch _dist_code[DIST_CODE_LEN]; -/* Distance codes. The first 256 values correspond to the distances - * 3 .. 258, the last 256 values correspond to the top 8 bits of - * the 15 bit distances. - */ - -uch _length_code[MAX_MATCH-MIN_MATCH+1]; -/* length code for each normalized match length (0 == MIN_MATCH) */ - -local int base_length[LENGTH_CODES]; -/* First normalized length for each code (0 = MIN_MATCH) */ - -local int base_dist[D_CODES]; -/* First normalized distance for each code (0 = distance of 1) */ - -#else -# include "trees.h" -#endif /* GEN_TREES_H */ - -struct static_tree_desc_s { - const ct_data *static_tree; /* static tree or NULL */ - const intf *extra_bits; /* extra bits for each code or NULL */ - int extra_base; /* base index for extra_bits */ - int elems; /* max number of elements in the tree */ - int max_length; /* max bit length for the codes */ -}; - -local static_tree_desc static_l_desc = -{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; - -local static_tree_desc static_d_desc = -{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; - -local static_tree_desc static_bl_desc = -{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; - -/* =========================================================================== - * Local (static) routines in this file. - */ - -local void tr_static_init OF((void)); -local void init_block OF((deflate_state *s)); -local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); -local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); -local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); -local void build_tree OF((deflate_state *s, tree_desc *desc)); -local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); -local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); -local int build_bl_tree OF((deflate_state *s)); -local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, - int blcodes)); -local void compress_block OF((deflate_state *s, const ct_data *ltree, - const ct_data *dtree)); -local int detect_data_type OF((deflate_state *s)); -local unsigned bi_reverse OF((unsigned value, int length)); -local void bi_windup OF((deflate_state *s)); -local void bi_flush OF((deflate_state *s)); -local void copy_block OF((deflate_state *s, charf *buf, unsigned len, - int header)); - -#ifdef GEN_TREES_H -local void gen_trees_header OF((void)); -#endif - -#ifndef DEBUG -# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) - /* Send a code of the given tree. c and tree must not have side effects */ - -#else /* DEBUG */ -# define send_code(s, c, tree) \ - { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ - send_bits(s, tree[c].Code, tree[c].Len); } -#endif - -/* =========================================================================== - * Output a short LSB first on the stream. - * IN assertion: there is enough room in pendingBuf. - */ -#define put_short(s, w) { \ - put_byte(s, (uch)((w) & 0xff)); \ - put_byte(s, (uch)((ush)(w) >> 8)); \ -} - -/* =========================================================================== - * Send a value on a given number of bits. - * IN assertion: length <= 16 and value fits in length bits. - */ -#ifdef DEBUG -local void send_bits OF((deflate_state *s, int value, int length)); - -local void send_bits(s, value, length) - deflate_state *s; - int value; /* value to send */ - int length; /* number of bits */ -{ - Tracevv((stderr," l %2d v %4x ", length, value)); - Assert(length > 0 && length <= 15, "invalid length"); - s->bits_sent += (ulg)length; - - /* If not enough room in bi_buf, use (valid) bits from bi_buf and - * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) - * unused bits in value. - */ - if (s->bi_valid > (int)Buf_size - length) { - s->bi_buf |= (ush)value << s->bi_valid; - put_short(s, s->bi_buf); - s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); - s->bi_valid += length - Buf_size; - } else { - s->bi_buf |= (ush)value << s->bi_valid; - s->bi_valid += length; - } -} -#else /* !DEBUG */ - -#define send_bits(s, value, length) \ -{ int len = length;\ - if (s->bi_valid > (int)Buf_size - len) {\ - int val = value;\ - s->bi_buf |= (ush)val << s->bi_valid;\ - put_short(s, s->bi_buf);\ - s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ - s->bi_valid += len - Buf_size;\ - } else {\ - s->bi_buf |= (ush)(value) << s->bi_valid;\ - s->bi_valid += len;\ - }\ -} -#endif /* DEBUG */ - - -/* the arguments must not have side effects */ - -/* =========================================================================== - * Initialize the various 'constant' tables. - */ -local void tr_static_init() -{ -#if defined(GEN_TREES_H) || !defined(STDC) - static int static_init_done = 0; - int n; /* iterates over tree elements */ - int bits; /* bit counter */ - int length; /* length value */ - int code; /* code value */ - int dist; /* distance index */ - ush bl_count[MAX_BITS+1]; - /* number of codes at each bit length for an optimal tree */ - - if (static_init_done) return; - - /* For some embedded targets, global variables are not initialized: */ -#ifdef NO_INIT_GLOBAL_POINTERS - static_l_desc.static_tree = static_ltree; - static_l_desc.extra_bits = extra_lbits; - static_d_desc.static_tree = static_dtree; - static_d_desc.extra_bits = extra_dbits; - static_bl_desc.extra_bits = extra_blbits; -#endif - - /* Initialize the mapping length (0..255) -> length code (0..28) */ - length = 0; - for (code = 0; code < LENGTH_CODES-1; code++) { - base_length[code] = length; - for (n = 0; n < (1< dist code (0..29) */ - dist = 0; - for (code = 0 ; code < 16; code++) { - base_dist[code] = dist; - for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ - for ( ; code < D_CODES; code++) { - base_dist[code] = dist << 7; - for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { - _dist_code[256 + dist++] = (uch)code; - } - } - Assert (dist == 256, "tr_static_init: 256+dist != 512"); - - /* Construct the codes of the static literal tree */ - for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; - n = 0; - while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; - while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; - while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; - while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; - /* Codes 286 and 287 do not exist, but we must include them in the - * tree construction to get a canonical Huffman tree (longest code - * all ones) - */ - gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); - - /* The static distance tree is trivial: */ - for (n = 0; n < D_CODES; n++) { - static_dtree[n].Len = 5; - static_dtree[n].Code = bi_reverse((unsigned)n, 5); - } - static_init_done = 1; - -# ifdef GEN_TREES_H - gen_trees_header(); -# endif -#endif /* defined(GEN_TREES_H) || !defined(STDC) */ -} - -/* =========================================================================== - * Genererate the file trees.h describing the static trees. - */ -#ifdef GEN_TREES_H -# ifndef DEBUG -# include -# endif - -# define SEPARATOR(i, last, width) \ - ((i) == (last)? "\n};\n\n" : \ - ((i) % (width) == (width)-1 ? ",\n" : ", ")) - -void gen_trees_header() -{ - FILE *header = fopen("trees.h", "w"); - int i; - - Assert (header != NULL, "Can't open trees.h"); - fprintf(header, - "/* header created automatically with -DGEN_TREES_H */\n\n"); - - fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); - for (i = 0; i < L_CODES+2; i++) { - fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, - static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); - } - - fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); - for (i = 0; i < D_CODES; i++) { - fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, - static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); - } - - fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); - for (i = 0; i < DIST_CODE_LEN; i++) { - fprintf(header, "%2u%s", _dist_code[i], - SEPARATOR(i, DIST_CODE_LEN-1, 20)); - } - - fprintf(header, - "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); - for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { - fprintf(header, "%2u%s", _length_code[i], - SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); - } - - fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); - for (i = 0; i < LENGTH_CODES; i++) { - fprintf(header, "%1u%s", base_length[i], - SEPARATOR(i, LENGTH_CODES-1, 20)); - } - - fprintf(header, "local const int base_dist[D_CODES] = {\n"); - for (i = 0; i < D_CODES; i++) { - fprintf(header, "%5u%s", base_dist[i], - SEPARATOR(i, D_CODES-1, 10)); - } - - fclose(header); -} -#endif /* GEN_TREES_H */ - -/* =========================================================================== - * Initialize the tree data structures for a new zlib stream. - */ -void ZLIB_INTERNAL _tr_init(deflate_state *s) -{ - tr_static_init(); - - s->l_desc.dyn_tree = s->dyn_ltree; - s->l_desc.stat_desc = &static_l_desc; - - s->d_desc.dyn_tree = s->dyn_dtree; - s->d_desc.stat_desc = &static_d_desc; - - s->bl_desc.dyn_tree = s->bl_tree; - s->bl_desc.stat_desc = &static_bl_desc; - - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef DEBUG - s->compressed_len = 0L; - s->bits_sent = 0L; -#endif - - /* Initialize the first block of the first file: */ - init_block(s); -} - -/* =========================================================================== - * Initialize a new block. - */ -local void init_block(deflate_state *s) -{ - int n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; - for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; - for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; - - s->dyn_ltree[END_BLOCK].Freq = 1; - s->opt_len = s->static_len = 0L; - s->last_lit = s->matches = 0; -} - -#define SMALLEST 1 -/* Index within the heap array of least frequent node in the Huffman tree */ - - -/* =========================================================================== - * Remove the smallest element from the heap and recreate the heap with - * one less element. Updates heap and heap_len. - */ -#define pqremove(s, tree, top) \ -{\ - top = s->heap[SMALLEST]; \ - s->heap[SMALLEST] = s->heap[s->heap_len--]; \ - pqdownheap(s, tree, SMALLEST); \ -} - -/* =========================================================================== - * Compares to subtrees, using the tree depth as tie breaker when - * the subtrees have equal frequency. This minimizes the worst case length. - */ -#define smaller(tree, n, m, depth) \ - (tree[n].Freq < tree[m].Freq || \ - (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) - -/* =========================================================================== - * Restore the heap property by moving down the tree starting at node k, - * exchanging a node with the smallest of its two sons if necessary, stopping - * when the heap property is re-established (each father smaller than its - * two sons). - */ -local void pqdownheap(deflate_state *s, - ct_data *tree, /* the tree to restore */ - int k /* node to move down */ - ) -{ - int v = s->heap[k]; - int j = k << 1; /* left son of k */ - while (j <= s->heap_len) { - /* Set j to the smallest of the two sons: */ - if (j < s->heap_len && - smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { - j++; - } - /* Exit if v is smaller than both sons */ - if (smaller(tree, v, s->heap[j], s->depth)) break; - - /* Exchange v with the smallest son */ - s->heap[k] = s->heap[j]; k = j; - - /* And continue down the tree, setting j to the left son of k */ - j <<= 1; - } - s->heap[k] = v; -} - -/* =========================================================================== - * Compute the optimal bit lengths for a tree and update the total bit length - * for the current block. - * IN assertion: the fields freq and dad are set, heap[heap_max] and - * above are the tree nodes sorted by increasing frequency. - * OUT assertions: the field len is set to the optimal bit length, the - * array bl_count contains the frequencies for each bit length. - * The length opt_len is updated; static_len is also updated if stree is - * not null. - */ -local void gen_bitlen(deflate_state *s, tree_desc *desc) /* the tree descriptor */ -{ - ct_data *tree = desc->dyn_tree; - int max_code = desc->max_code; - const ct_data *stree = desc->stat_desc->static_tree; - const intf *extra = desc->stat_desc->extra_bits; - int base = desc->stat_desc->extra_base; - int max_length = desc->stat_desc->max_length; - int h; /* heap index */ - int n, m; /* iterate over the tree elements */ - int bits; /* bit length */ - int xbits; /* extra bits */ - ush f; /* frequency */ - int overflow = 0; /* number of elements with bit length too large */ - - for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; - - /* In a first pass, compute the optimal bit lengths (which may - * overflow in the case of the bit length tree). - */ - tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ - - for (h = s->heap_max+1; h < HEAP_SIZE; h++) { - n = s->heap[h]; - bits = tree[tree[n].Dad].Len + 1; - if (bits > max_length) bits = max_length, overflow++; - tree[n].Len = (ush)bits; - /* We overwrite tree[n].Dad which is no longer needed */ - - if (n > max_code) continue; /* not a leaf node */ - - s->bl_count[bits]++; - xbits = 0; - if (n >= base) xbits = extra[n-base]; - f = tree[n].Freq; - s->opt_len += (ulg)f * (bits + xbits); - if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); - } - if (overflow == 0) return; - - Trace((stderr,"\nbit length overflow\n")); - /* This happens for example on obj2 and pic of the Calgary corpus */ - - /* Find the first bit length which could increase: */ - do { - bits = max_length-1; - while (s->bl_count[bits] == 0) bits--; - s->bl_count[bits]--; /* move one leaf down the tree */ - s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ - s->bl_count[max_length]--; - /* The brother of the overflow item also moves one step up, - * but this does not affect bl_count[max_length] - */ - overflow -= 2; - } while (overflow > 0); - - /* Now recompute all bit lengths, scanning in increasing frequency. - * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all - * lengths instead of fixing only the wrong ones. This idea is taken - * from 'ar' written by Haruhiko Okumura.) - */ - for (bits = max_length; bits != 0; bits--) { - n = s->bl_count[bits]; - while (n != 0) { - m = s->heap[--h]; - if (m > max_code) continue; - if ((unsigned) tree[m].Len != (unsigned) bits) { - Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); - s->opt_len += ((long)bits - (long)tree[m].Len) - *(long)tree[m].Freq; - tree[m].Len = (ush)bits; - } - n--; - } - } -} - -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -local void gen_codes ( - ct_data *tree, /* the tree to decorate */ - int max_code, /* largest code with non zero frequency */ - ushf *bl_count) /* number of codes at each bit length */ -{ - ush next_code[MAX_BITS+1]; /* next code value for each bit length */ - ush code = 0; /* running code value */ - int bits; /* bit index */ - int n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits-1]) << 1; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; - const ct_data *stree = desc->stat_desc->static_tree; - int elems = desc->stat_desc->elems; - int n, m; /* iterate over heap elements */ - int max_code = -1; /* largest code with non zero frequency */ - int node; /* new node being created */ - - /* Construct the initial heap, with least frequent element in - * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - * heap[0] is not used. - */ - s->heap_len = 0, s->heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) { - if (tree[n].Freq != 0) { - s->heap[++(s->heap_len)] = max_code = n; - s->depth[n] = 0; - } else { - tree[n].Len = 0; - } - } - - /* The pkzip format requires that at least one distance code exists, - * and that at least one bit should be sent even if there is only one - * possible code. So to avoid special checks later on we force at least - * two codes of non zero frequency. - */ - while (s->heap_len < 2) { - node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); - tree[node].Freq = 1; - s->depth[node] = 0; - s->opt_len--; if (stree) s->static_len -= stree[node].Len; - /* node is 0 or 1 so it does not have extra bits */ - } - desc->max_code = max_code; - - /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - * establish sub-heaps of increasing lengths: - */ - for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); - - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ - node = elems; /* next internal node of the tree */ - do { - pqremove(s, tree, n); /* n = node of least frequency */ - m = s->heap[SMALLEST]; /* m = node of next least frequency */ - - s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ - s->heap[--(s->heap_max)] = m; - - /* Create a new node father of n and m */ - tree[node].Freq = tree[n].Freq + tree[m].Freq; - s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? - s->depth[n] : s->depth[m]) + 1); - tree[n].Dad = tree[m].Dad = (ush)node; -#ifdef DUMP_BL_TREE - if (tree == s->bl_tree) { - fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", - node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); - } -#endif - /* and insert the new node in the heap */ - s->heap[SMALLEST] = node++; - pqdownheap(s, tree, SMALLEST); - - } while (s->heap_len >= 2); - - s->heap[--(s->heap_max)] = s->heap[SMALLEST]; - - /* At this point, the fields freq and dad are set. We can now - * generate the bit lengths. - */ - gen_bitlen(s, (tree_desc *)desc); - - /* The field len is now set, we can generate the bit codes */ - gen_codes ((ct_data *)tree, max_code, s->bl_count); -} - -/* =========================================================================== - * Scan a literal or distance tree to determine the frequencies of the codes - * in the bit length tree. - */ -local void scan_tree ( - deflate_state *s, - ct_data *tree, /* the tree to be scanned */ - int max_code) /* and its largest code of non zero frequency */ -{ - int n; /* iterates over all tree elements */ - int prevlen = -1; /* last emitted length */ - int curlen; /* length of current code */ - int nextlen = tree[0].Len; /* length of next code */ - int count = 0; /* repeat count of the current code */ - int max_count = 7; /* max repeat count */ - int min_count = 4; /* min repeat count */ - - if (nextlen == 0) max_count = 138, min_count = 3; - tree[max_code+1].Len = (ush)0xffff; /* guard */ - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[n+1].Len; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - s->bl_tree[curlen].Freq += count; - } else if (curlen != 0) { - if (curlen != prevlen) s->bl_tree[curlen].Freq++; - s->bl_tree[REP_3_6].Freq++; - } else if (count <= 10) { - s->bl_tree[REPZ_3_10].Freq++; - } else { - s->bl_tree[REPZ_11_138].Freq++; - } - count = 0; prevlen = curlen; - if (nextlen == 0) { - max_count = 138, min_count = 3; - } else if (curlen == nextlen) { - max_count = 6, min_count = 3; - } else { - max_count = 7, min_count = 4; - } - } -} - -/* =========================================================================== - * Send a literal or distance tree in compressed form, using the codes in - * bl_tree. - */ -local void send_tree ( - deflate_state *s, - ct_data *tree, /* the tree to be scanned */ - int max_code) /* and its largest code of non zero frequency */ -{ - int n; /* iterates over all tree elements */ - int prevlen = -1; /* last emitted length */ - int curlen; /* length of current code */ - int nextlen = tree[0].Len; /* length of next code */ - int count = 0; /* repeat count of the current code */ - int max_count = 7; /* max repeat count */ - int min_count = 4; /* min repeat count */ - - /* tree[max_code+1].Len = -1; */ /* guard already set */ - if (nextlen == 0) max_count = 138, min_count = 3; - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[n+1].Len; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - do { send_code(s, curlen, s->bl_tree); } while (--count != 0); - - } else if (curlen != 0) { - if (curlen != prevlen) { - send_code(s, curlen, s->bl_tree); count--; - } - Assert(count >= 3 && count <= 6, " 3_6?"); - send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); - - } else if (count <= 10) { - send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); - - } else { - send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); - } - count = 0; prevlen = curlen; - if (nextlen == 0) { - max_count = 138, min_count = 3; - } else if (curlen == nextlen) { - max_count = 6, min_count = 3; - } else { - max_count = 7, min_count = 4; - } - } -} - -/* =========================================================================== - * Construct the Huffman tree for the bit lengths and return the index in - * bl_order of the last bit length code to send. - */ -local int build_bl_tree(deflate_state *s) -{ - int max_blindex; /* index of last bit length code of non zero freq */ - - /* Determine the bit length frequencies for literal and distance trees */ - scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); - scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); - - /* Build the bit length tree: */ - build_tree(s, (tree_desc *)(&(s->bl_desc))); - /* opt_len now includes the length of the tree representations, except - * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. - */ - - /* Determine the number of bit length codes to send. The pkzip format - * requires that at least 4 bit length codes be sent. (appnote.txt says - * 3 but the actual value used is 4.) - */ - for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { - if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; - } - /* Update opt_len to include the bit length tree and counts */ - s->opt_len += 3*(max_blindex+1) + 5+5+4; - Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", - s->opt_len, s->static_len)); - - return max_blindex; -} - -/* =========================================================================== - * Send the header for a block using dynamic Huffman trees: the counts, the - * lengths of the bit length codes, the literal tree and the distance tree. - * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - */ -local void send_all_trees( - deflate_state *s, - int lcodes, int dcodes, int blcodes) /* number of codes for each tree */ -{ - int rank; /* index in bl_order */ - - Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); - Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, - "too many codes"); - Tracev((stderr, "\nbl counts: ")); - send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ - send_bits(s, dcodes-1, 5); - send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ - for (rank = 0; rank < blcodes; rank++) { - Tracev((stderr, "\nbl code %2d ", bl_order[rank])); - send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); - } - Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); - - send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ - Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); - - send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ - Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); -} - -/* =========================================================================== - * Send a stored block - */ -void ZLIB_INTERNAL _tr_stored_block( - deflate_state *s, - charf *buf, /* input block */ - ulg stored_len, /* length of input block */ - int last) /* one if this is the last block for a file */ -{ - send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ -#ifdef DEBUG - s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; - s->compressed_len += (stored_len + 4) << 3; -#endif - copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ -} - -/* =========================================================================== - * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) - */ -void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) -{ - bi_flush(s); -} - -/* =========================================================================== - * Send one empty static block to give enough lookahead for inflate. - * This takes 10 bits, of which 7 may remain in the bit buffer. - */ -void ZLIB_INTERNAL _tr_align(deflate_state *s) -{ - send_bits(s, STATIC_TREES<<1, 3); - send_code(s, END_BLOCK, static_ltree); -#ifdef DEBUG - s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ -#endif - bi_flush(s); -} - -/* =========================================================================== - * Determine the best encoding for the current block: dynamic trees, static - * trees or store, and output the encoded block to the zip file. - */ -void ZLIB_INTERNAL _tr_flush_block( - deflate_state *s, - charf *buf, /* input block, or NULL if too old */ - ulg stored_len, /* length of input block */ - int last) /* one if this is the last block for a file */ -{ - ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ - int max_blindex = 0; /* index of last bit length code of non zero freq */ - - /* Build the Huffman trees unless a stored block is forced */ - if (s->level > 0) { - - /* Check if the file is binary or text */ - if (s->strm->data_type == Z_UNKNOWN) - s->strm->data_type = detect_data_type(s); - - /* Construct the literal and distance trees */ - build_tree(s, (tree_desc *)(&(s->l_desc))); - Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, - s->static_len)); - - build_tree(s, (tree_desc *)(&(s->d_desc))); - Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, - s->static_len)); - /* At this point, opt_len and static_len are the total bit lengths of - * the compressed block data, excluding the tree representations. - */ - - /* Build the bit length tree for the above two trees, and get the index - * in bl_order of the last bit length code to send. - */ - max_blindex = build_bl_tree(s); - - /* Determine the best encoding. Compute the block lengths in bytes. */ - opt_lenb = (s->opt_len+3+7)>>3; - static_lenb = (s->static_len+3+7)>>3; - - Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", - opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, - s->last_lit)); - - if (static_lenb <= opt_lenb) opt_lenb = static_lenb; - - } else { - Assert(buf != (char*)0, "lost buf"); - opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ - } - -#ifdef FORCE_STORED - if (buf != (char*)0) { /* force stored block */ -#else - if (stored_len+4 <= opt_lenb && buf != (char*)0) { - /* 4: two words for the lengths */ -#endif - /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - * Otherwise we can't have processed more than WSIZE input bytes since - * the last block flush, because compression would have been - * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - * transform a block into a stored block. - */ - _tr_stored_block(s, buf, stored_len, last); - -#ifdef FORCE_STATIC - } else if (static_lenb >= 0) { /* force static trees */ -#else - } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { -#endif - send_bits(s, (STATIC_TREES<<1)+last, 3); - compress_block(s, (const ct_data *)static_ltree, - (const ct_data *)static_dtree); -#ifdef DEBUG - s->compressed_len += 3 + s->static_len; -#endif - } else { - send_bits(s, (DYN_TREES<<1)+last, 3); - send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, - max_blindex+1); - compress_block(s, (const ct_data *)s->dyn_ltree, - (const ct_data *)s->dyn_dtree); -#ifdef DEBUG - s->compressed_len += 3 + s->opt_len; -#endif - } - Assert (s->compressed_len == s->bits_sent, "bad compressed size"); - /* The above check is made mod 2^32, for files larger than 512 MB - * and uLong implemented on 32 bits. - */ - init_block(s); - - if (last) { - bi_windup(s); -#ifdef DEBUG - s->compressed_len += 7; /* align on byte boundary */ -#endif - } - Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, - s->compressed_len-7*last)); -} - -/* =========================================================================== - * Save the match info and tally the frequency counts. Return true if - * the current block must be flushed. - */ -int ZLIB_INTERNAL _tr_tally ( - deflate_state *s, - unsigned dist, /* distance of matched string */ - unsigned lc) /* match length-MIN_MATCH or unmatched char (if dist==0) */ -{ - s->d_buf[s->last_lit] = (ush)dist; - s->l_buf[s->last_lit++] = (uch)lc; - if (dist == 0) { - /* lc is the unmatched char */ - s->dyn_ltree[lc].Freq++; - } else { - s->matches++; - /* Here, lc is the match length - MIN_MATCH */ - dist--; /* dist = match distance - 1 */ - Assert((ush)dist < (ush)MAX_DIST(s) && - (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && - (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); - - s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; - s->dyn_dtree[d_code(dist)].Freq++; - } - -#ifdef TRUNCATE_BLOCK - /* Try to guess if it is profitable to stop the current block here */ - if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { - /* Compute an upper bound for the compressed length */ - ulg out_length = (ulg)s->last_lit*8L; - ulg in_length = (ulg)((long)s->strstart - s->block_start); - int dcode; - for (dcode = 0; dcode < D_CODES; dcode++) { - out_length += (ulg)s->dyn_dtree[dcode].Freq * - (5L+extra_dbits[dcode]); - } - out_length >>= 3; - Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", - s->last_lit, in_length, out_length, - 100L - out_length*100L/in_length)); - if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; - } -#endif - return (s->last_lit == s->lit_bufsize-1); - /* We avoid equality with lit_bufsize because of wraparound at 64K - * on 16 bit machines and because stored blocks are restricted to - * 64K-1 bytes. - */ -} - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -local void compress_block( - deflate_state *s, - const ct_data *ltree, /* literal tree */ - const ct_data *dtree) /* distance tree */ -{ - unsigned dist; /* distance of matched string */ - int lc; /* match length or unmatched char (if dist == 0) */ - unsigned lx = 0; /* running index in l_buf */ - unsigned code; /* the code to send */ - int extra; /* number of extra bits to send */ - - if (s->last_lit != 0) do { - dist = s->d_buf[lx]; - lc = s->l_buf[lx++]; - if (dist == 0) { - send_code(s, lc, ltree); /* send a literal byte */ - Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - send_code(s, code+LITERALS+1, ltree); /* send the length code */ - extra = extra_lbits[code]; - if (extra != 0) { - lc -= base_length[code]; - send_bits(s, lc, extra); /* send the extra length bits */ - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - Assert (code < D_CODES, "bad d_code"); - - send_code(s, code, dtree); /* send the distance code */ - extra = extra_dbits[code]; - if (extra != 0) { - dist -= base_dist[code]; - send_bits(s, dist, extra); /* send the extra distance bits */ - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ - Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, - "pendingBuf overflow"); - - } while (lx < s->last_lit); - - send_code(s, END_BLOCK, ltree); -} - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "black list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -local int detect_data_type(deflate_state *s) -{ - /* black_mask is the bit mask of black-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - unsigned long black_mask = 0xf3ffc07fUL; - int n; - - /* Check for non-textual ("black-listed") bytes. */ - for (n = 0; n <= 31; n++, black_mask >>= 1) - if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) - return Z_BINARY; - - /* Check for textual ("white-listed") bytes. */ - if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 - || s->dyn_ltree[13].Freq != 0) - return Z_TEXT; - for (n = 32; n < LITERALS; n++) - if (s->dyn_ltree[n].Freq != 0) - return Z_TEXT; - - /* There are no "black-listed" or "white-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -} - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -local unsigned bi_reverse( - unsigned code, /* the value to invert */ - int len) /* its bit length */ -{ - register unsigned res = 0; - do { - res |= code & 1; - code >>= 1, res <<= 1; - } while (--len > 0); - return res >> 1; -} - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -local void bi_flush(deflate_state *s) -{ - if (s->bi_valid == 16) { - put_short(s, s->bi_buf); - s->bi_buf = 0; - s->bi_valid = 0; - } else if (s->bi_valid >= 8) { - put_byte(s, (Byte)s->bi_buf); - s->bi_buf >>= 8; - s->bi_valid -= 8; - } -} - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -local void bi_windup(deflate_state *s) -{ - if (s->bi_valid > 8) { - put_short(s, s->bi_buf); - } else if (s->bi_valid > 0) { - put_byte(s, (Byte)s->bi_buf); - } - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef DEBUG - s->bits_sent = (s->bits_sent+7) & ~7; -#endif -} - -/* =========================================================================== - * Copy a stored block, storing first the length and its - * one's complement if requested. - */ -local void copy_block( - deflate_state *s, - charf *buf, /* the input data */ - unsigned len, /* its length */ - int header) /* true if block header must be written */ -{ - bi_windup(s); /* align on byte boundary */ - - if (header) { - put_short(s, (ush)len); - put_short(s, (ush)~len); -#ifdef DEBUG - s->bits_sent += 2*16; -#endif - } -#ifdef DEBUG - s->bits_sent += (ulg)len<<3; -#endif - while (len--) { - put_byte(s, *buf++); - } -} diff --git a/builtins/zlib/trees.h b/builtins/zlib/trees.h deleted file mode 100644 index d35639d82a278..0000000000000 --- a/builtins/zlib/trees.h +++ /dev/null @@ -1,128 +0,0 @@ -/* header created automatically with -DGEN_TREES_H */ - -local const ct_data static_ltree[L_CODES+2] = { -{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, -{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, -{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, -{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, -{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, -{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, -{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, -{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, -{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, -{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, -{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, -{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, -{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, -{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, -{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, -{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, -{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, -{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, -{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, -{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, -{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, -{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, -{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, -{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, -{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, -{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, -{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, -{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, -{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, -{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, -{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, -{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, -{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, -{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, -{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, -{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, -{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, -{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, -{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, -{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, -{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, -{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, -{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, -{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, -{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, -{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, -{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, -{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, -{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, -{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, -{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, -{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, -{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, -{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, -{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, -{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, -{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, -{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} -}; - -local const ct_data static_dtree[D_CODES] = { -{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, -{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, -{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, -{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, -{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, -{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} -}; - -const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { - 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, - 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, -10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, -11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, -12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, -13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, -13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, -14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, -14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, -14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, -15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, -15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, -15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, -18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, -23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, -24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, -26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, -26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, -27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, -28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, -29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, -29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, -29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 -}; - -const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, -13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, -17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, -19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, -21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, -22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, -23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, -24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, -25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, -25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, -26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, -26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, -27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 -}; - -local const int base_length[LENGTH_CODES] = { -0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, -64, 80, 96, 112, 128, 160, 192, 224, 0 -}; - -local const int base_dist[D_CODES] = { - 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, - 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, - 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 -}; - diff --git a/builtins/zlib/trees_cf.c b/builtins/zlib/trees_cf.c deleted file mode 100644 index c70d4bbbb16e1..0000000000000 --- a/builtins/zlib/trees_cf.c +++ /dev/null @@ -1,1298 +0,0 @@ -/* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2012 Jean-loup Gailly - * detect_data_type() function provided freely by Cosmin Truta, 2006 - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* - * ALGORITHM - * - * The "deflation" process uses several Huffman trees. The more - * common source values are represented by shorter bit sequences. - * - * Each code tree is stored in a compressed form which is itself - * a Huffman encoding of the lengths of all the code strings (in - * ascending order by source values). The actual code strings are - * reconstructed from the lengths in the inflate process, as described - * in the deflate specification. - * - * REFERENCES - * - * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". - * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc - * - * Storer, James A. - * Data Compression: Methods and Theory, pp. 49-50. - * Computer Science Press, 1988. ISBN 0-7167-8156-5. - * - * Sedgewick, R. - * Algorithms, p290. - * Addison-Wesley, 1983. ISBN 0-201-06672-6. - */ - -/* @(#) $Id$ */ - -/* #define GEN_TREES_H */ - -#include "deflate_cf.h" - -#ifdef DEBUG -# include -#endif - -/* =========================================================================== - * Constants - */ - -#define MAX_BL_BITS 7 -/* Bit length codes must not exceed MAX_BL_BITS bits */ - -#define END_BLOCK 256 -/* end of block literal code */ - -#define REP_3_6 16 -/* repeat previous bit length 3-6 times (2 bits of repeat count) */ - -#define REPZ_3_10 17 -/* repeat a zero length 3-10 times (3 bits of repeat count) */ - -#define REPZ_11_138 18 -/* repeat a zero length 11-138 times (7 bits of repeat count) */ - -static const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ - = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; - -static const int extra_dbits[D_CODES] /* extra bits for each distance code */ - = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ - = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; - -static const uint8_t bl_order[BL_CODES] - = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; -/* The lengths of the bit length codes are sent in order of decreasing - * probability, to avoid transmitting the lengths for unused bit length codes. - */ - -/* =========================================================================== - * Local data. These are initialized only once. - */ - -#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ - -#if defined(GEN_TREES_H) || !defined(STDC) -/* non ANSI compilers may not accept trees.h */ - -static ct_data static_ltree[L_CODES+2]; -/* The static literal tree. Since the bit lengths are imposed, there is no - * need for the L_CODES extra codes used during heap construction. However - * The codes 286 and 287 are needed to build a canonical tree (see _tr_init - * below). - */ - -static ct_data static_dtree[D_CODES]; -/* The static distance tree. (Actually a trivial tree since all codes use - * 5 bits.) - */ - -uint8_t _dist_code[DIST_CODE_LEN]; -/* Distance codes. The first 256 values correspond to the distances - * 3 .. 258, the last 256 values correspond to the top 8 bits of - * the 15 bit distances. - */ - -uint8_t _length_code[MAX_MATCH-MIN_MATCH+1]; -/* length code for each normalized match length (0 == MIN_MATCH) */ - -static int base_length[LENGTH_CODES]; -/* First normalized length for each code (0 = MIN_MATCH) */ - -static int base_dist[D_CODES]; -/* First normalized distance for each code (0 = distance of 1) */ - -#else -# include "trees.h" -#endif /* GEN_TREES_H */ - -struct static_tree_desc_s { - const ct_data *static_tree; /* static tree or NULL */ - const int *extra_bits; /* extra bits for each code or NULL */ - int extra_base; /* base index for extra_bits */ - int elems; /* max number of elements in the tree */ - int max_length; /* max bit length for the codes */ -}; - -static static_tree_desc static_l_desc = -{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; - -static static_tree_desc static_d_desc = -{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; - -static static_tree_desc static_bl_desc = -{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; - -/* =========================================================================== - * Local (static) routines in this file. - */ - -static void tr_static_init (void); -static void init_block (deflate_state *s); -static void pqdownheap (deflate_state *s, ct_data *tree, int k); -static void gen_bitlen (deflate_state *s, tree_desc *desc); -static void gen_codes (ct_data *tree, int max_code, uint16_t *bl_count); -static void build_tree (deflate_state *s, tree_desc *desc); -static void scan_tree (deflate_state *s, ct_data *tree, int max_code); -static void send_tree (deflate_state *s, ct_data *tree, int max_code); -static int build_bl_tree (deflate_state *s); -static void send_all_trees (deflate_state *s, int lcodes, int dcodes, - int blcodes); -static void compress_block (deflate_state *s, const ct_data *ltree, - const ct_data *dtree); -static int detect_data_type (deflate_state *s); -static unsigned bi_reverse (unsigned value, int length); -static void bi_windup (deflate_state *s); -static void bi_flush (deflate_state *s); -static void copy_block (deflate_state *s, uint8_t *buf, unsigned len, - int header); - -#ifdef GEN_TREES_H -static void gen_trees_header OF(void); -#endif - -#ifndef DEBUG -# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) - /* Send a code of the given tree. c and tree must not have side effects */ - -#else /* DEBUG */ -# define send_code(s, c, tree) \ - { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ - send_bits(s, tree[c].Code, tree[c].Len); } -#endif - -/* =========================================================================== - * Send a value on a given number of bits. - * IN assertion: length <= 64 and value fits in length bits. - */ - - -static void send_bits(deflate_state* s, uint64_t val, int len) { - -#ifdef DEBUG - Tracevv((stderr," l %2d v %4llx ", len, val)); - Assert(len > 0 && len <= 64, "invalid length"); - s->bits_sent += len; -#endif - - s->bi_buf ^= (val<bi_valid); - s->bi_valid += len; - if (s->bi_valid >= 64) { - *(uint64_t*)(&s->pending_buf[s->pending]) = s->bi_buf; - s->pending += 8; - s->bi_valid -= 64; - s->bi_buf = val >> (len - s->bi_valid); - } -} - -/* the arguments must not have side effects */ - -/* =========================================================================== - * Initialize the various 'constant' tables. - */ -static void tr_static_init() -{ -#if defined(GEN_TREES_H) || !defined(STDC) - static int static_init_done = 0; - int n; /* iterates over tree elements */ - int bits; /* bit counter */ - int length; /* length value */ - int code; /* code value */ - int dist; /* distance index */ - uint16_t bl_count[MAX_BITS+1]; - /* number of codes at each bit length for an optimal tree */ - - if (static_init_done) return; - - /* For some embedded targets, global variables are not initialized: */ -#ifdef NO_INIT_GLOBAL_POINTERS - static_l_desc.static_tree = static_ltree; - static_l_desc.extra_bits = extra_lbits; - static_d_desc.static_tree = static_dtree; - static_d_desc.extra_bits = extra_dbits; - static_bl_desc.extra_bits = extra_blbits; -#endif - - /* Initialize the mapping length (0..255) -> length code (0..28) */ - length = 0; - for (code = 0; code < LENGTH_CODES-1; code++) { - base_length[code] = length; - for (n = 0; n < (1< dist code (0..29) */ - dist = 0; - for (code = 0 ; code < 16; code++) { - base_dist[code] = dist; - for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ - for ( ; code < D_CODES; code++) { - base_dist[code] = dist << 7; - for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { - _dist_code[256 + dist++] = (uint8_t)code; - } - } - Assert (dist == 256, "tr_static_init: 256+dist != 512"); - - /* Construct the codes of the static literal tree */ - for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; - n = 0; - while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; - while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; - while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; - while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; - /* Codes 286 and 287 do not exist, but we must include them in the - * tree construction to get a canonical Huffman tree (longest code - * all ones) - */ - gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); - - /* The static distance tree is trivial: */ - for (n = 0; n < D_CODES; n++) { - static_dtree[n].Len = 5; - static_dtree[n].Code = bi_reverse((unsigned)n, 5); - } - static_init_done = 1; - -# ifdef GEN_TREES_H - gen_trees_header(); -# endif -#endif /* defined(GEN_TREES_H) || !defined(STDC) */ -} - -/* =========================================================================== - * Genererate the file trees.h describing the static trees. - */ -#ifdef GEN_TREES_H -# ifndef DEBUG -# include -# endif - -# define SEPARATOR(i, last, width) \ - ((i) == (last)? "\n};\n\n" : \ - ((i) % (width) == (width)-1 ? ",\n" : ", ")) - -void gen_trees_header() -{ - FILE *header = fopen("trees.h", "w"); - int i; - - Assert (header != NULL, "Can't open trees.h"); - fprintf(header, - "/* header created automatically with -DGEN_TREES_H */\n\n"); - - fprintf(header, "static const ct_data static_ltree[L_CODES+2] = {\n"); - for (i = 0; i < L_CODES+2; i++) { - fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, - static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); - } - - fprintf(header, "static const ct_data static_dtree[D_CODES] = {\n"); - for (i = 0; i < D_CODES; i++) { - fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, - static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); - } - - fprintf(header, "const uint8_t ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); - for (i = 0; i < DIST_CODE_LEN; i++) { - fprintf(header, "%2u%s", _dist_code[i], - SEPARATOR(i, DIST_CODE_LEN-1, 20)); - } - - fprintf(header, - "const uint8_t ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); - for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { - fprintf(header, "%2u%s", _length_code[i], - SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); - } - - fprintf(header, "static const int base_length[LENGTH_CODES] = {\n"); - for (i = 0; i < LENGTH_CODES; i++) { - fprintf(header, "%1u%s", base_length[i], - SEPARATOR(i, LENGTH_CODES-1, 20)); - } - - fprintf(header, "static const int base_dist[D_CODES] = {\n"); - for (i = 0; i < D_CODES; i++) { - fprintf(header, "%5u%s", base_dist[i], - SEPARATOR(i, D_CODES-1, 10)); - } - - fclose(header); -} -#endif /* GEN_TREES_H */ - -/* =========================================================================== - * Initialize the tree data structures for a new zlib stream. - */ -void ZLIB_INTERNAL _tr_init(s) - deflate_state *s; -{ - tr_static_init(); - - s->l_desc.dyn_tree = s->dyn_ltree; - s->l_desc.stat_desc = &static_l_desc; - - s->d_desc.dyn_tree = s->dyn_dtree; - s->d_desc.stat_desc = &static_d_desc; - - s->bl_desc.dyn_tree = s->bl_tree; - s->bl_desc.stat_desc = &static_bl_desc; - - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef DEBUG - s->compressed_len = 0L; - s->bits_sent = 0L; -#endif - - /* Initialize the first block of the first file: */ - init_block(s); -} - -/* =========================================================================== - * Initialize a new block. - */ -static void init_block(s) - deflate_state *s; -{ - int n; /* iterates over tree elements */ - - /* Initialize the trees. */ - for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; - for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; - for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; - - s->dyn_ltree[END_BLOCK].Freq = 1; - s->opt_len = s->static_len = 0L; - s->last_lit = s->matches = 0; -} - -#define SMALLEST 1 -/* Index within the heap array of least frequent node in the Huffman tree */ - - -/* =========================================================================== - * Remove the smallest element from the heap and recreate the heap with - * one less element. Updates heap and heap_len. - */ -#define pqremove(s, tree, top) \ -{\ - top = s->heap[SMALLEST]; \ - s->heap[SMALLEST] = s->heap[s->heap_len--]; \ - pqdownheap(s, tree, SMALLEST); \ -} - -/* =========================================================================== - * Compares to subtrees, using the tree depth as tie breaker when - * the subtrees have equal frequency. This minimizes the worst case length. - */ -#define smaller(tree, n, m, depth) \ - (tree[n].Freq < tree[m].Freq || \ - (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) - -/* =========================================================================== - * Restore the heap property by moving down the tree starting at node k, - * exchanging a node with the smallest of its two sons if necessary, stopping - * when the heap property is re-established (each father smaller than its - * two sons). - */ -static void pqdownheap(s, tree, k) - deflate_state *s; - ct_data *tree; /* the tree to restore */ - int k; /* node to move down */ -{ - int v = s->heap[k]; - int j = k << 1; /* left son of k */ - while (j <= s->heap_len) { - /* Set j to the smallest of the two sons: */ - if (j < s->heap_len && - smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { - j++; - } - /* Exit if v is smaller than both sons */ - if (smaller(tree, v, s->heap[j], s->depth)) break; - - /* Exchange v with the smallest son */ - s->heap[k] = s->heap[j]; k = j; - - /* And continue down the tree, setting j to the left son of k */ - j <<= 1; - } - s->heap[k] = v; -} - -/* =========================================================================== - * Compute the optimal bit lengths for a tree and update the total bit length - * for the current block. - * IN assertion: the fields freq and dad are set, heap[heap_max] and - * above are the tree nodes sorted by increasing frequency. - * OUT assertions: the field len is set to the optimal bit length, the - * array bl_count contains the frequencies for each bit length. - * The length opt_len is updated; static_len is also updated if stree is - * not null. - */ -static void gen_bitlen(s, desc) - deflate_state *s; - tree_desc *desc; /* the tree descriptor */ -{ - ct_data *tree = desc->dyn_tree; - int max_code = desc->max_code; - const ct_data *stree = desc->stat_desc->static_tree; - const intf *extra = desc->stat_desc->extra_bits; - int base = desc->stat_desc->extra_base; - int max_length = desc->stat_desc->max_length; - int h; /* heap index */ - int n, m; /* iterate over the tree elements */ - int bits; /* bit length */ - int xbits; /* extra bits */ - uint16_t f; /* frequency */ - int overflow = 0; /* number of elements with bit length too large */ - - for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; - - /* In a first pass, compute the optimal bit lengths (which may - * overflow in the case of the bit length tree). - */ - tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ - - for (h = s->heap_max+1; h < HEAP_SIZE; h++) { - n = s->heap[h]; - bits = tree[tree[n].Dad].Len + 1; - if (bits > max_length) bits = max_length, overflow++; - tree[n].Len = (uint16_t)bits; - /* We overwrite tree[n].Dad which is no longer needed */ - - if (n > max_code) continue; /* not a leaf node */ - - s->bl_count[bits]++; - xbits = 0; - if (n >= base) xbits = extra[n-base]; - f = tree[n].Freq; - s->opt_len += (ulg)f * (bits + xbits); - if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); - } - if (overflow == 0) return; - - Trace((stderr,"\nbit length overflow\n")); - /* This happens for example on obj2 and pic of the Calgary corpus */ - - /* Find the first bit length which could increase: */ - do { - bits = max_length-1; - while (s->bl_count[bits] == 0) bits--; - s->bl_count[bits]--; /* move one leaf down the tree */ - s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ - s->bl_count[max_length]--; - /* The brother of the overflow item also moves one step up, - * but this does not affect bl_count[max_length] - */ - overflow -= 2; - } while (overflow > 0); - - /* Now recompute all bit lengths, scanning in increasing frequency. - * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all - * lengths instead of fixing only the wrong ones. This idea is taken - * from 'ar' written by Haruhiko Okumura.) - */ - for (bits = max_length; bits != 0; bits--) { - n = s->bl_count[bits]; - while (n != 0) { - m = s->heap[--h]; - if (m > max_code) continue; - if ((unsigned) tree[m].Len != (unsigned) bits) { - Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); - s->opt_len += ((long)bits - (long)tree[m].Len) - *(long)tree[m].Freq; - tree[m].Len = (uint16_t)bits; - } - n--; - } - } -} - -/* =========================================================================== - * Generate the codes for a given tree and bit counts (which need not be - * optimal). - * IN assertion: the array bl_count contains the bit length statistics for - * the given tree and the field len is set for all tree elements. - * OUT assertion: the field code is set for all tree elements of non - * zero code length. - */ -static void gen_codes (tree, max_code, bl_count) - ct_data *tree; /* the tree to decorate */ - int max_code; /* largest code with non zero frequency */ - uint16_t *bl_count; /* number of codes at each bit length */ -{ - uint16_t next_code[MAX_BITS+1]; /* next code value for each bit length */ - uint16_t code = 0; /* running code value */ - int bits; /* bit index */ - int n; /* code index */ - - /* The distribution counts are first used to generate the code values - * without bit reversal. - */ - for (bits = 1; bits <= MAX_BITS; bits++) { - next_code[bits] = code = (code + bl_count[bits-1]) << 1; - } - /* Check that the bit counts in bl_count are consistent. The last code - * must be all ones. - */ - Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; - const ct_data *stree = desc->stat_desc->static_tree; - int elems = desc->stat_desc->elems; - int n, m; /* iterate over heap elements */ - int max_code = -1; /* largest code with non zero frequency */ - int node; /* new node being created */ - - /* Construct the initial heap, with least frequent element in - * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - * heap[0] is not used. - */ - s->heap_len = 0, s->heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) { - if (tree[n].Freq != 0) { - s->heap[++(s->heap_len)] = max_code = n; - s->depth[n] = 0; - } else { - tree[n].Len = 0; - } - } - - /* The pkzip format requires that at least one distance code exists, - * and that at least one bit should be sent even if there is only one - * possible code. So to avoid special checks later on we force at least - * two codes of non zero frequency. - */ - while (s->heap_len < 2) { - node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); - tree[node].Freq = 1; - s->depth[node] = 0; - s->opt_len--; if (stree) s->static_len -= stree[node].Len; - /* node is 0 or 1 so it does not have extra bits */ - } - desc->max_code = max_code; - - /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - * establish sub-heaps of increasing lengths: - */ - for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); - - /* Construct the Huffman tree by repeatedly combining the least two - * frequent nodes. - */ - node = elems; /* next internal node of the tree */ - do { - pqremove(s, tree, n); /* n = node of least frequency */ - m = s->heap[SMALLEST]; /* m = node of next least frequency */ - - s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ - s->heap[--(s->heap_max)] = m; - - /* Create a new node father of n and m */ - tree[node].Freq = tree[n].Freq + tree[m].Freq; - s->depth[node] = (uint8_t)((s->depth[n] >= s->depth[m] ? - s->depth[n] : s->depth[m]) + 1); - tree[n].Dad = tree[m].Dad = (uint16_t)node; -#ifdef DUMP_BL_TREE - if (tree == s->bl_tree) { - fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", - node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); - } -#endif - /* and insert the new node in the heap */ - s->heap[SMALLEST] = node++; - pqdownheap(s, tree, SMALLEST); - - } while (s->heap_len >= 2); - - s->heap[--(s->heap_max)] = s->heap[SMALLEST]; - - /* At this point, the fields freq and dad are set. We can now - * generate the bit lengths. - */ - gen_bitlen(s, (tree_desc *)desc); - - /* The field len is now set, we can generate the bit codes */ - gen_codes ((ct_data *)tree, max_code, s->bl_count); -} - -/* =========================================================================== - * Scan a literal or distance tree to determine the frequencies of the codes - * in the bit length tree. - */ -static void scan_tree (s, tree, max_code) - deflate_state *s; - ct_data *tree; /* the tree to be scanned */ - int max_code; /* and its largest code of non zero frequency */ -{ - int n; /* iterates over all tree elements */ - int prevlen = -1; /* last emitted length */ - int curlen; /* length of current code */ - int nextlen = tree[0].Len; /* length of next code */ - int count = 0; /* repeat count of the current code */ - int max_count = 7; /* max repeat count */ - int min_count = 4; /* min repeat count */ - - if (nextlen == 0) max_count = 138, min_count = 3; - tree[max_code+1].Len = (uint16_t)0xffff; /* guard */ - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[n+1].Len; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - s->bl_tree[curlen].Freq += count; - } else if (curlen != 0) { - if (curlen != prevlen) s->bl_tree[curlen].Freq++; - s->bl_tree[REP_3_6].Freq++; - } else if (count <= 10) { - s->bl_tree[REPZ_3_10].Freq++; - } else { - s->bl_tree[REPZ_11_138].Freq++; - } - count = 0; prevlen = curlen; - if (nextlen == 0) { - max_count = 138, min_count = 3; - } else if (curlen == nextlen) { - max_count = 6, min_count = 3; - } else { - max_count = 7, min_count = 4; - } - } -} - -/* =========================================================================== - * Send a literal or distance tree in compressed form, using the codes in - * bl_tree. - */ -static void send_tree (s, tree, max_code) - deflate_state *s; - ct_data *tree; /* the tree to be scanned */ - int max_code; /* and its largest code of non zero frequency */ -{ - int n; /* iterates over all tree elements */ - int prevlen = -1; /* last emitted length */ - int curlen; /* length of current code */ - int nextlen = tree[0].Len; /* length of next code */ - int count = 0; /* repeat count of the current code */ - int max_count = 7; /* max repeat count */ - int min_count = 4; /* min repeat count */ - - /* tree[max_code+1].Len = -1; */ /* guard already set */ - if (nextlen == 0) max_count = 138, min_count = 3; - - for (n = 0; n <= max_code; n++) { - curlen = nextlen; nextlen = tree[n+1].Len; - if (++count < max_count && curlen == nextlen) { - continue; - } else if (count < min_count) { - do { send_code(s, curlen, s->bl_tree); } while (--count != 0); - - } else if (curlen != 0) { - if (curlen != prevlen) { - send_code(s, curlen, s->bl_tree); count--; - } - Assert(count >= 3 && count <= 6, " 3_6?"); - send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); - - } else if (count <= 10) { - send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); - - } else { - send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); - } - count = 0; prevlen = curlen; - if (nextlen == 0) { - max_count = 138, min_count = 3; - } else if (curlen == nextlen) { - max_count = 6, min_count = 3; - } else { - max_count = 7, min_count = 4; - } - } -} - -/* =========================================================================== - * Construct the Huffman tree for the bit lengths and return the index in - * bl_order of the last bit length code to send. - */ -static int build_bl_tree(s) - deflate_state *s; -{ - int max_blindex; /* index of last bit length code of non zero freq */ - - /* Determine the bit length frequencies for literal and distance trees */ - scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); - scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); - - /* Build the bit length tree: */ - build_tree(s, (tree_desc *)(&(s->bl_desc))); - /* opt_len now includes the length of the tree representations, except - * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. - */ - - /* Determine the number of bit length codes to send. The pkzip format - * requires that at least 4 bit length codes be sent. (appnote.txt says - * 3 but the actual value used is 4.) - */ - for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { - if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; - } - /* Update opt_len to include the bit length tree and counts */ - s->opt_len += 3*(max_blindex+1) + 5+5+4; - Tracev((stderr, "\ndyn trees: dyn %lld, stat %lld", - s->opt_len, s->static_len)); - - return max_blindex; -} - -/* =========================================================================== - * Send the header for a block using dynamic Huffman trees: the counts, the - * lengths of the bit length codes, the literal tree and the distance tree. - * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - */ -static void send_all_trees(s, lcodes, dcodes, blcodes) - deflate_state *s; - int lcodes, dcodes, blcodes; /* number of codes for each tree */ -{ - int rank; /* index in bl_order */ - - Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); - Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, - "too many codes"); - Tracev((stderr, "\nbl counts: ")); - send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ - send_bits(s, dcodes-1, 5); - send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ - for (rank = 0; rank < blcodes; rank++) { - Tracev((stderr, "\nbl code %2d ", bl_order[rank])); - send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); - } - Tracev((stderr, "\nbl tree: sent %lld", s->bits_sent)); - - send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ - Tracev((stderr, "\nlit tree: sent %lld", s->bits_sent)); - - send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ - Tracev((stderr, "\ndist tree: sent %lld", s->bits_sent)); -} - -/* =========================================================================== - * Send a stored block - */ -void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) - deflate_state *s; - uint8_t *buf; /* input block */ - uint64_t stored_len; /* length of input block */ - int last; /* one if this is the last block for a file */ -{ - send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ -#ifdef DEBUG - s->compressed_len = (s->compressed_len + 3 + 7) & (uint64_t)~7L; - s->compressed_len += (stored_len + 4) << 3; -#endif - copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ -} - -/* =========================================================================== - * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) - */ -void ZLIB_INTERNAL _tr_flush_bits(s) - deflate_state *s; -{ - bi_flush(s); -} - -/* =========================================================================== - * Send one empty static block to give enough lookahead for inflate. - * This takes 10 bits, of which 7 may remain in the bit buffer. - */ -void ZLIB_INTERNAL _tr_align(s) - deflate_state *s; -{ - send_bits(s, STATIC_TREES<<1, 3); - send_code(s, END_BLOCK, static_ltree); -#ifdef DEBUG - s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ -#endif - bi_flush(s); -} - -/* =========================================================================== - * Determine the best encoding for the current block: dynamic trees, static - * trees or store, and output the encoded block to the zip file. - */ -void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) - deflate_state *s; - uint8_t *buf; /* input block, or NULL if too old */ - uint64_t stored_len; /* length of input block */ - int last; /* one if this is the last block for a file */ -{ - uint64_t opt_lenb, static_lenb; /* opt_len and static_len in bytes */ - int max_blindex = 0; /* index of last bit length code of non zero freq */ - - /* Build the Huffman trees unless a stored block is forced */ - if (s->level > 0) { - - /* Check if the file is binary or text */ - if (s->strm->data_type == Z_UNKNOWN) - s->strm->data_type = detect_data_type(s); - - /* Construct the literal and distance trees */ - build_tree(s, (tree_desc *)(&(s->l_desc))); - Tracev((stderr, "\nlit data: dyn %lld, stat %lld", s->opt_len, - s->static_len)); - - build_tree(s, (tree_desc *)(&(s->d_desc))); - Tracev((stderr, "\ndist data: dyn %lld, stat %lld", s->opt_len, - s->static_len)); - /* At this point, opt_len and static_len are the total bit lengths of - * the compressed block data, excluding the tree representations. - */ - - /* Build the bit length tree for the above two trees, and get the index - * in bl_order of the last bit length code to send. - */ - max_blindex = build_bl_tree(s); - - /* Determine the best encoding. Compute the block lengths in bytes. */ - opt_lenb = (s->opt_len+3+7)>>3; - static_lenb = (s->static_len+3+7)>>3; - - Tracev((stderr, "\nopt %llu(%llu) stat %llu(%llu) stored %llu lit %u ", - opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, - s->last_lit)); - - if (static_lenb <= opt_lenb) opt_lenb = static_lenb; - - } else { - Assert(buf != (uint8_t*)0, "lost buf"); - opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ - } - -#ifdef FORCE_STORED - if (buf != NULL) { /* force stored block */ -#else - if (stored_len+4 <= opt_lenb && buf != NULL) { - /* 4: two words for the lengths */ -#endif - /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - * Otherwise we can't have processed more than WSIZE input bytes since - * the last block flush, because compression would have been - * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - * transform a block into a stored block. - */ - _tr_stored_block(s, buf, stored_len, last); - -#ifdef FORCE_STATIC - } else if (static_lenb >= 0) { /* force static trees */ -#else - } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { -#endif - send_bits(s, (STATIC_TREES<<1)+last, 3); - compress_block(s, (const ct_data *)static_ltree, - (const ct_data *)static_dtree); -#ifdef DEBUG - s->compressed_len += 3 + s->static_len; -#endif - } else { - send_bits(s, (DYN_TREES<<1)+last, 3); - send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, - max_blindex+1); - compress_block(s, (const ct_data *)s->dyn_ltree, - (const ct_data *)s->dyn_dtree); -#ifdef DEBUG - s->compressed_len += 3 + s->opt_len; -#endif - } - Assert (s->compressed_len == s->bits_sent, "bad compressed size"); - init_block(s); - - if (last) { - bi_windup(s); -#ifdef DEBUG - s->compressed_len += 7; /* align on byte boundary */ -#endif - } - Tracev((stderr,"\ncomprlen %llu(%llu) ", s->compressed_len>>3, - s->compressed_len-7*last)); -} - -/* =========================================================================== - * Save the match info and tally the frequency counts. Return true if - * the current block must be flushed. - */ -int ZLIB_INTERNAL _tr_tally (s, dist, lc) - deflate_state *s; - unsigned dist; /* distance of matched string */ - unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ -{ - s->d_buf[s->last_lit] = (uint16_t)dist; - s->l_buf[s->last_lit++] = (uint8_t)lc; - if (dist == 0) { - /* lc is the unmatched char */ - s->dyn_ltree[lc].Freq++; - } else { - s->matches++; - /* Here, lc is the match length - MIN_MATCH */ - dist--; /* dist = match distance - 1 */ - Assert((uint16_t)dist < (ush)MAX_DIST(s) && - (uint16_t)lc <= (ush)(MAX_MATCH-MIN_MATCH) && - (uint16_t)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); - - s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; - s->dyn_dtree[d_code(dist)].Freq++; - } - -#ifdef TRUNCATE_BLOCK - /* Try to guess if it is profitable to stop the current block here */ - if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { - /* Compute an upper bound for the compressed length */ - uint64_t out_length = (ulg)s->last_lit*8L; - uint64_t in_length = (ulg)((long)s->strstart - s->block_start); - int dcode; - for (dcode = 0; dcode < D_CODES; dcode++) { - out_length += (ulg)s->dyn_dtree[dcode].Freq * - (5L+extra_dbits[dcode]); - } - out_length >>= 3; - Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", - s->last_lit, in_length, out_length, - 100L - out_length*100L/in_length)); - if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; - } -#endif - return (s->last_lit == s->lit_bufsize-1); - /* We avoid equality with lit_bufsize because of wraparound at 64K - * on 16 bit machines and because stored blocks are restricted to - * 64K-1 bytes. - */ -} - -/* =========================================================================== - * Send the block data compressed using the given Huffman trees - */ -static void compress_block(s, ltree, dtree) - deflate_state *s; - const ct_data *ltree; /* literal tree */ - const ct_data *dtree; /* distance tree */ -{ - unsigned dist; /* distance of matched string */ - int lc; /* match length or unmatched char (if dist == 0) */ - unsigned lx = 0; /* running index in l_buf */ - unsigned code; /* the code to send */ - int extra; /* number of extra bits to send */ - - uint64_t bit_buf = s->bi_buf; - int filled = s->bi_valid; - - uint64_t val ; - int len ; - - if (s->last_lit != 0) do { - dist = s->d_buf[lx]; - lc = s->l_buf[lx++]; - if (dist == 0) { - uint64_t val = ltree[lc].Code; - int len = ltree[lc].Len; -#ifdef DEBUG - Tracevv((stderr," l %2d v %4llx ", len, val)); - Assert(len > 0 && len <= 64, "invalid length"); - s->bits_sent += len; -#endif - bit_buf ^= (val << filled); - filled += len; - - if(filled >= 64) { - *(uint64_t*)(&s->pending_buf[s->pending]) = bit_buf; - s->pending += 8; - filled -= 64; - bit_buf = val >> (len - filled); - } - - Tracecv(isgraph(lc), (stderr," '%c' ", lc)); - } else { - uint64_t val ; - int len ; - /* Here, lc is the match length - MIN_MATCH */ - code = _length_code[lc]; - - val = ltree[code+LITERALS+1].Code; - len = ltree[code+LITERALS+1].Len; -#ifdef DEBUG - Tracevv((stderr," l %2d v %4llx ", len, val)); - Assert(len > 0 && len <= 64, "invalid length"); - s->bits_sent += len; -#endif - bit_buf ^= (val << filled); - filled += len; - if(filled >= 64) { - *(uint64_t*)(&s->pending_buf[s->pending]) = bit_buf; - s->pending += 8; - filled -= 64; - bit_buf = val >> (len - filled); - } - - extra = extra_lbits[code]; - if (extra != 0) { - lc -= base_length[code]; - val = lc; - len = extra; -#ifdef DEBUG - Tracevv((stderr," l %2d v %4llx ", len, val)); - Assert(len > 0 && len <= 64, "invalid length"); - s->bits_sent += len; -#endif - bit_buf ^= (val << filled); - filled += len; - - if(filled >= 64) { - *(uint64_t*)(&s->pending_buf[s->pending]) = bit_buf; - s->pending += 8; - filled -= 64; - bit_buf = val >> (len - filled); - } - } - dist--; /* dist is now the match distance - 1 */ - code = d_code(dist); - Assert (code < D_CODES, "bad d_code"); - val = dtree[code].Code; - len = dtree[code].Len; -#ifdef DEBUG - Tracevv((stderr," l %2d v %4llx ", len, val)); - Assert(len > 0 && len <= 64, "invalid length"); - s->bits_sent += len; -#endif - bit_buf ^= (val << filled); - filled += len; - if(filled >= 64) { - *(uint64_t*)(&s->pending_buf[s->pending]) = bit_buf; - s->pending += 8; - filled -= 64; - bit_buf = val >> (len - filled); - } - - extra = extra_dbits[code]; - if (extra != 0) { - dist -= base_dist[code]; - - val = dist; - len = extra; - bit_buf ^= (val << filled); - filled += len; -#ifdef DEBUG - Tracevv((stderr," l %2d v %4llx ", len, val)); - Assert(len > 0 && len <= 64, "invalid length"); - s->bits_sent += len; -#endif - if(filled >= 64) { - *(uint64_t*)(&s->pending_buf[s->pending]) = bit_buf; - s->pending += 8; - filled -= 64; - bit_buf = val >> (len - filled); - } - } - } /* literal or match pair ? */ - - /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ - Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, - "pendingBuf overflow"); - - } while (lx < s->last_lit); - val = ltree[END_BLOCK].Code; - len = ltree[END_BLOCK].Len; - -#ifdef DEBUG - Tracevv((stderr," l %2d v %4llx ", len, val)); - Assert(len > 0 && len <= 64, "invalid length"); - s->bits_sent += len; -#endif - bit_buf ^= (val << filled); - filled += len; - - if(filled >= 64) { - *(uint64_t*)(&s->pending_buf[s->pending]) = bit_buf; - s->pending += 8; - filled -= 64; - bit_buf = val >> (len - filled); - } - - s->bi_buf = bit_buf; - s->bi_valid = filled; -} - -/* =========================================================================== - * Check if the data type is TEXT or BINARY, using the following algorithm: - * - TEXT if the two conditions below are satisfied: - * a) There are no non-portable control characters belonging to the - * "black list" (0..6, 14..25, 28..31). - * b) There is at least one printable character belonging to the - * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). - * - BINARY otherwise. - * - The following partially-portable control characters form a - * "gray list" that is ignored in this detection algorithm: - * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). - * IN assertion: the fields Freq of dyn_ltree are set. - */ -static int detect_data_type(s) - deflate_state *s; -{ - /* black_mask is the bit mask of black-listed bytes - * set bits 0..6, 14..25, and 28..31 - * 0xf3ffc07f = binary 11110011111111111100000001111111 - */ - unsigned long black_mask = 0xf3ffc07fUL; - int n; - - /* Check for non-textual ("black-listed") bytes. */ - for (n = 0; n <= 31; n++, black_mask >>= 1) - if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) - return Z_BINARY; - - /* Check for textual ("white-listed") bytes. */ - if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 - || s->dyn_ltree[13].Freq != 0) - return Z_TEXT; - for (n = 32; n < LITERALS; n++) - if (s->dyn_ltree[n].Freq != 0) - return Z_TEXT; - - /* There are no "black-listed" or "white-listed" bytes: - * this stream either is empty or has tolerated ("gray-listed") bytes only. - */ - return Z_BINARY; -} - -/* =========================================================================== - * Reverse the first len bits of a code, using straightforward code (a faster - * method would use a table) - * IN assertion: 1 <= len <= 15 - */ -static unsigned bi_reverse(code, len) - unsigned code; /* the value to invert */ - int len; /* its bit length */ -{ - register unsigned res = 0; - do { - res |= code & 1; - code >>= 1, res <<= 1; - } while (--len > 0); - return res >> 1; -} - -/* =========================================================================== - * Flush the bit buffer, keeping at most 7 bits in it. - */ -static void bi_flush(s) - deflate_state *s; -{ - while (s->bi_valid >= 16) { - put_short(s, s->bi_buf); - s->bi_buf >>= 16; - s->bi_valid -= 16; - } - if (s->bi_valid >= 8) { - put_byte(s, (Byte)s->bi_buf); - s->bi_buf >>= 8; - s->bi_valid -= 8; - } -} - -/* =========================================================================== - * Flush the bit buffer and align the output on a byte boundary - */ -static void bi_windup(s) - deflate_state *s; -{ - while (s->bi_valid >= 16) { - put_short(s, s->bi_buf); - s->bi_buf >>= 16; - s->bi_valid -= 16; - } - if (s->bi_valid > 8) { - put_short(s, s->bi_buf); - } else if (s->bi_valid > 0) { - put_byte(s, (Byte)s->bi_buf); - } - s->bi_buf = 0; - s->bi_valid = 0; -#ifdef DEBUG - s->bits_sent = (s->bits_sent+7) & ~7; -#endif -} - -/* =========================================================================== - * Copy a stored block, storing first the length and its - * one's complement if requested. - */ -static void copy_block(s, buf, len, header) - deflate_state *s; - uint8_t *buf; /* the input data */ - unsigned len; /* its length */ - int header; /* true if block header must be written */ -{ - bi_windup(s); /* align on byte boundary */ - - if (header) { - put_short(s, (uint16_t)len); - put_short(s, (uint16_t)~len); -#ifdef DEBUG - s->bits_sent += 2*16; -#endif - } -#ifdef DEBUG - s->bits_sent += (ulg)len<<3; -#endif - while (len--) { - put_byte(s, *buf++); - } -} diff --git a/builtins/zlib/uncompr.c b/builtins/zlib/uncompr.c deleted file mode 100644 index ef963213a54e0..0000000000000 --- a/builtins/zlib/uncompr.c +++ /dev/null @@ -1,55 +0,0 @@ -/* uncompr.c -- decompress a memory buffer - * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#define ZLIB_INTERNAL -#include "zlib.h" - -/* =========================================================================== - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted. -*/ -int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen) -{ - z_stream stream; - int err; - - stream.next_in = (z_const Bytef *)source; - stream.avail_in = (uInt)sourceLen; - /* Check for source > 64K on 16-bit machine: */ - if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; - - stream.next_out = dest; - stream.avail_out = (uInt)*destLen; - if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; - - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - - err = inflateInit(&stream); - if (err != Z_OK) return err; - - err = inflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - inflateEnd(&stream); - if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) - return Z_DATA_ERROR; - return err; - } - *destLen = stream.total_out; - - err = inflateEnd(&stream); - return err; -} diff --git a/builtins/zlib/zconf.h b/builtins/zlib/zconf.h deleted file mode 100644 index 9987a775530c0..0000000000000 --- a/builtins/zlib/zconf.h +++ /dev/null @@ -1,511 +0,0 @@ -/* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2013 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#ifndef ZCONF_H -#define ZCONF_H - -/* - * If you *really* need a unique prefix for all types and library functions, - * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. - * Even better than compiling with -DZ_PREFIX would be to use configure to set - * this permanently in zconf.h using "./configure --zprefix". - */ -#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ -# define Z_PREFIX_SET - -/* all linked symbols */ -# define _dist_code z__dist_code -# define _length_code z__length_code -# define _tr_align z__tr_align -# define _tr_flush_bits z__tr_flush_bits -# define _tr_flush_block z__tr_flush_block -# define _tr_init z__tr_init -# define _tr_stored_block z__tr_stored_block -# define _tr_tally z__tr_tally -# define adler32 z_adler32 -# define adler32_combine z_adler32_combine -# define adler32_combine64 z_adler32_combine64 -# ifndef Z_SOLO -# define compress z_compress -# define compress2 z_compress2 -# define compressBound z_compressBound -# endif -# define crc32 z_crc32 -# define crc32_combine z_crc32_combine -# define crc32_combine64 z_crc32_combine64 -# define deflate z_deflate -# define deflateBound z_deflateBound -# define deflateCopy z_deflateCopy -# define deflateEnd z_deflateEnd -# define deflateInit2_ z_deflateInit2_ -# define deflateInit_ z_deflateInit_ -# define deflateParams z_deflateParams -# define deflatePending z_deflatePending -# define deflatePrime z_deflatePrime -# define deflateReset z_deflateReset -# define deflateResetKeep z_deflateResetKeep -# define deflateSetDictionary z_deflateSetDictionary -# define deflateSetHeader z_deflateSetHeader -# define deflateTune z_deflateTune -# define deflate_copyright z_deflate_copyright -# define get_crc_table z_get_crc_table -# ifndef Z_SOLO -# define gz_error z_gz_error -# define gz_intmax z_gz_intmax -# define gz_strwinerror z_gz_strwinerror -# define gzbuffer z_gzbuffer -# define gzclearerr z_gzclearerr -# define gzclose z_gzclose -# define gzclose_r z_gzclose_r -# define gzclose_w z_gzclose_w -# define gzdirect z_gzdirect -# define gzdopen z_gzdopen -# define gzeof z_gzeof -# define gzerror z_gzerror -# define gzflush z_gzflush -# define gzgetc z_gzgetc -# define gzgetc_ z_gzgetc_ -# define gzgets z_gzgets -# define gzoffset z_gzoffset -# define gzoffset64 z_gzoffset64 -# define gzopen z_gzopen -# define gzopen64 z_gzopen64 -# ifdef _WIN32 -# define gzopen_w z_gzopen_w -# endif -# define gzprintf z_gzprintf -# define gzvprintf z_gzvprintf -# define gzputc z_gzputc -# define gzputs z_gzputs -# define gzread z_gzread -# define gzrewind z_gzrewind -# define gzseek z_gzseek -# define gzseek64 z_gzseek64 -# define gzsetparams z_gzsetparams -# define gztell z_gztell -# define gztell64 z_gztell64 -# define gzungetc z_gzungetc -# define gzwrite z_gzwrite -# endif -# define inflate z_inflate -# define inflateBack z_inflateBack -# define inflateBackEnd z_inflateBackEnd -# define inflateBackInit_ z_inflateBackInit_ -# define inflateCopy z_inflateCopy -# define inflateEnd z_inflateEnd -# define inflateGetHeader z_inflateGetHeader -# define inflateInit2_ z_inflateInit2_ -# define inflateInit_ z_inflateInit_ -# define inflateMark z_inflateMark -# define inflatePrime z_inflatePrime -# define inflateReset z_inflateReset -# define inflateReset2 z_inflateReset2 -# define inflateSetDictionary z_inflateSetDictionary -# define inflateGetDictionary z_inflateGetDictionary -# define inflateSync z_inflateSync -# define inflateSyncPoint z_inflateSyncPoint -# define inflateUndermine z_inflateUndermine -# define inflateResetKeep z_inflateResetKeep -# define inflate_copyright z_inflate_copyright -# define inflate_fast z_inflate_fast -# define inflate_table z_inflate_table -# ifndef Z_SOLO -# define uncompress z_uncompress -# endif -# define zError z_zError -# ifndef Z_SOLO -# define zcalloc z_zcalloc -# define zcfree z_zcfree -# endif -# define zlibCompileFlags z_zlibCompileFlags -# define zlibVersion z_zlibVersion - -/* all zlib typedefs in zlib.h and zconf.h */ -# define Byte z_Byte -# define Bytef z_Bytef -# define alloc_func z_alloc_func -# define charf z_charf -# define free_func z_free_func -# ifndef Z_SOLO -# define gzFile z_gzFile -# endif -# define gz_header z_gz_header -# define gz_headerp z_gz_headerp -# define in_func z_in_func -# define intf z_intf -# define out_func z_out_func -# define uInt z_uInt -# define uIntf z_uIntf -# define uLong z_uLong -# define uLongf z_uLongf -# define voidp z_voidp -# define voidpc z_voidpc -# define voidpf z_voidpf - -/* all zlib structs in zlib.h and zconf.h */ -# define gz_header_s z_gz_header_s -# define internal_state z_internal_state - -#endif - -#if defined(__MSDOS__) && !defined(MSDOS) -# define MSDOS -#endif -#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) -# define OS2 -#endif -#if defined(_WINDOWS) && !defined(WINDOWS) -# define WINDOWS -#endif -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) -# ifndef WIN32 -# define WIN32 -# endif -#endif -#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) -# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) -# ifndef SYS16BIT -# define SYS16BIT -# endif -# endif -#endif - -/* - * Compile with -DMAXSEG_64K if the alloc function cannot allocate more - * than 64k bytes at a time (needed on systems with 16-bit int). - */ -#ifdef SYS16BIT -# define MAXSEG_64K -#endif -#ifdef MSDOS -# define UNALIGNED_OK -#endif - -#ifdef __STDC_VERSION__ -# ifndef STDC -# define STDC -# endif -# if __STDC_VERSION__ >= 199901L -# ifndef STDC99 -# define STDC99 -# endif -# endif -#endif -#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) -# define STDC -#endif -#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) -# define STDC -#endif -#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) -# define STDC -#endif -#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) -# define STDC -#endif - -#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ -# define STDC -#endif - -#ifndef STDC -# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ -# define const /* note: need a more gentle solution here */ -# endif -#endif - -#if defined(ZLIB_CONST) && !defined(z_const) -# define z_const const -#else -# define z_const -#endif - -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) -# define NO_DUMMY_DECL -#endif - -/* Maximum value for memLevel in deflateInit2 */ -#ifndef MAX_MEM_LEVEL -# ifdef MAXSEG_64K -# define MAX_MEM_LEVEL 8 -# else -# define MAX_MEM_LEVEL 9 -# endif -#endif - -/* Maximum value for windowBits in deflateInit2 and inflateInit2. - * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files - * created by gzip. (Files created by minigzip can still be extracted by - * gzip.) - */ -#ifndef MAX_WBITS -# define MAX_WBITS 15 /* 32K LZ77 window */ -#endif - -/* The memory requirements for deflate are (in bytes): - (1 << (windowBits+2)) + (1 << (memLevel+9)) - that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) - plus a few kilobytes for small objects. For example, if you want to reduce - the default memory requirements from 256K to 128K, compile with - make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" - Of course this will generally degrade compression (there's no free lunch). - - The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes - for small objects. -*/ - - /* Type declarations */ - -#ifndef OF /* function prototypes */ -# ifdef STDC -# define OF(args) args -# else -# define OF(args) () -# endif -#endif - -#ifndef Z_ARG /* function prototypes for stdarg */ -# if defined(STDC) || defined(Z_HAVE_STDARG_H) -# define Z_ARG(args) args -# else -# define Z_ARG(args) () -# endif -#endif - -/* The following definitions for FAR are needed only for MSDOS mixed - * model programming (small or medium model with some far allocations). - * This was tested only with MSC; for other MSDOS compilers you may have - * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, - * just define FAR to be empty. - */ -#ifdef SYS16BIT -# if defined(M_I86SM) || defined(M_I86MM) - /* MSC small or medium model */ -# define SMALL_MEDIUM -# ifdef _MSC_VER -# define FAR _far -# else -# define FAR far -# endif -# endif -# if (defined(__SMALL__) || defined(__MEDIUM__)) - /* Turbo C small or medium model */ -# define SMALL_MEDIUM -# ifdef __BORLANDC__ -# define FAR _far -# else -# define FAR far -# endif -# endif -#endif - -#if defined(WINDOWS) || defined(WIN32) - /* If building or using zlib as a DLL, define ZLIB_DLL. - * This is not mandatory, but it offers a little performance increase. - */ -# ifdef ZLIB_DLL -# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) -# ifdef ZLIB_INTERNAL -# define ZEXTERN extern __declspec(dllexport) -# else -# define ZEXTERN extern __declspec(dllimport) -# endif -# endif -# endif /* ZLIB_DLL */ - /* If building or using zlib with the WINAPI/WINAPIV calling convention, - * define ZLIB_WINAPI. - * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. - */ -# ifdef ZLIB_WINAPI -# ifdef FAR -# undef FAR -# endif -# include - /* No need for _export, use ZLIB.DEF instead. */ - /* For complete Windows compatibility, use WINAPI, not __stdcall. */ -# define ZEXPORT WINAPI -# ifdef WIN32 -# define ZEXPORTVA WINAPIV -# else -# define ZEXPORTVA FAR CDECL -# endif -# endif -#endif - -#if defined (__BEOS__) -# ifdef ZLIB_DLL -# ifdef ZLIB_INTERNAL -# define ZEXPORT __declspec(dllexport) -# define ZEXPORTVA __declspec(dllexport) -# else -# define ZEXPORT __declspec(dllimport) -# define ZEXPORTVA __declspec(dllimport) -# endif -# endif -#endif - -#ifndef ZEXTERN -# define ZEXTERN extern -#endif -#ifndef ZEXPORT -# define ZEXPORT -#endif -#ifndef ZEXPORTVA -# define ZEXPORTVA -#endif - -#ifndef FAR -# define FAR -#endif - -#if !defined(__MACTYPES__) -typedef unsigned char Byte; /* 8 bits */ -#endif -typedef unsigned int uInt; /* 16 bits or more */ -typedef unsigned long uLong; /* 32 bits or more */ - -#ifdef SMALL_MEDIUM - /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ -# define Bytef Byte FAR -#else - typedef Byte FAR Bytef; -#endif -typedef char FAR charf; -typedef int FAR intf; -typedef uInt FAR uIntf; -typedef uLong FAR uLongf; - -#ifdef STDC - typedef void const *voidpc; - typedef void FAR *voidpf; - typedef void *voidp; -#else - typedef Byte const *voidpc; - typedef Byte FAR *voidpf; - typedef Byte *voidp; -#endif - -#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) -# include -# if (UINT_MAX == 0xffffffffUL) -# define Z_U4 unsigned -# elif (ULONG_MAX == 0xffffffffUL) -# define Z_U4 unsigned long -# elif (USHRT_MAX == 0xffffffffUL) -# define Z_U4 unsigned short -# endif -#endif - -#ifdef Z_U4 - typedef Z_U4 z_crc_t; -#else - typedef unsigned long z_crc_t; -#endif - -#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ -# define Z_HAVE_UNISTD_H -#endif - -#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ -# define Z_HAVE_STDARG_H -#endif - -#ifdef STDC -# ifndef Z_SOLO -# include /* for off_t */ -# endif -#endif - -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -# ifndef Z_SOLO -# include /* for va_list */ -# endif -#endif - -#ifdef _WIN32 -# ifndef Z_SOLO -# include /* for wchar_t */ -# endif -#endif - -/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and - * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even - * though the former does not conform to the LFS document), but considering - * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as - * equivalently requesting no 64-bit operations - */ -#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 -# undef _LARGEFILE64_SOURCE -#endif - -#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) -# define Z_HAVE_UNISTD_H -#endif -#ifndef Z_SOLO -# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) -# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ -# ifdef VMS -# include /* for off_t */ -# endif -# ifndef z_off_t -# define z_off_t off_t -# endif -# endif -#endif - -#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 -# define Z_LFS64 -#endif - -#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) -# define Z_LARGE64 -#endif - -#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) -# define Z_WANT64 -#endif - -#if !defined(SEEK_SET) && !defined(Z_SOLO) -# define SEEK_SET 0 /* Seek from beginning of file. */ -# define SEEK_CUR 1 /* Seek from current position. */ -# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ -#endif - -#ifndef z_off_t -# define z_off_t long -#endif - -#if !defined(_WIN32) && defined(Z_LARGE64) -# define z_off64_t off64_t -#else -# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) -# define z_off64_t __int64 -# else -# define z_off64_t z_off_t -# endif -#endif - -/* MVS linker does not support external names larger than 8 bytes */ -#if defined(__MVS__) - #pragma map(deflateInit_,"DEIN") - #pragma map(deflateInit2_,"DEIN2") - #pragma map(deflateEnd,"DEEND") - #pragma map(deflateBound,"DEBND") - #pragma map(inflateInit_,"ININ") - #pragma map(inflateInit2_,"ININ2") - #pragma map(inflateEnd,"INEND") - #pragma map(inflateSync,"INSY") - #pragma map(inflateSetDictionary,"INSEDI") - #pragma map(compressBound,"CMBND") - #pragma map(inflate_table,"INTABL") - #pragma map(inflate_fast,"INFA") - #pragma map(inflate_copyright,"INCOPY") -#endif - -#endif /* ZCONF_H */ diff --git a/builtins/zlib/zconf_cf.h b/builtins/zlib/zconf_cf.h deleted file mode 100644 index f0cc91bf3b5f4..0000000000000 --- a/builtins/zlib/zconf_cf.h +++ /dev/null @@ -1,511 +0,0 @@ -/* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2013 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#ifndef ZCONF_H -#define ZCONF_H -#include -/* - * If you *really* need a unique prefix for all types and library functions, - * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. - * Even better than compiling with -DZ_PREFIX would be to use configure to set - * this permanently in zconf.h using "./configure --zprefix". - */ -#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ -# define Z_PREFIX_SET - -/* all linked symbols */ -# define _dist_code z__dist_code -# define _length_code z__length_code -# define _tr_align z__tr_align -# define _tr_flush_bits z__tr_flush_bits -# define _tr_flush_block z__tr_flush_block -# define _tr_init z__tr_init -# define _tr_stored_block z__tr_stored_block -# define _tr_tally z__tr_tally -# define adler32 z_adler32 -# define adler32_combine z_adler32_combine -# define adler32_combine64 z_adler32_combine64 -# ifndef Z_SOLO -# define compress z_compress -# define compress2 z_compress2 -# define compressBound z_compressBound -# endif -# define crc32 z_crc32 -# define crc32_combine z_crc32_combine -# define crc32_combine64 z_crc32_combine64 -# define deflate z_deflate -# define deflateBound z_deflateBound -# define deflateCopy z_deflateCopy -# define deflateEnd z_deflateEnd -# define deflateInit2_ z_deflateInit2_ -# define deflateInit_ z_deflateInit_ -# define deflateParams z_deflateParams -# define deflatePending z_deflatePending -# define deflatePrime z_deflatePrime -# define deflateReset z_deflateReset -# define deflateResetKeep z_deflateResetKeep -# define deflateSetDictionary z_deflateSetDictionary -# define deflateSetHeader z_deflateSetHeader -# define deflateTune z_deflateTune -# define deflate_copyright z_deflate_copyright -# define get_crc_table z_get_crc_table -# ifndef Z_SOLO -# define gz_error z_gz_error -# define gz_intmax z_gz_intmax -# define gz_strwinerror z_gz_strwinerror -# define gzbuffer z_gzbuffer -# define gzclearerr z_gzclearerr -# define gzclose z_gzclose -# define gzclose_r z_gzclose_r -# define gzclose_w z_gzclose_w -# define gzdirect z_gzdirect -# define gzdopen z_gzdopen -# define gzeof z_gzeof -# define gzerror z_gzerror -# define gzflush z_gzflush -# define gzgetc z_gzgetc -# define gzgetc_ z_gzgetc_ -# define gzgets z_gzgets -# define gzoffset z_gzoffset -# define gzoffset64 z_gzoffset64 -# define gzopen z_gzopen -# define gzopen64 z_gzopen64 -# ifdef _WIN32 -# define gzopen_w z_gzopen_w -# endif -# define gzprintf z_gzprintf -# define gzvprintf z_gzvprintf -# define gzputc z_gzputc -# define gzputs z_gzputs -# define gzread z_gzread -# define gzrewind z_gzrewind -# define gzseek z_gzseek -# define gzseek64 z_gzseek64 -# define gzsetparams z_gzsetparams -# define gztell z_gztell -# define gztell64 z_gztell64 -# define gzungetc z_gzungetc -# define gzwrite z_gzwrite -# endif -# define inflate z_inflate -# define inflateBack z_inflateBack -# define inflateBackEnd z_inflateBackEnd -# define inflateBackInit_ z_inflateBackInit_ -# define inflateCopy z_inflateCopy -# define inflateEnd z_inflateEnd -# define inflateGetHeader z_inflateGetHeader -# define inflateInit2_ z_inflateInit2_ -# define inflateInit_ z_inflateInit_ -# define inflateMark z_inflateMark -# define inflatePrime z_inflatePrime -# define inflateReset z_inflateReset -# define inflateReset2 z_inflateReset2 -# define inflateSetDictionary z_inflateSetDictionary -# define inflateGetDictionary z_inflateGetDictionary -# define inflateSync z_inflateSync -# define inflateSyncPoint z_inflateSyncPoint -# define inflateUndermine z_inflateUndermine -# define inflateResetKeep z_inflateResetKeep -# define inflate_copyright z_inflate_copyright -# define inflate_fast z_inflate_fast -# define inflate_table z_inflate_table -# ifndef Z_SOLO -# define uncompress z_uncompress -# endif -# define zError z_zError -# ifndef Z_SOLO -# define zcalloc z_zcalloc -# define zcfree z_zcfree -# endif -# define zlibCompileFlags z_zlibCompileFlags -# define zlibVersion z_zlibVersion - -/* all zlib typedefs in zlib.h and zconf.h */ -# define Byte z_Byte -# define Bytef z_Bytef -# define alloc_func z_alloc_func -# define charf z_charf -# define free_func z_free_func -# ifndef Z_SOLO -# define gzFile z_gzFile -# endif -# define gz_header z_gz_header -# define gz_headerp z_gz_headerp -# define in_func z_in_func -# define intf z_intf -# define out_func z_out_func -# define uInt z_uInt -# define uIntf z_uIntf -# define uLong z_uLong -# define uLongf z_uLongf -# define voidp z_voidp -# define voidpc z_voidpc -# define voidpf z_voidpf - -/* all zlib structs in zlib.h and zconf.h */ -# define gz_header_s z_gz_header_s -# define internal_state z_internal_state - -#endif - -#if defined(__MSDOS__) && !defined(MSDOS) -# define MSDOS -#endif -#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) -# define OS2 -#endif -#if defined(_WINDOWS) && !defined(WINDOWS) -# define WINDOWS -#endif -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) -# ifndef WIN32 -# define WIN32 -# endif -#endif -#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) -# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) -# ifndef SYS16BIT -# define SYS16BIT -# endif -# endif -#endif - -/* - * Compile with -DMAXSEG_64K if the alloc function cannot allocate more - * than 64k bytes at a time (needed on systems with 16-bit int). - */ -#ifdef SYS16BIT -# define MAXSEG_64K -#endif -#ifdef MSDOS -# define UNALIGNED_OK -#endif - -#ifdef __STDC_VERSION__ -# ifndef STDC -# define STDC -# endif -# if __STDC_VERSION__ >= 199901L -# ifndef STDC99 -# define STDC99 -# endif -# endif -#endif -#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) -# define STDC -#endif -#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) -# define STDC -#endif -#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) -# define STDC -#endif -#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) -# define STDC -#endif - -#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ -# define STDC -#endif - -#ifndef STDC -# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ -# define const /* note: need a more gentle solution here */ -# endif -#endif - -#if defined(ZLIB_CONST) && !defined(z_const) -# define z_const const -#else -# define z_const -#endif - -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) -# define NO_DUMMY_DECL -#endif - -/* Maximum value for memLevel in deflateInit2 */ -#ifndef MAX_MEM_LEVEL -# ifdef MAXSEG_64K -# define MAX_MEM_LEVEL 8 -# else -# define MAX_MEM_LEVEL 9 -# endif -#endif - -/* Maximum value for windowBits in deflateInit2 and inflateInit2. - * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files - * created by gzip. (Files created by minigzip can still be extracted by - * gzip.) - */ -#ifndef MAX_WBITS -# define MAX_WBITS 15 /* 32K LZ77 window */ -#endif - -/* The memory requirements for deflate are (in bytes): - (1 << (windowBits+2)) + (1 << (memLevel+9)) - that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) - plus a few kilobytes for small objects. For example, if you want to reduce - the default memory requirements from 256K to 128K, compile with - make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" - Of course this will generally degrade compression (there's no free lunch). - - The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes - for small objects. -*/ - - /* Type declarations */ - -#ifndef OF /* function prototypes */ -# ifdef STDC -# define OF(args) args -# else -# define OF(args) () -# endif -#endif - -#ifndef Z_ARG /* function prototypes for stdarg */ -# if defined(STDC) || defined(Z_HAVE_STDARG_H) -# define Z_ARG(args) args -# else -# define Z_ARG(args) () -# endif -#endif - -/* The following definitions for FAR are needed only for MSDOS mixed - * model programming (small or medium model with some far allocations). - * This was tested only with MSC; for other MSDOS compilers you may have - * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, - * just define FAR to be empty. - */ -#ifdef SYS16BIT -# if defined(M_I86SM) || defined(M_I86MM) - /* MSC small or medium model */ -# define SMALL_MEDIUM -# ifdef _MSC_VER -# define FAR _far -# else -# define FAR far -# endif -# endif -# if (defined(__SMALL__) || defined(__MEDIUM__)) - /* Turbo C small or medium model */ -# define SMALL_MEDIUM -# ifdef __BORLANDC__ -# define FAR _far -# else -# define FAR far -# endif -# endif -#endif - -#if defined(WINDOWS) || defined(WIN32) - /* If building or using zlib as a DLL, define ZLIB_DLL. - * This is not mandatory, but it offers a little performance increase. - */ -# ifdef ZLIB_DLL -# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) -# ifdef ZLIB_INTERNAL -# define ZEXTERN extern __declspec(dllexport) -# else -# define ZEXTERN extern __declspec(dllimport) -# endif -# endif -# endif /* ZLIB_DLL */ - /* If building or using zlib with the WINAPI/WINAPIV calling convention, - * define ZLIB_WINAPI. - * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. - */ -# ifdef ZLIB_WINAPI -# ifdef FAR -# undef FAR -# endif -# include - /* No need for _export, use ZLIB.DEF instead. */ - /* For complete Windows compatibility, use WINAPI, not __stdcall. */ -# define ZEXPORT WINAPI -# ifdef WIN32 -# define ZEXPORTVA WINAPIV -# else -# define ZEXPORTVA FAR CDECL -# endif -# endif -#endif - -#if defined (__BEOS__) -# ifdef ZLIB_DLL -# ifdef ZLIB_INTERNAL -# define ZEXPORT __declspec(dllexport) -# define ZEXPORTVA __declspec(dllexport) -# else -# define ZEXPORT __declspec(dllimport) -# define ZEXPORTVA __declspec(dllimport) -# endif -# endif -#endif - -#ifndef ZEXTERN -# define ZEXTERN extern -#endif -#ifndef ZEXPORT -# define ZEXPORT -#endif -#ifndef ZEXPORTVA -# define ZEXPORTVA -#endif - -#ifndef FAR -# define FAR -#endif - -#if !defined(__MACTYPES__) -typedef uint8_t Byte; /* 8 bits */ -#endif -typedef uint32_t uInt; /* 32 bits */ -typedef uint64_t uLong; /* 64 bits */ - -#ifdef SMALL_MEDIUM - /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ -# define Bytef Byte FAR -#else - typedef Byte FAR Bytef; -#endif -typedef char FAR charf; -typedef int FAR intf; -typedef uInt FAR uIntf; -typedef uLong FAR uLongf; - -#ifdef STDC - typedef void const *voidpc; - typedef void FAR *voidpf; - typedef void *voidp; -#else - typedef Byte const *voidpc; - typedef Byte FAR *voidpf; - typedef Byte *voidp; -#endif - -#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) -# include -# if (UINT_MAX == 0xffffffffUL) -# define Z_U4 unsigned -# elif (ULONG_MAX == 0xffffffffUL) -# define Z_U4 unsigned long -# elif (USHRT_MAX == 0xffffffffUL) -# define Z_U4 unsigned short -# endif -#endif - -#ifdef Z_U4 - typedef Z_U4 z_crc_t; -#else - typedef unsigned long z_crc_t; -#endif - -#if HAVE_UNISTD_H /* was set to #if 1 by ./configure */ -# define Z_HAVE_UNISTD_H -#endif - -#if HAVE_STDARG_H /* was set to #if 1 by ./configure */ -# define Z_HAVE_STDARG_H -#endif - -#ifdef STDC -# ifndef Z_SOLO -# include /* for off_t */ -# endif -#endif - -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -# ifndef Z_SOLO -# include /* for va_list */ -# endif -#endif - -#ifdef _WIN32 -# ifndef Z_SOLO -# include /* for wchar_t */ -# endif -#endif - -/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and - * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even - * though the former does not conform to the LFS document), but considering - * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as - * equivalently requesting no 64-bit operations - */ -#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 -# undef _LARGEFILE64_SOURCE -#endif - -#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) -# define Z_HAVE_UNISTD_H -#endif -#ifndef Z_SOLO -# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) -# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ -# ifdef VMS -# include /* for off_t */ -# endif -# ifndef z_off_t -# define z_off_t off_t -# endif -# endif -#endif - -#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 -# define Z_LFS64 -#endif - -#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) -# define Z_LARGE64 -#endif - -#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) -# define Z_WANT64 -#endif - -#if !defined(SEEK_SET) && !defined(Z_SOLO) -# define SEEK_SET 0 /* Seek from beginning of file. */ -# define SEEK_CUR 1 /* Seek from current position. */ -# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ -#endif - -#ifndef z_off_t -# define z_off_t long -#endif - -#if !defined(_WIN32) && defined(Z_LARGE64) -# define z_off64_t off64_t -#else -# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) -# define z_off64_t __int64 -# else -# define z_off64_t z_off_t -# endif -#endif - -/* MVS linker does not support external names larger than 8 bytes */ -#if defined(__MVS__) - #pragma map(deflateInit_,"DEIN") - #pragma map(deflateInit2_,"DEIN2") - #pragma map(deflateEnd,"DEEND") - #pragma map(deflateBound,"DEBND") - #pragma map(inflateInit_,"ININ") - #pragma map(inflateInit2_,"ININ2") - #pragma map(inflateEnd,"INEND") - #pragma map(inflateSync,"INSY") - #pragma map(inflateSetDictionary,"INSEDI") - #pragma map(compressBound,"CMBND") - #pragma map(inflate_table,"INTABL") - #pragma map(inflate_fast,"INFA") - #pragma map(inflate_copyright,"INCOPY") -#endif - -#endif /* ZCONF_H */ diff --git a/builtins/zlib/zlib.h b/builtins/zlib/zlib.h deleted file mode 100644 index 18fd639067f1a..0000000000000 --- a/builtins/zlib/zlib.h +++ /dev/null @@ -1,1772 +0,0 @@ -/* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.8, April 28th, 2013 - - Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - - - The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 - (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). -*/ - -#ifndef ZLIB_H -#define ZLIB_H - -#ifdef R__HAS_CLOUDFLARE_ZLIB -#include "zconf_cf.h" -#else -#include "zconf.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#define ZLIB_VERSION "1.2.8" -#define ZLIB_VERNUM 0x1280 -#define ZLIB_VER_MAJOR 1 -#define ZLIB_VER_MINOR 2 -#define ZLIB_VER_REVISION 8 -#define ZLIB_VER_SUBREVISION 0 - -/* - The 'zlib' compression library provides in-memory compression and - decompression functions, including integrity checks of the uncompressed data. - This version of the library supports only one compression method (deflation) - but other algorithms will be added later and will have the same stream - interface. - - Compression can be done in a single step if the buffers are large enough, - or can be done by repeated calls of the compression function. In the latter - case, the application must provide more input and/or consume the output - (providing more output space) before each call. - - The compressed data format used by default by the in-memory functions is - the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped - around a deflate stream, which is itself documented in RFC 1951. - - The library also supports reading and writing files in gzip (.gz) format - with an interface similar to that of stdio using the functions that start - with "gz". The gzip format is different from the zlib format. gzip is a - gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. - - This library can optionally read and write gzip streams in memory as well. - - The zlib format was designed to be compact and fast for use in memory - and on communications channels. The gzip format was designed for single- - file compression on file systems, has a larger header than zlib to maintain - directory information, and uses a different, slower check method than zlib. - - The library does not install any signal handler. The decoder checks - the consistency of the compressed data, so the library should never crash - even in case of corrupted input. -*/ - -typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); -typedef void (*free_func) OF((voidpf opaque, voidpf address)); - -struct internal_state; - -typedef struct z_stream_s { - z_const Bytef *next_in; /* next input byte */ - uInt avail_in; /* number of bytes available at next_in */ - uLong total_in; /* total number of input bytes read so far */ - - Bytef *next_out; /* next output byte should be put there */ - uInt avail_out; /* remaining free space at next_out */ - uLong total_out; /* total number of bytes output so far */ - - z_const char *msg; /* last error message, NULL if no error */ - struct internal_state FAR *state; /* not visible by applications */ - - alloc_func zalloc; /* used to allocate the internal state */ - free_func zfree; /* used to free the internal state */ - voidpf opaque; /* private data object passed to zalloc and zfree */ - - int data_type; /* best guess about the data type: binary or text */ - uLong adler; /* adler32 value of the uncompressed data */ - uLong reserved; /* reserved for future use */ -} z_stream; - -typedef z_stream FAR *z_streamp; - -/* - gzip header information passed to and from zlib routines. See RFC 1952 - for more details on the meanings of these fields. -*/ -typedef struct gz_header_s { - int text; /* true if compressed data believed to be text */ - uLong time; /* modification time */ - int xflags; /* extra flags (not used when writing a gzip file) */ - int os; /* operating system */ - Bytef *extra; /* pointer to extra field or Z_NULL if none */ - uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ - uInt extra_max; /* space at extra (only when reading header) */ - Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ - uInt name_max; /* space at name (only when reading header) */ - Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ - uInt comm_max; /* space at comment (only when reading header) */ - int hcrc; /* true if there was or will be a header crc */ - int done; /* true when done reading gzip header (not used - when writing a gzip file) */ -} gz_header; - -typedef gz_header FAR *gz_headerp; - -/* - The application must update next_in and avail_in when avail_in has dropped - to zero. It must update next_out and avail_out when avail_out has dropped - to zero. The application must initialize zalloc, zfree and opaque before - calling the init function. All other fields are set by the compression - library and must not be updated by the application. - - The opaque value provided by the application will be passed as the first - parameter for calls of zalloc and zfree. This can be useful for custom - memory management. The compression library attaches no meaning to the - opaque value. - - zalloc must return Z_NULL if there is not enough memory for the object. - If zlib is used in a multi-threaded application, zalloc and zfree must be - thread safe. - - On 16-bit systems, the functions zalloc and zfree must be able to allocate - exactly 65536 bytes, but will not be required to allocate more than this if - the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers - returned by zalloc for objects of exactly 65536 bytes *must* have their - offset normalized to zero. The default allocation function provided by this - library ensures this (see zutil.c). To reduce memory requirements and avoid - any allocation of 64K objects, at the expense of compression ratio, compile - the library with -DMAX_WBITS=14 (see zconf.h). - - The fields total_in and total_out can be used for statistics or progress - reports. After compression, total_in holds the total size of the - uncompressed data and may be saved for use in the decompressor (particularly - if the decompressor wants to decompress everything in a single step). -*/ - - /* constants */ - -#define Z_NO_FLUSH 0 -#define Z_PARTIAL_FLUSH 1 -#define Z_SYNC_FLUSH 2 -#define Z_FULL_FLUSH 3 -#define Z_FINISH 4 -#define Z_BLOCK 5 -#define Z_TREES 6 -/* Allowed flush values; see deflate() and inflate() below for details */ - -#define Z_OK 0 -#define Z_STREAM_END 1 -#define Z_NEED_DICT 2 -#define Z_ERRNO (-1) -#define Z_STREAM_ERROR (-2) -#define Z_DATA_ERROR (-3) -#define Z_MEM_ERROR (-4) -#define Z_BUF_ERROR (-5) -#define Z_VERSION_ERROR (-6) -/* Return codes for the compression/decompression functions. Negative values - * are errors, positive values are used for special but normal events. - */ - -#define Z_NO_COMPRESSION 0 -#define Z_BEST_SPEED 1 -#define Z_BEST_COMPRESSION 9 -#define Z_DEFAULT_COMPRESSION (-1) -/* compression levels */ - -#define Z_FILTERED 1 -#define Z_HUFFMAN_ONLY 2 -#define Z_RLE 3 -#define Z_FIXED 4 -#define Z_DEFAULT_STRATEGY 0 -/* compression strategy; see deflateInit2() below for details */ - -#define Z_BINARY 0 -#define Z_TEXT 1 -#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ -#define Z_UNKNOWN 2 -/* Possible values of the data_type field (though see inflate()) */ - -#define Z_DEFLATED 8 -/* The deflate compression method (the only one supported in this version) */ - -#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ - -#define zlib_version zlibVersion() -/* for compatibility with versions < 1.0.2 */ - - - /* basic functions */ - -ZEXTERN const char * ZEXPORT zlibVersion OF((void)); -/* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is not - compatible with the zlib.h header file used by the application. This check - is automatically made by deflateInit and inflateInit. - */ - -/* -ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); - - Initializes the internal stream state for compression. The fields - zalloc, zfree and opaque must be initialized before by the caller. If - zalloc and zfree are set to Z_NULL, deflateInit updates them to use default - allocation functions. - - The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: - 1 gives best speed, 9 gives best compression, 0 gives no compression at all - (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION - requests a default compromise between speed and compression (currently - equivalent to level 6). - - deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if level is not a valid compression level, or - Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible - with the version assumed by the caller (ZLIB_VERSION). msg is set to null - if there is no error message. deflateInit does not perform any compression: - this will be done by deflate(). -*/ - - -ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); -/* - deflate compresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. deflate performs one or both of the - following actions: - - - Compress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in and avail_in are updated and - processing will resume at this point for the next call of deflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. This action is forced if the parameter flush is non zero. - Forcing flush frequently degrades the compression ratio, so this parameter - should be set only when necessary (in interactive applications). Some - output may be provided even if flush is not set. - - Before the call of deflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming more - output, and updating avail_in or avail_out accordingly; avail_out should - never be zero before the call. The application can consume the compressed - output when it wants, for example when the output buffer is full (avail_out - == 0), or after each call of deflate(). If deflate returns Z_OK and with - zero avail_out, it must be called again after making room in the output - buffer because there might be more output pending. - - Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to - decide how much data to accumulate before producing output, in order to - maximize compression. - - If the parameter flush is set to Z_SYNC_FLUSH, all pending output is - flushed to the output buffer and the output is aligned on a byte boundary, so - that the decompressor can get all input data available so far. (In - particular avail_in is zero after the call if enough output space has been - provided before the call.) Flushing may degrade compression for some - compression algorithms and so it should be used only when necessary. This - completes the current deflate block and follows it with an empty stored block - that is three bits plus filler bits to the next byte, followed by four bytes - (00 00 ff ff). - - If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the - output buffer, but the output is not aligned to a byte boundary. All of the - input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. - This completes the current deflate block and follows it with an empty fixed - codes block that is 10 bits long. This assures that enough bytes are output - in order for the decompressor to finish the block before the empty fixed code - block. - - If flush is set to Z_BLOCK, a deflate block is completed and emitted, as - for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to - seven bits of the current block are held to be written as the next byte after - the next deflate block is completed. In this case, the decompressor may not - be provided enough bits at this point in order to complete decompression of - the data provided so far to the compressor. It may need to wait for the next - block to be emitted. This is for advanced applications that need to control - the emission of deflate blocks. - - If flush is set to Z_FULL_FLUSH, all output is flushed as with - Z_SYNC_FLUSH, and the compression state is reset so that decompression can - restart from this point if previous compressed data has been damaged or if - random access is desired. Using Z_FULL_FLUSH too often can seriously degrade - compression. - - If deflate returns with avail_out == 0, this function must be called again - with the same value of the flush parameter and more output space (updated - avail_out), until the flush is complete (deflate returns with non-zero - avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that - avail_out is greater than six to avoid repeated flush markers due to - avail_out == 0 on return. - - If the parameter flush is set to Z_FINISH, pending input is processed, - pending output is flushed and deflate returns with Z_STREAM_END if there was - enough output space; if deflate returns with Z_OK, this function must be - called again with Z_FINISH and more output space (updated avail_out) but no - more input data, until it returns with Z_STREAM_END or an error. After - deflate has returned Z_STREAM_END, the only possible operations on the stream - are deflateReset or deflateEnd. - - Z_FINISH can be used immediately after deflateInit if all the compression - is to be done in a single step. In this case, avail_out must be at least the - value returned by deflateBound (see below). Then deflate is guaranteed to - return Z_STREAM_END. If not enough output space is provided, deflate will - not return Z_STREAM_END, and it must be called again as described above. - - deflate() sets strm->adler to the adler32 checksum of all input read - so far (that is, total_in bytes). - - deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered - binary. This field is only for information purposes and does not affect the - compression algorithm in any manner. - - deflate() returns Z_OK if some progress has been made (more input - processed or more output produced), Z_STREAM_END if all input has been - consumed and all output has been produced (only when flush is set to - Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not - fatal, and deflate() can be called again with more input and more output - space to continue compressing. -*/ - - -ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any pending - output. - - deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the - stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, msg - may be set but then points to a static string (which must not be - deallocated). -*/ - - -/* -ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); - - Initializes the internal stream state for decompression. The fields - next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the - exact value depends on the compression method), inflateInit determines the - compression method from the zlib header and allocates all data structures - accordingly; otherwise the allocation will be deferred to the first call of - inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to - use default allocation functions. - - inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller, or Z_STREAM_ERROR if the parameters are - invalid, such as a null pointer to the structure. msg is set to null if - there is no error message. inflateInit does not perform any decompression - apart from possibly reading the zlib header if present: actual decompression - will be done by inflate(). (So next_in and avail_in may be modified, but - next_out and avail_out are unused and unchanged.) The current implementation - of inflateInit() does not process any header information -- that is deferred - until inflate() is called. -*/ - - -ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); -/* - inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. inflate performs one or both of the - following actions: - - - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing will - resume at this point for the next call of inflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there is - no more input data or no more space in the output buffer (see below about - the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming more - output, and updating the next_* and avail_* values accordingly. The - application can consume the uncompressed output when it wants, for example - when the output buffer is full (avail_out == 0), or after each call of - inflate(). If inflate returns Z_OK and with zero avail_out, it must be - called again after making room in the output buffer because there might be - more output pending. - - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, - Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much - output as possible to the output buffer. Z_BLOCK requests that inflate() - stop if and when it gets to the next deflate block boundary. When decoding - the zlib or gzip format, this will cause inflate() to return immediately - after the header and before the first block. When doing a raw inflate, - inflate() will go ahead and process the first block, and will return when it - gets to the end of that block, or when it runs out of data. - - The Z_BLOCK option assists in appending to or combining deflate streams. - Also to assist in this, on return inflate() will set strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 if - inflate() is currently decoding the last block in the deflate stream, plus - 128 if inflate() returned immediately after decoding an end-of-block code or - decoding the complete header up to just before the first byte of the deflate - stream. The end-of-block will not be indicated until all of the uncompressed - data from that block has been written to strm->next_out. The number of - unused bits may in general be greater than seven, except when bit 7 of - data_type is set, in which case the number of unused bits will be less than - eight. data_type is set as noted here every time inflate() returns for all - flush options, and so can be used to determine the amount of currently - consumed input in bits. - - The Z_TREES option behaves as Z_BLOCK does, but it also returns when the - end of each deflate block header is reached, before any actual data in that - block is decoded. This allows the caller to determine the length of the - deflate block header for later use in random access within a deflate block. - 256 is added to the value of strm->data_type when inflate() returns - immediately after reaching the end of the deflate block header. - - inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step (a - single call of inflate), the parameter flush should be set to Z_FINISH. In - this case all pending input is processed and all pending output is flushed; - avail_out must be large enough to hold all of the uncompressed data for the - operation to complete. (The size of the uncompressed data may have been - saved by the compressor for this purpose.) The use of Z_FINISH is not - required to perform an inflation in one step. However it may be used to - inform inflate that a faster approach can be used for the single inflate() - call. Z_FINISH also informs inflate to not maintain a sliding window if the - stream completes, which reduces inflate's memory footprint. If the stream - does not complete, either because not all of the stream is provided or not - enough output space is provided, then a sliding window will be allocated and - inflate() can be called again to continue the operation as if Z_NO_FLUSH had - been used. - - In this implementation, inflate() always flushes as much output as - possible to the output buffer, and always uses the faster approach on the - first call. So the effects of the flush parameter in this implementation are - on the return value of inflate() as noted below, when inflate() returns early - when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of - memory for a sliding window when Z_FINISH is used. - - If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the Adler-32 checksum of the dictionary - chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the Adler-32 checksum of all output produced so far (that is, - total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed adler32 - checksum is equal to that saved by the compressor and returns Z_STREAM_END - only if the checksum is correct. - - inflate() can decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically, if requested when - initializing with inflateInit2(). Any information contained in the gzip - header is not retained, so applications that need that information should - instead use raw inflate, see inflateInit2() below, or inflateBack() and - perform their own processing of the gzip header and trailer. When processing - gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output - producted so far. The CRC-32 is checked against the gzip trailer. - - inflate() returns Z_OK if some progress has been made (more input processed - or more output produced), Z_STREAM_END if the end of the compressed data has - been reached and all uncompressed output has been produced, Z_NEED_DICT if a - preset dictionary is needed at this point, Z_DATA_ERROR if the input data was - corrupted (input stream not conforming to the zlib format or incorrect check - value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, - Z_BUF_ERROR if no progress is possible or if there was not enough room in the - output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and - inflate() can be called again with more input and more output space to - continue decompressing. If Z_DATA_ERROR is returned, the application may - then call inflateSync() to look for a good compression block if a partial - recovery of the data is desired. -*/ - - -ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any pending - output. - - inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a - static string (which must not be deallocated). -*/ - - - /* Advanced functions */ - -/* - The following functions are needed only in some special applications. -*/ - -/* -ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy)); - - This is another version of deflateInit with more compression options. The - fields next_in, zalloc, zfree and opaque must be initialized before by the - caller. - - The method parameter is the compression method. It must be Z_DEFLATED in - this version of the library. - - The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8..15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if - deflateInit is used instead. - - windowBits can also be -8..-15 for raw deflate. In this case, -windowBits - determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute an adler32 check value. - - windowBits can also be greater than 15 for optional gzip encoding. Add - 16 to windowBits to write a simple gzip header and trailer around the - compressed data instead of a zlib wrapper. The gzip header will have no - file name, no extra data, no comment, no modification time (set to zero), no - header crc, and the operating system will be set to 255 (unknown). If a - gzip stream is being written, strm->adler is a crc32 instead of an adler32. - - The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but is - slow and reduces compression ratio; memLevel=9 uses maximum memory for - optimal speed. The default value is 8. See zconf.h for total memory usage - as a function of windowBits and memLevel. - - The strategy parameter is used to tune the compression algorithm. Use the - value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a - filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no - string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman - coding and less string matching; it is somewhat intermediate between - Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as - fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The - strategy parameter only affects the compression ratio but not the - correctness of the compressed output even if it is not set appropriately. - Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler - decoder for special applications. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid - method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is - incompatible with the version assumed by the caller (ZLIB_VERSION). msg is - set to null if there is no error message. deflateInit2 does not perform any - compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); -/* - Initializes the compression dictionary from the given byte sequence - without producing any compressed output. When using the zlib format, this - function must be called immediately after deflateInit, deflateInit2 or - deflateReset, and before any call of deflate. When doing raw deflate, this - function must be called either before any call of deflate, or immediately - after the completion of a deflate block, i.e. after all input has been - consumed and all output has been delivered when using any of the flush - options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The - compressor and decompressor must use exactly the same dictionary (see - inflateSetDictionary). - - The dictionary should consist of strings (byte sequences) that are likely - to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a - dictionary is most useful when the data to be compressed is short and can be - predicted with good accuracy; the data can then be compressed better than - with the default empty dictionary. - - Depending on the size of the compression data structures selected by - deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size - provided in deflateInit or deflateInit2. Thus the strings most likely to be - useful should be put at the end of the dictionary, not at the front. In - addition, the current implementation of deflate will use at most the window - size minus 262 bytes of the provided dictionary. - - Upon return of this function, strm->adler is set to the adler32 value - of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The adler32 value - applies to the whole dictionary even if only a subset of the dictionary is - actually used by the compressor.) If a raw deflate was requested, then the - adler32 value is not computed and strm->adler is not set. - - deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is - inconsistent (for example if deflate has already been called for this stream - or if not at a block boundary for raw deflate). deflateSetDictionary does - not perform any compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, - z_streamp source)); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when several compression strategies will be - tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed - by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and can - consume lots of memory. - - deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being Z_NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); -/* - This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. The - stream will keep the same compression level and any other attributes that - may have been set by deflateInit2. - - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL). -*/ - -ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, - int level, - int strategy)); -/* - Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2. This can be - used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different strategy. - If the compression level is changed, the input available so far is - compressed with the old level (and may be flushed); the new level will take - effect only at the next call of deflate(). - - Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to be - compressed and flushed. In particular, strm->avail_out must be non-zero. - - deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if - strm->avail_out was zero. -*/ - -ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, - int good_length, - int max_lazy, - int nice_length, - int max_chain)); -/* - Fine tune deflate's internal compression parameters. This should only be - used by someone who understands the algorithm used by zlib's deflate for - searching for the best matching string, and even then only by the most - fanatic optimizer trying to squeeze out the last compressed bit for their - specific input data. Read the deflate.c source code for the meaning of the - max_lazy, good_length, nice_length, and max_chain parameters. - - deflateTune() can be called after deflateInit() or deflateInit2(), and - returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. - */ - -ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, - uLong sourceLen)); -/* - deflateBound() returns an upper bound on the compressed size after - deflation of sourceLen bytes. It must be called after deflateInit() or - deflateInit2(), and after deflateSetHeader(), if used. This would be used - to allocate an output buffer for deflation in a single pass, and so would be - called before deflate(). If that first deflate() call is provided the - sourceLen input bytes, an output buffer allocated to the size returned by - deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed - to return Z_STREAM_END. Note that it is possible for the compressed size to - be larger than the value returned by deflateBound() if flush options other - than Z_FINISH or Z_NO_FLUSH are used. -*/ - -ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, - unsigned *pending, - int *bits)); -/* - deflatePending() returns the number of bytes and bits of output that have - been generated, but not yet provided in the available output. The bytes not - provided would be due to the available output space having being consumed. - The number of bits of output not provided are between 0 and 7, where they - await more bits to join them in order to fill out a full byte. If pending - or bits are Z_NULL, then those values are not set. - - deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. - */ - -ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, - int bits, - int value)); -/* - deflatePrime() inserts bits in the deflate output stream. The intent - is that this function is used to start off the deflate output with the bits - leftover from a previous deflate stream when appending to it. As such, this - function can only be used for raw deflate, and must be used before the first - deflate() call after a deflateInit2() or deflateReset(). bits must be less - than or equal to 16, and that many of the least significant bits of value - will be inserted in the output. - - deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough - room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the - source stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, - gz_headerp head)); -/* - deflateSetHeader() provides gzip header information for when a gzip - stream is requested by deflateInit2(). deflateSetHeader() may be called - after deflateInit2() or deflateReset() and before the first call of - deflate(). The text, time, os, extra field, name, and comment information - in the provided gz_header structure are written to the gzip header (xflag is - ignored -- the extra flags are set according to the compression level). The - caller must assure that, if not Z_NULL, name and comment are terminated with - a zero byte, and that if extra is not Z_NULL, that extra_len bytes are - available there. If hcrc is true, a gzip header crc is included. Note that - the current versions of the command-line version of gzip (up through version - 1.3.x) do not support header crc's, and will report that it is a "multi-part - gzip file" and give up. - - If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to 255, with no extra, name, or comment - fields. The gzip header is returned to the default state by deflateReset(). - - deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, - int windowBits)); - - This is another version of inflateInit with an extra parameter. The - fields next_in, avail_in, zalloc, zfree and opaque must be initialized - before by the caller. - - The windowBits parameter is the base two logarithm of the maximum window - size (the size of the history buffer). It should be in the range 8..15 for - this version of the library. The default value is 15 if inflateInit is used - instead. windowBits must be greater than or equal to the windowBits value - provided to deflateInit2() while compressing, or it must be equal to 15 if - deflateInit2() was not used. If a compressed stream with a larger window - size is given as input, inflate() will return with the error code - Z_DATA_ERROR instead of trying to allocate a larger window. - - windowBits can also be zero to request that inflate use the window size in - the zlib header of the compressed stream. - - windowBits can also be -8..-15 for raw inflate. In this case, -windowBits - determines the window size. inflate() will then process raw deflate data, - not looking for a zlib or gzip header, not generating a check value, and not - looking for any check values for comparison at the end of the stream. This - is for use with other formats that use the deflate compressed data format - such as zip. Those formats provide their own check values. If a custom - format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an adler32 or a crc32 be applied to - the uncompressed data as is done in the zlib, gzip, and zip formats. For - most applications, the zlib format should be used as is. Note that comments - above on the use in deflateInit2() applies to the magnitude of windowBits. - - windowBits can also be greater than 15 for optional gzip decoding. Add - 32 to windowBits to enable zlib and gzip decoding with automatic header - detection, or add 16 to decode only the gzip format (the zlib format will - return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a - crc32 instead of an adler32. - - inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller, or Z_STREAM_ERROR if the parameters are - invalid, such as a null pointer to the structure. msg is set to null if - there is no error message. inflateInit2 does not perform any decompression - apart from possibly reading the zlib header if present: actual decompression - will be done by inflate(). (So next_in and avail_in may be modified, but - next_out and avail_out are unused and unchanged.) The current implementation - of inflateInit2() does not process any header information -- that is - deferred until inflate() is called. -*/ - -ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); -/* - Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate, - if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the adler32 value returned by that call of inflate. - The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called at any - time to set the dictionary. If the provided dictionary is smaller than the - window and there is already data in the window, then the provided dictionary - will amend what's there. The application must insure that the dictionary - that was used for compression is provided. - - inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is - inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect adler32 value). inflateSetDictionary does not - perform any decompression: this will be done by subsequent calls of - inflate(). -*/ - -ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, - Bytef *dictionary, - uInt *dictLength)); -/* - Returns the sliding dictionary being maintained by inflate. dictLength is - set to the number of bytes in the dictionary, and that many bytes are copied - to dictionary. dictionary must have enough space, where 32768 bytes is - always enough. If inflateGetDictionary() is called with dictionary equal to - Z_NULL, then only the dictionary length is returned, and nothing is copied. - Similary, if dictLength is Z_NULL, then it is not set. - - inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the - stream state is inconsistent. -*/ - -ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); -/* - Skips invalid compressed data until a possible full flush point (see above - for the description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. - - inflateSync searches for a 00 00 FF FF pattern in the compressed data. - All full flush points have this pattern, but not all occurrences of this - pattern are full flush points. - - inflateSync returns Z_OK if a possible full flush point has been found, - Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point - has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. - In the success case, the application may save the current current value of - total_in which indicates where valid compressed data was found. In the - error case, the application may repeatedly call inflateSync, providing more - input each time, until success or end of the input data. -*/ - -ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, - z_streamp source)); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when randomly accessing a large stream. The - first pass through the stream can periodically record the inflate state, - allowing restarting inflate at those points when randomly accessing the - stream. - - inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being Z_NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); -/* - This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. The - stream will keep attributes that may have been set by inflateInit2. - - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL). -*/ - -ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, - int windowBits)); -/* - This function is the same as inflateReset, but it also permits changing - the wrap and window size requests. The windowBits parameter is interpreted - the same as it is for inflateInit2. - - inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being Z_NULL), or if - the windowBits parameter is invalid. -*/ - -ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, - int bits, - int value)); -/* - This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. - - If bits is negative, then the input stream bit buffer is emptied. Then - inflatePrime() can be called again to put bits in the buffer. This is used - to clear out bits leftover after feeding inflate a block description prior - to feeding inflate codes. - - inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); -/* - This function returns two values, one in the lower 16 bits of the return - value, and the other in the remaining upper bits, obtained by shifting the - return value down 16 bits. If the upper value is -1 and the lower value is - zero, then inflate() is currently decoding information outside of a block. - If the upper value is -1 and the lower value is non-zero, then inflate is in - the middle of a stored block, with the lower value equaling the number of - bytes from the input remaining to copy. If the upper value is not -1, then - it is the number of bits back from the current bit position in the input of - the code (literal or length/distance pair) currently being processed. In - that case the lower value is the number of bytes already emitted for that - code. - - A code is being processed if inflate is waiting for more input to complete - decoding of the code, or if it has completed decoding but is waiting for - more output space to write the literal or match data. - - inflateMark() is used to mark locations in the input data for random - access, which may be at bit positions, and to note those cases where the - output of a code may span boundaries of random access blocks. The current - location in the input stream can be determined from avail_in and data_type - as noted in the description for the Z_BLOCK flush parameter for inflate. - - inflateMark returns the value noted above or -1 << 16 if the provided - source stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, - gz_headerp head)); -/* - inflateGetHeader() requests that gzip header information be stored in the - provided gz_header structure. inflateGetHeader() may be called after - inflateInit2() or inflateReset(), and before the first call of inflate(). - As inflate() processes the gzip stream, head->done is zero until the header - is completed, at which time head->done is set to one. If a zlib stream is - being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be - used to force inflate() to return immediately after header processing is - complete and before any actual data is decompressed. - - The text, time, xflags, and os fields are filled in with the gzip header - contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max - contains the maximum number of bytes to write to extra. Once done is true, - extra_len contains the actual extra field length, and extra contains the - extra field, or that field truncated if extra_max is less than extra_len. - If name is not Z_NULL, then up to name_max characters are written there, - terminated with a zero unless the length is greater than name_max. If - comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When any - of extra, name, or comment are not Z_NULL and the respective field is not - present in the header, then that field is set to Z_NULL to signal its - absence. This allows the use of deflateSetHeader() with the returned - structure to duplicate the header. However if those fields are set to - allocated memory, then the application will need to save those pointers - elsewhere so that they can be eventually freed. - - If inflateGetHeader is not used, then the header information is simply - discarded. The header is always checked for validity, including the header - CRC if present. inflateReset() will reset the process to discard the header - information. The application would need to call inflateGetHeader() again to - retrieve the header from the next gzip stream. - - inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, - unsigned char FAR *window)); - - Initialize the internal stream state for decompression using inflateBack() - calls. The fields zalloc, zfree and opaque in strm must be initialized - before the call. If zalloc and zfree are Z_NULL, then the default library- - derived memory allocation routines are used. windowBits is the base two - logarithm of the window size, in the range 8..15. window is a caller - supplied buffer of that size. Except for special applications where it is - assured that deflate was used with small window sizes, windowBits must be 15 - and a 32K byte window must be supplied to be able to decompress general - deflate streams. - - See inflateBack() for the usage of these routines. - - inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the parameters are invalid, Z_MEM_ERROR if the internal state could not be - allocated, or Z_VERSION_ERROR if the version of the library does not match - the version of the header file. -*/ - -typedef unsigned (*in_func) OF((void FAR *, - z_const unsigned char FAR * FAR *)); -typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); - -ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, - in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc)); -/* - inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is potentially more efficient than - inflate() for file i/o applications, in that it avoids copying between the - output and the sliding window by simply making the window itself the output - buffer. inflate() can be faster on modern CPUs when used with large - buffers. inflateBack() trusts the application to not change the output - buffer passed by the output function, at least until inflateBack() returns. - - inflateBackInit() must be called first to allocate the internal state - and to initialize the state with the user-provided window buffer. - inflateBack() may then be used multiple times to inflate a complete, raw - deflate stream with each call. inflateBackEnd() is then called to free the - allocated state. - - A raw deflate stream is one with no zlib or gzip header or trailer. - This routine would normally be used in a utility that reads zip or gzip - files and writes out uncompressed files. The utility would decode the - header and process the trailer on its own, hence this routine expects only - the raw deflate stream to decompress. This is different from the normal - behavior of inflate(), which expects either a zlib or gzip header and - trailer around the deflate stream. - - inflateBack() uses two subroutines supplied by the caller that are then - called by inflateBack() for input and output. inflateBack() calls those - routines until it reads a complete deflate stream and writes out all of the - uncompressed data, or until it encounters an error. The function's - parameters and return types are defined above in the in_func and out_func - typedefs. inflateBack() will call in(in_desc, &buf) which should return the - number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero--buf is ignored in that - case--and inflateBack() will return a buffer error. inflateBack() will call - out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() - should return zero on success, or non-zero on failure. If out() returns - non-zero, inflateBack() will return with an error. Neither in() nor out() - are permitted to change the contents of the window provided to - inflateBackInit(), which is also the buffer that out() uses to write from. - The length written by out() will be at most the window size. Any non-zero - amount of input may be provided by in(). - - For convenience, inflateBack() can be provided input on the first call by - setting strm->next_in and strm->avail_in. If that input is exhausted, then - in() will be called. Therefore strm->next_in must be initialized before - calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called - immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in - must also be initialized, and then if strm->avail_in is not zero, input will - initially be taken from strm->next_in[0 .. strm->avail_in - 1]. - - The in_desc and out_desc parameters of inflateBack() is passed as the - first parameter of in() and out() respectively when they are called. These - descriptors can be optionally used to pass any information that the caller- - supplied in() and out() functions need to do their job. - - On return, inflateBack() will set strm->next_in and strm->avail_in to - pass back any unused input that was provided by the last in() call. The - return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR - if in() or out() returned an error, Z_DATA_ERROR if there was a format error - in the deflate stream (in which case strm->msg is set to indicate the nature - of the error), or Z_STREAM_ERROR if the stream was not properly initialized. - In the case of Z_BUF_ERROR, an input or output error can be distinguished - using strm->next_in which will be Z_NULL only if in() returned an error. If - strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning - non-zero. (in() will always be called before out(), so strm->next_in is - assured to be defined if out() returns non-zero.) Note that inflateBack() - cannot return Z_OK. -*/ - -ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); -/* - All memory allocated by inflateBackInit() is freed. - - inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream - state was inconsistent. -*/ - -ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); -/* Return flags indicating compile-time options. - - Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: - 1.0: size of uInt - 3.2: size of uLong - 5.4: size of voidpf (pointer) - 7.6: size of z_off_t - - Compiler, assembler, and debug options: - 8: DEBUG - 9: ASMV or ASMINF -- use ASM code - 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention - 11: 0 (reserved) - - One-time table building (smaller code, but not thread-safe if true): - 12: BUILDFIXED -- build static block decoding tables when needed - 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed - 14,15: 0 (reserved) - - Library content (indicates missing functionality): - 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking - deflate code when not needed) - 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect - and decode gzip streams (to avoid linking crc code) - 18-19: 0 (reserved) - - Operation variations (changes in library functionality): - 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate - 21: FASTEST -- deflate algorithm with only one, lowest compression level - 22,23: 0 (reserved) - - The sprintf variant used by gzprintf (zero is best): - 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format - 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! - 26: 0 = returns value, 1 = void -- 1 means inferred string length returned - - Remainder: - 27-31: 0 (reserved) - */ - -#ifndef Z_SOLO - - /* utility functions */ - -/* - The following utility functions are implemented on top of the basic - stream-oriented functions. To simplify the interface, some default options - are assumed (compression level and memory usage, standard memory allocation - functions). The source code of these utility functions can be modified if - you need special options. -*/ - -ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); -/* - Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total size - of the destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - - compress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer. -*/ - -ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen, - int level)); -/* - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ - -ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); -/* - compressBound() returns an upper bound on the compressed size after - compress() or compress2() on sourceLen bytes. It would be used before a - compress() or compress2() call to allocate the destination buffer. -*/ - -ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); -/* - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total size - of the destination buffer, which must be large enough to hold the entire - uncompressed data. (The size of the uncompressed data must have been saved - previously by the compressor and transmitted to the decompressor by some - mechanism outside the scope of this compression library.) Upon exit, destLen - is the actual size of the uncompressed buffer. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In - the case where there is not enough room, uncompress() will fill the output - buffer with the uncompressed data up to that point. -*/ - - /* gzip file access functions */ - -/* - This library supports reading and writing files in gzip (.gz) format with - an interface similar to that of stdio, using the functions that start with - "gz". The gzip format is different from the zlib format. gzip is a gzip - wrapper, documented in RFC 1952, wrapped around a deflate stream. -*/ - -typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ - -/* -ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); - - Opens a gzip (.gz) file for reading or writing. The mode parameter is as - in fopen ("rb" or "wb") but can also include a compression level ("wb9") or - a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only - compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' - for fixed code compression as in "wb9F". (See the description of - deflateInit2 for more information about the strategy parameter.) 'T' will - request transparent writing or appending with no compression and not using - the gzip format. - - "a" can be used instead of "w" to request that the gzip stream that will - be written be appended to the file. "+" will result in an error, since - reading and writing to the same gzip file is not supported. The addition of - "x" when writing will create the file exclusively, which fails if the file - already exists. On systems that support it, the addition of "e" when - reading or writing will set the flag to close the file on an execve() call. - - These functions, as well as gzip, will read and decode a sequence of gzip - streams in a file. The append function of gzopen() can be used to create - such a file. (Also see gzflush() for another way to do this.) When - appending, gzopen does not test whether the file begins with a gzip stream, - nor does it look for the end of the gzip streams to begin appending. gzopen - will simply append a gzip stream to the existing file. - - gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. When - reading, this will be detected automatically by looking for the magic two- - byte gzip header. - - gzopen returns NULL if the file could not be opened, if there was - insufficient memory to allocate the gzFile state, or if an invalid mode was - specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). - errno can be checked to determine if the reason gzopen failed was that the - file could not be opened. -*/ - -ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); -/* - gzdopen associates a gzFile with the file descriptor fd. File descriptors - are obtained from calls like open, dup, creat, pipe or fileno (if the file - has been previously opened with fopen). The mode parameter is as in gzopen. - - The next call of gzclose on the returned gzFile will also close the file - descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor - fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, - mode);. The duplicated descriptor should be saved to avoid a leak, since - gzdopen does not close fd if it fails. If you are using fileno() to get the - file descriptor from a FILE *, then you will have to use dup() to avoid - double-close()ing the file descriptor. Both gzclose() and fclose() will - close the associated file descriptor, so they need to have different file - descriptors. - - gzdopen returns NULL if there was insufficient memory to allocate the - gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not - provided, or '+' was provided), or if fd is -1. The file descriptor is not - used until the next gz* read, write, seek, or close operation, so gzdopen - will not detect if fd is invalid (unless fd is -1). -*/ - -ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); -/* - Set the internal buffer size used by this library's functions. The - default buffer size is 8192 bytes. This function must be called after - gzopen() or gzdopen(), and before any other calls that read or write the - file. The buffer memory allocation is always deferred to the first read or - write. Two buffers are allocated, either both of the specified size when - writing, or one of the specified size and the other twice that size when - reading. A larger buffer size of, for example, 64K or 128K bytes will - noticeably increase the speed of decompression (reading). - - The new buffer size also affects the maximum length for gzprintf(). - - gzbuffer() returns 0 on success, or -1 on failure, such as being called - too late. -*/ - -ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); -/* - Dynamically update the compression level or strategy. See the description - of deflateInit2 for the meaning of these parameters. - - gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not - opened for writing. -*/ - -ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); -/* - Reads the given number of uncompressed bytes from the compressed file. If - the input file is not in gzip format, gzread copies the given number of - bytes into the buffer directly from the file. - - After reaching the end of a gzip stream in the input, gzread will continue - to read, looking for another gzip stream. Any number of gzip streams may be - concatenated in the input file, and will all be decompressed by gzread(). - If something other than a gzip stream is encountered after a gzip stream, - that remaining trailing garbage is ignored (and no error is returned). - - gzread can be used to read a gzip file that is being concurrently written. - Upon reaching the end of the input, gzread will return with the available - data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then - gzclearerr can be used to clear the end of file indicator in order to permit - gzread to be tried again. Z_OK indicates that a gzip stream was completed - on the last gzread. Z_BUF_ERROR indicates that the input file ended in the - middle of a gzip stream. Note that gzread does not return -1 in the event - of an incomplete gzip stream. This error is deferred until gzclose(), which - will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip - stream. Alternatively, gzerror can be used before gzclose to detect this - case. - - gzread returns the number of uncompressed bytes actually read, less than - len for end of file, or -1 for error. -*/ - -ZEXTERN int ZEXPORT gzwrite OF((gzFile file, - voidpc buf, unsigned len)); -/* - Writes the given number of uncompressed bytes into the compressed file. - gzwrite returns the number of uncompressed bytes written or 0 in case of - error. -*/ - -ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); -/* - Converts, formats, and writes the arguments to the compressed file under - control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written, or 0 in case of error. The number of - uncompressed bytes written is limited to 8191, or one less than the buffer - size given to gzbuffer(). The caller should assure that this limit is not - exceeded. If it is exceeded, then gzprintf() will return an error (0) with - nothing written. In this case, there may also be a buffer overflow with - unpredictable consequences, which is possible only if zlib was compiled with - the insecure functions sprintf() or vsprintf() because the secure snprintf() - or vsnprintf() functions were not available. This can be determined using - zlibCompileFlags(). -*/ - -ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); -/* - Writes the given null-terminated string to the compressed file, excluding - the terminating null character. - - gzputs returns the number of characters written, or -1 in case of error. -*/ - -ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); -/* - Reads bytes from the compressed file until len-1 characters are read, or a - newline character is read and transferred to buf, or an end-of-file - condition is encountered. If any characters are read or if len == 1, the - string is terminated with a null character. If no characters are read due - to an end-of-file or len < 1, then the buffer is left untouched. - - gzgets returns buf which is a null-terminated string, or it returns NULL - for end-of-file or in case of error. If there was an error, the contents at - buf are indeterminate. -*/ - -ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); -/* - Writes c, converted to an unsigned char, into the compressed file. gzputc - returns the value that was written, or -1 in case of error. -*/ - -ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); -/* - Reads one byte from the compressed file. gzgetc returns this byte or -1 - in case of end of file or error. This is implemented as a macro for speed. - As such, it does not do all of the checking the other functions do. I.e. - it does not check to see if file is NULL, nor whether the structure file - points to has been clobbered or not. -*/ - -ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); -/* - Push one character back onto the stream to be read as the first character - on the next read. At least one character of push-back is allowed. - gzungetc() returns the character pushed, or -1 on failure. gzungetc() will - fail if c is -1, and may fail if a character has been pushed but not read - yet. If gzungetc is used immediately after gzopen or gzdopen, at least the - output buffer size of pushed characters is allowed. (See gzbuffer above.) - The pushed character will be discarded if the stream is repositioned with - gzseek() or gzrewind(). -*/ - -ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); -/* - Flushes all pending output into the compressed file. The parameter flush - is as in the deflate() function. The return value is the zlib error number - (see function gzerror below). gzflush is only permitted when writing. - - If the flush parameter is Z_FINISH, the remaining data is written and the - gzip stream is completed in the output. If gzwrite() is called again, a new - gzip stream will be started in the output. gzread() is able to read such - concatented gzip streams. - - gzflush should be called only when strictly necessary because it will - degrade compression if called too often. -*/ - -/* -ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, - z_off_t offset, int whence)); - - Sets the starting position for the next gzread or gzwrite on the given - compressed file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); - the value SEEK_END is not supported. - - If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are - supported; gzseek then compresses a sequence of zeroes up to the new - starting position. - - gzseek returns the resulting offset location as measured in bytes from - the beginning of the uncompressed stream, or -1 in case of error, in - particular if the file is opened for writing and the new starting position - would be before the current position. -*/ - -ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); -/* - Rewinds the given file. This function is supported only for reading. - - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) -*/ - -/* -ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); - - Returns the starting position for the next gzread or gzwrite on the given - compressed file. This position represents a number of bytes in the - uncompressed data stream, and is zero when starting, even if appending or - reading a gzip stream from the middle of a file using gzdopen(). - - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) -*/ - -/* -ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); - - Returns the current offset in the file being read or written. This offset - includes the count of bytes that precede the gzip stream, for example when - appending or when using gzdopen() for reading. When reading, the offset - does not include as yet unused buffered input. This information can be used - for a progress indicator. On error, gzoffset() returns -1. -*/ - -ZEXTERN int ZEXPORT gzeof OF((gzFile file)); -/* - Returns true (1) if the end-of-file indicator has been set while reading, - false (0) otherwise. Note that the end-of-file indicator is set only if the - read tried to go past the end of the input, but came up short. Therefore, - just like feof(), gzeof() may return false even if there is no more data to - read, in the event that the last read request was for the exact number of - bytes remaining in the input file. This will happen if the input file size - is an exact multiple of the buffer size. - - If gzeof() returns true, then the read functions will return no more data, - unless the end-of-file indicator is reset by gzclearerr() and the input file - has grown since the previous end of file was detected. -*/ - -ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); -/* - Returns true (1) if file is being copied directly while reading, or false - (0) if file is a gzip stream being decompressed. - - If the input file is empty, gzdirect() will return true, since the input - does not contain a gzip stream. - - If gzdirect() is used immediately after gzopen() or gzdopen() it will - cause buffers to be allocated to allow reading the file to determine if it - is a gzip file. Therefore if gzbuffer() is used, it should be called before - gzdirect(). - - When writing, gzdirect() returns true (1) if transparent writing was - requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: - gzdirect() is not needed when writing. Transparent writing must be - explicitly requested, so the application already knows the answer. When - linking statically, using gzdirect() will include all of the zlib code for - gzip file reading and decompression, which may not be desired.) -*/ - -ZEXTERN int ZEXPORT gzclose OF((gzFile file)); -/* - Flushes all pending output if necessary, closes the compressed file and - deallocates the (de)compression state. Note that once file is closed, you - cannot call gzerror with file, since its structures have been deallocated. - gzclose must not be called more than once on the same file, just as free - must not be called more than once on the same allocation. - - gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a - file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the - last read ended in the middle of a gzip stream, or Z_OK on success. -*/ - -ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); -ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); -/* - Same as gzclose(), but gzclose_r() is only for use when reading, and - gzclose_w() is only for use when writing or appending. The advantage to - using these instead of gzclose() is that they avoid linking in zlib - compression or decompression code that is not used when only reading or only - writing respectively. If gzclose() is used, then both compression and - decompression code will be included the application when linking to a static - zlib library. -*/ - -ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); -/* - Returns the error message for the last error which occurred on the given - compressed file. errnum is set to zlib error number. If an error occurred - in the file system and not in the compression library, errnum is set to - Z_ERRNO and the application may consult errno to get the exact error code. - - The application must not modify the returned string. Future calls to - this function may invalidate the previously returned string. If file is - closed, then the string previously returned by gzerror will no longer be - available. - - gzerror() should be used to distinguish errors from end-of-file for those - functions above that do not distinguish those cases in their return values. -*/ - -ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); -/* - Clears the error and end-of-file flags for file. This is analogous to the - clearerr() function in stdio. This is useful for continuing to read a gzip - file that is being written concurrently. -*/ - -#endif /* !Z_SOLO */ - - /* checksum functions */ - -/* - These functions are not related to compression but are exported - anyway because they might be useful in applications using the compression - library. -*/ - -ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); -/* - Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. If buf is Z_NULL, this function returns the - required initial value for the checksum. - - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed - much faster. - - Usage example: - - uLong adler = adler32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - adler = adler32(adler, buffer, length); - } - if (adler != original_adler) error(); -*/ - -/* -ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, - z_off_t len2)); - - Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 - and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for - each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note - that the z_off_t type (like off_t) is a signed integer. If len2 is - negative, the result has no meaning or utility. -*/ - -ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); -/* - Update a running CRC-32 with the bytes buf[0..len-1] and return the - updated CRC-32. If buf is Z_NULL, this function returns the required - initial value for the crc. Pre- and post-conditioning (one's complement) is - performed within this function so it shouldn't be done by the application. - - Usage example: - - uLong crc = crc32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - crc = crc32(crc, buffer, length); - } - if (crc != original_crc) error(); -*/ - -/* -ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); - - Combine two CRC-32 check values into one. For two sequences of bytes, - seq1 and seq2 with lengths len1 and len2, CRC-32 check values were - calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 - check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. -*/ - - - /* various hacks, don't look :) */ - -/* deflateInit and inflateInit are macros to allow checking the zlib version - * and the compiler's view of z_stream: - */ -ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size)); -ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size)); -#define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) -#define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) -#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) -#define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ - (int)sizeof(z_stream)) -#define inflateBackInit(strm, windowBits, window) \ - inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, (int)sizeof(z_stream)) - -#ifndef Z_SOLO - -/* gzgetc() macro and its supporting function and exposed data structure. Note - * that the real internal state is much larger than the exposed structure. - * This abbreviated structure exposes just enough for the gzgetc() macro. The - * user should not mess with these exposed elements, since their names or - * behavior could change in the future, perhaps even capriciously. They can - * only be used by the gzgetc() macro. You have been warned. - */ -struct gzFile_s { - unsigned have; - unsigned char *next; - z_off64_t pos; -}; -ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ -#ifdef Z_PREFIX_SET -# undef z_gzgetc -# define z_gzgetc(g) \ - ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) -#else -# define gzgetc(g) \ - ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) -#endif - -/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or - * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if - * both are true, the application gets the *64 functions, and the regular - * functions are changed to 64 bits) -- in case these are set on systems - * without large file support, _LFS64_LARGEFILE must also be true - */ -#ifdef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); - ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); -#endif - -#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) -# ifdef Z_PREFIX_SET -# define z_gzopen z_gzopen64 -# define z_gzseek z_gzseek64 -# define z_gztell z_gztell64 -# define z_gzoffset z_gzoffset64 -# define z_adler32_combine z_adler32_combine64 -# define z_crc32_combine z_crc32_combine64 -# else -# define gzopen gzopen64 -# define gzseek gzseek64 -# define gztell gztell64 -# define gzoffset gzoffset64 -# define adler32_combine adler32_combine64 -# define crc32_combine crc32_combine64 -# endif -# ifndef Z_LARGE64 - ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); - ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); - ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); - ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); -# endif -#else - ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); - ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); - ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); - ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); - ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); -#endif - -#else /* Z_SOLO */ - - ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); - -#endif /* !Z_SOLO */ - -/* hack for buggy compilers */ -#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) - struct internal_state {int dummy;}; -#endif - -/* undocumented functions */ -ZEXTERN const char * ZEXPORT zError OF((int)); -ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); -ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); -ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); -ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); -ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); -#if defined(_WIN32) && !defined(Z_SOLO) -ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, - const char *mode)); -#endif -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -# ifndef Z_SOLO -ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, - const char *format, - va_list va)); -# endif -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* ZLIB_H */ diff --git a/builtins/zlib/zutil.c b/builtins/zlib/zutil.c deleted file mode 100644 index aaeb6da0fc8a9..0000000000000 --- a/builtins/zlib/zutil.c +++ /dev/null @@ -1,309 +0,0 @@ -/* zutil.c -- target dependent utility functions for the compression library - * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#include "zutil.h" -#ifndef Z_SOLO -# include "gzguts.h" -#endif - -#ifndef NO_DUMMY_DECL -struct internal_state {int dummy;}; /* for buggy compilers */ -#endif - -z_const char * const z_errmsg[10] = { -"need dictionary", /* Z_NEED_DICT 2 */ -"stream end", /* Z_STREAM_END 1 */ -"", /* Z_OK 0 */ -"file error", /* Z_ERRNO (-1) */ -"stream error", /* Z_STREAM_ERROR (-2) */ -"data error", /* Z_DATA_ERROR (-3) */ -"insufficient memory", /* Z_MEM_ERROR (-4) */ -"buffer error", /* Z_BUF_ERROR (-5) */ -"incompatible version",/* Z_VERSION_ERROR (-6) */ -""}; - - -const char * ZEXPORT zlibVersion() -{ - return ZLIB_VERSION; -} - -uLong ZEXPORT zlibCompileFlags() -{ - uLong flags; - - flags = 0; - switch ((int)(sizeof(uInt))) { - case 2: break; - case 4: flags += 1; break; - case 8: flags += 2; break; - default: flags += 3; - } - switch ((int)(sizeof(uLong))) { - case 2: break; - case 4: flags += 1 << 2; break; - case 8: flags += 2 << 2; break; - default: flags += 3 << 2; - } - switch ((int)(sizeof(voidpf))) { - case 2: break; - case 4: flags += 1 << 4; break; - case 8: flags += 2 << 4; break; - default: flags += 3 << 4; - } - switch ((int)(sizeof(z_off_t))) { - case 2: break; - case 4: flags += 1 << 6; break; - case 8: flags += 2 << 6; break; - default: flags += 3 << 6; - } -#ifdef DEBUG - flags += 1 << 8; -#endif -#if defined(ASMV) || defined(ASMINF) - flags += 1 << 9; -#endif -#ifdef ZLIB_WINAPI - flags += 1 << 10; -#endif -#ifdef BUILDFIXED - flags += 1 << 12; -#endif -#ifdef DYNAMIC_CRC_TABLE - flags += 1 << 13; -#endif -#ifdef NO_GZCOMPRESS - flags += 1L << 16; -#endif -#ifdef NO_GZIP - flags += 1L << 17; -#endif -#ifdef PKZIP_BUG_WORKAROUND - flags += 1L << 20; -#endif -#ifdef FASTEST - flags += 1L << 21; -#endif -#if defined(STDC) || defined(Z_HAVE_STDARG_H) -# ifdef NO_vsnprintf - flags += 1L << 25; -# ifdef HAS_vsprintf_void - flags += 1L << 26; -# endif -# else -# ifdef HAS_vsnprintf_void - flags += 1L << 26; -# endif -# endif -#else - flags += 1L << 24; -# ifdef NO_snprintf - flags += 1L << 25; -# ifdef HAS_sprintf_void - flags += 1L << 26; -# endif -# else -# ifdef HAS_snprintf_void - flags += 1L << 26; -# endif -# endif -#endif - return flags; -} - -#ifdef DEBUG - -# ifndef verbose -# define verbose 0 -# endif -int ZLIB_INTERNAL z_verbose = verbose; - -void ZLIB_INTERNAL z_error(char *m) -{ - fprintf(stderr, "%s\n", m); - exit(1); -} -#endif - -/* exported to allow conversion of error code to string for compress() and - * uncompress() - */ -const char * ZEXPORT zError(int err) -{ - return ERR_MSG(err); -} - -#if defined(_WIN32_WCE) - /* The Microsoft C Run-Time Library for Windows CE doesn't have - * errno. We define it as a global variable to simplify porting. - * Its value is always 0 and should not be used. - */ - int errno = 0; -#endif - -#ifndef HAVE_MEMCPY - -void ZLIB_INTERNAL zmemcpy(Bytef* dest, const Bytef* source, uInt len) -{ - if (len == 0) return; - do { - *dest++ = *source++; /* ??? to be unrolled */ - } while (--len != 0); -} - -int ZLIB_INTERNAL zmemcmp(const Bytef* s1, const Bytef* s2, uInt len) -{ - uInt j; - - for (j = 0; j < len; j++) { - if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; - } - return 0; -} - -void ZLIB_INTERNAL zmemzero(Bytef* dest, uInt len) -{ - if (len == 0) return; - do { - *dest++ = 0; /* ??? to be unrolled */ - } while (--len != 0); -} -#endif - -#ifndef Z_SOLO - -#ifdef SYS16BIT - -#ifdef __TURBOC__ -/* Turbo C in 16-bit mode */ - -# define MY_ZCALLOC - -/* Turbo C malloc() does not allow dynamic allocation of 64K bytes - * and farmalloc(64K) returns a pointer with an offset of 8, so we - * must fix the pointer. Warning: the pointer must be put back to its - * original form in order to free it, use zcfree(). - */ - -#define MAX_PTR 10 -/* 10*64K = 640K */ - -local int next_ptr = 0; - -typedef struct ptr_table_s { - voidpf org_ptr; - voidpf new_ptr; -} ptr_table; - -local ptr_table table[MAX_PTR]; -/* This table is used to remember the original form of pointers - * to large buffers (64K). Such pointers are normalized with a zero offset. - * Since MSDOS is not a preemptive multitasking OS, this table is not - * protected from concurrent access. This hack doesn't work anyway on - * a protected system like OS/2. Use Microsoft C instead. - */ - -voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) -{ - voidpf buf = opaque; /* just to make some compilers happy */ - ulg bsize = (ulg)items*size; - - /* If we allocate less than 65520 bytes, we assume that farmalloc - * will return a usable pointer which doesn't have to be normalized. - */ - if (bsize < 65520L) { - buf = farmalloc(bsize); - if (*(ush*)&buf != 0) return buf; - } else { - buf = farmalloc(bsize + 16L); - } - if (buf == NULL || next_ptr >= MAX_PTR) return NULL; - table[next_ptr].org_ptr = buf; - - /* Normalize the pointer to seg:0 */ - *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; - *(ush*)&buf = 0; - table[next_ptr++].new_ptr = buf; - return buf; -} - -void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) -{ - int n; - if (*(ush*)&ptr != 0) { /* object < 64K */ - farfree(ptr); - return; - } - /* Find the original pointer */ - for (n = 0; n < next_ptr; n++) { - if (ptr != table[n].new_ptr) continue; - - farfree(table[n].org_ptr); - while (++n < next_ptr) { - table[n-1] = table[n]; - } - next_ptr--; - return; - } - ptr = opaque; /* just to make some compilers happy */ - Assert(0, "zcfree: ptr not found"); -} - -#endif /* __TURBOC__ */ - - -#ifdef M_I86 -/* Microsoft C in 16-bit mode */ - -# define MY_ZCALLOC - -#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) -# define _halloc halloc -# define _hfree hfree -#endif - -voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) -{ - if (opaque) opaque = 0; /* to make compiler happy */ - return _halloc((long)items, size); -} - -void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) -{ - if (opaque) opaque = 0; /* to make compiler happy */ - _hfree(ptr); -} - -#endif /* M_I86 */ - -#endif /* SYS16BIT */ - - -#ifndef MY_ZCALLOC /* Any system without a special alloc function */ - -#ifndef STDC -extern voidp malloc OF((uInt size)); -extern voidp calloc OF((uInt items, uInt size)); -extern void free OF((voidpf ptr)); -#endif - -voidpf ZLIB_INTERNAL zcalloc(voidpf opaque, unsigned items, unsigned size) -{ - if (opaque) items += size - size; /* make compiler happy */ - return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : - (voidpf)calloc(items, size); -} - -void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) -{ - free(ptr); - if (opaque) return; /* make compiler happy */ -} - -#endif /* MY_ZCALLOC */ - -#endif /* !Z_SOLO */ diff --git a/builtins/zlib/zutil.h b/builtins/zlib/zutil.h deleted file mode 100644 index 38906a2b018b0..0000000000000 --- a/builtins/zlib/zutil.h +++ /dev/null @@ -1,229 +0,0 @@ -/* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2013 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -/* @(#) $Id$ */ - -#ifndef ZUTIL_H -#define ZUTIL_H - -#ifdef HAVE_HIDDEN -# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) -#else -# define ZLIB_INTERNAL -#endif - -#include "zlib.h" -#include - -#if defined(STDC) && !defined(Z_SOLO) -# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) -# include -# endif -# include -# include -#endif - -#ifdef Z_SOLO - typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ -#endif - -#ifndef local -# define local static -#endif -/* compile with -Dlocal if your debugger can't find static symbols */ - -typedef unsigned char uch; -typedef uch FAR uchf; -typedef unsigned short ush; -typedef ush FAR ushf; -typedef unsigned long ulg; - -extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ -/* (size given to avoid silly warnings with Visual C++) */ - -#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] - -#define ERR_RETURN(strm,err) \ - return (strm->msg = ERR_MSG(err), (err)) -/* To be used only when the state is known to be valid */ - - /* common constants */ - -#ifndef DEF_WBITS -# define DEF_WBITS MAX_WBITS -#endif -/* default windowBits for decompression. MAX_WBITS is for compression only */ - -#if MAX_MEM_LEVEL >= 8 -# define DEF_MEM_LEVEL 8 -#else -# define DEF_MEM_LEVEL MAX_MEM_LEVEL -#endif -/* default memLevel */ - -#define STORED_BLOCK 0 -#define STATIC_TREES 1 -#define DYN_TREES 2 -/* The three kinds of block type */ - -#define MIN_MATCH 3 -#define MAX_MATCH 258 -/* The minimum and maximum match lengths */ - -#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ - - /* target dependencies */ - -#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) -# define OS_CODE 0x00 -# ifndef Z_SOLO -# if defined(__TURBOC__) || defined(__BORLANDC__) -# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) - /* Allow compilation with ANSI keywords only enabled */ - void _Cdecl farfree( void *block ); - void *_Cdecl farmalloc( unsigned long nbytes ); -# else -# include -# endif -# else /* MSC or DJGPP */ -# include -# endif -# endif -#endif - -#ifdef AMIGA -# define OS_CODE 0x01 -#endif - -#if defined(VAXC) || defined(VMS) -# define OS_CODE 0x02 -# define F_OPEN(name, mode) \ - fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") -#endif - -#if defined(ATARI) || defined(atarist) -# define OS_CODE 0x05 -#endif - -#ifdef OS2 -# define OS_CODE 0x06 -# if defined(M_I86) && !defined(Z_SOLO) -# include -# endif -#endif - -#if defined(MACOS) -# define OS_CODE 0x07 -#endif - -#ifdef TOPS20 -# define OS_CODE 0x0a -#endif - -#ifdef WIN32 -# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ -# define OS_CODE 0x0b -# endif -#endif - -#ifdef __50SERIES /* Prime/PRIMOS */ -# define OS_CODE 0x0f -#endif - -#if defined(__BORLANDC__) && !defined(MSDOS) - #pragma warn -8004 - #pragma warn -8008 - #pragma warn -8066 -#endif - -/* provide prototypes for these when building zlib without LFS */ -#if !defined(_WIN32) && \ - (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) - ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); - ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); -#endif - - /* common defaults */ - -#ifndef OS_CODE -# define OS_CODE 0x03 /* assume Unix */ -#endif - -#ifndef F_OPEN -# define F_OPEN(name, mode) fopen((name), (mode)) -#endif - - /* functions */ - -#if defined(pyr) || defined(Z_SOLO) -# define NO_MEMCPY -#endif -#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) - /* Use our own functions for small and medium model with MSC <= 5.0. - * You may have to use the same strategy for Borland C (untested). - * The __SC__ check is for Symantec. - */ -# define NO_MEMCPY -#endif -#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) -# define HAVE_MEMCPY -#endif -#ifdef HAVE_MEMCPY -# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ -# define zmemcpy _fmemcpy -# define zmemcmp _fmemcmp -# define zmemzero(dest, len) _fmemset(dest, 0, len) -# else -# define zmemcpy memcpy -# define zmemcmp memcmp -# define zmemzero(dest, len) memset(dest, 0, len) -# endif -#else - void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); - int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); - void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); -#endif - -/* Diagnostic functions */ -#ifdef DEBUG -# include - extern int ZLIB_INTERNAL z_verbose; - extern void ZLIB_INTERNAL z_error OF((char *m)); -# define Assert(cond,msg) {if(!(cond)) z_error(msg);} -# define Trace(x) {if (z_verbose>=0) fprintf x ;} -# define Tracev(x) {if (z_verbose>0) fprintf x ;} -# define Tracevv(x) {if (z_verbose>1) fprintf x ;} -# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} -# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} -#else -# define Assert(cond,msg) -# define Trace(x) -# define Tracev(x) -# define Tracevv(x) -# define Tracec(c,x) -# define Tracecv(c,x) -#endif - -#ifndef Z_SOLO - voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, - unsigned size)); - void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); -#endif - -#define ZALLOC(strm, items, size) \ - (*((strm)->zalloc))((strm)->opaque, (items), (size)) -#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) -#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} - -/* Reverse the bytes in a 32-bit value */ -#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ - (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) - -#endif /* ZUTIL_H */ diff --git a/builtins/zstd/CMakeLists.txt b/builtins/zstd/CMakeLists.txt index e89a5bef5201a..ebcc57460ba69 100644 --- a/builtins/zstd/CMakeLists.txt +++ b/builtins/zstd/CMakeLists.txt @@ -25,6 +25,13 @@ if(MSVC) -DCMAKE_CXXFLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DCMAKE_CXXFLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE} -DCMAKE_CXXFLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}) + if(NOT CMAKE_GENERATOR MATCHES Ninja) + if(winrtdebug) + set(ROOT_ZSTD_BUILD_COMMAND_FLAGS "--config Debug") + else() + set(ROOT_ZSTD_BUILD_COMMAND_FLAGS "--config $,RelWithDebInfo,Release>") + endif() + endif() endif() ExternalProject_Add( @@ -40,8 +47,8 @@ ExternalProject_Add( -DCMAKE_CXX_VISIBILITY_PRESET=hidden -DCMAKE_C_VISIBILITY_PRESET=hidden -DZSTD_BUILD_SHARED=OFF - BUILD_COMMAND ${CMAKE_COMMAND} --build "/" --config $ - INSTALL_COMMAND ${CMAKE_COMMAND} --install "/" --config $ + BUILD_COMMAND ${CMAKE_COMMAND} --build "/" ${ROOT_ZSTD_BUILD_COMMAND_FLAGS} + INSTALL_COMMAND ${CMAKE_COMMAND} --install "/" ${ROOT_ZSTD_BUILD_COMMAND_FLAGS} LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 BUILD_IN_SOURCE 1 BUILD_BYPRODUCTS ${ROOT_ZSTD_LIBRARY} @@ -55,3 +62,10 @@ set_target_properties(ZSTD::ZSTD PROPERTIES IMPORTED_LOCATION ${ROOT_ZSTD_LIBRARY} INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIR} ) + +# Set the canonical output of find_package according to +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names +set(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR} PARENT_SCOPE) +set(ZSTD_LIBRARIES ${ROOT_ZSTD_LIBRARY} PARENT_SCOPE) +set(ZSTD_FOUND TRUE PARENT_SCOPE) +set(ZSTD_VERSION ${ROOT_ZSTD_VERSION} PARENT_SCOPE) diff --git a/cmake/modules/RootConfiguration.cmake b/cmake/modules/RootConfiguration.cmake index d223c74be3754..2c36dca4cb3f3 100644 --- a/cmake/modules/RootConfiguration.cmake +++ b/cmake/modules/RootConfiguration.cmake @@ -402,12 +402,10 @@ set(uselzma undef) set(usezstd undef) set(use${compression_default} define) -# cloudflare zlib is available only on x86 and aarch64 platforms with Linux -# for other platforms we have available builtin zlib 1.2.8 -if(builtin_zlib AND ZLIB_CF) - set(usecloudflarezlib define) +if(ZLIB_NG) + set(usezlibng define) else() - set(usecloudflarezlib undef) + set(usezlibng undef) endif() if(runtime_cxxmodules) set(usecxxmodules define) diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index 6b0a0702a6826..c3fd867327ae5 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -34,13 +34,21 @@ else() set(runtimedir ${CMAKE_INSTALL_PYTHONDIR}) endif() -if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") +if(CMAKE_GENERATOR MATCHES "Visual Studio") + set(GeneratorNeedsResourecLock False) set(GeneratorNeedsBuildSerialization True) - if(MSVC) - set(build_config "--config $") + if(winrtdebug) + set(build_config "--config Debug") + else() + set(build_config "--config $,RelWithDebInfo,Release>") endif() endif() +if(CMAKE_GENERATOR MATCHES Ninja) + set(GeneratorNeedsResourecLock True) + set(GeneratorNeedsBuildSerialization True) +endif() + set(ROOT_LIBRARY_PROPERTIES_NO_VERSION ${ROOT_LIBRARY_PROPERTIES_NO_VERSION} SUFFIX ${libsuffix} PREFIX ${libprefix} ) @@ -2514,7 +2522,7 @@ endfunction(ROOTTEST_ADD_AUTOMACROS) # order to manage dependencies. # #------------------------------------------------------------------------------- -macro(ROOTTEST_COMPILE_MACRO filename) +function(ROOTTEST_COMPILE_MACRO filename) CMAKE_PARSE_ARGUMENTS(ARG "" "BUILDOBJ;BUILDLIB" "FIXTURES_SETUP;FIXTURES_CLEANUP;FIXTURES_REQUIRED" ${ARGN}) # Add defines to root_compile_macro, in order to have out-of-source builds @@ -2555,6 +2563,7 @@ macro(ROOTTEST_COMPILE_MACRO filename) ROOTTEST_TARGETNAME_FROM_FILE(COMPILE_MACRO_TEST ${filename}) set(COMPILE_MACRO_TEST ${COMPILE_MACRO_TEST}-build) + set(COMPILE_MACRO_TEST ${COMPILE_MACRO_TEST} PARENT_SCOPE) add_test(NAME ${COMPILE_MACRO_TEST} COMMAND ${compile_macro_command}) if(NOT MSVC OR win_broken_tests) @@ -2575,7 +2584,7 @@ macro(ROOTTEST_COMPILE_MACRO filename) FIXTURES_REQUIRED ${ARG_FIXTURES_REQUIRED}) endif() -endmacro(ROOTTEST_COMPILE_MACRO) +endfunction(ROOTTEST_COMPILE_MACRO) #------------------------------------------------------------------------------- # @@ -2665,7 +2674,9 @@ macro(ROOTTEST_GENERATE_DICTIONARY dictname) set_property(TEST ${GENERATE_DICTIONARY_TEST} PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) if(GeneratorNeedsBuildSerialization) - set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + if(GeneratorNeedsResourecLock) + set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + endif() set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() @@ -2773,14 +2784,17 @@ macro(ROOTTEST_GENERATE_REFLEX_DICTIONARY dictionary) set_property(TEST ${GENERATE_REFLEX_TEST} PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) if(GeneratorNeedsBuildSerialization) - set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + if(GeneratorNeedsResourecLock) + set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + endif() set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() if (ARG_FIXTURES_SETUP) - set_property(TEST ${GENERATE_REFLEX_TEST} PROPERTY - FIXTURES_SETUP ${ARG_FIXTURES_SETUP}) + set(more_fixtures ${ARG_FIXTURES_SETUP}) endif() + set_property(TEST ${GENERATE_REFLEX_TEST} PROPERTY + FIXTURES_SETUP ${GENERATE_REFLEX_TEST}-fixture ${more_fixtures}) if (ARG_FIXTURES_CLEANUP) set_property(TEST ${GENERATE_REFLEX_TEST} PROPERTY @@ -2885,7 +2899,9 @@ macro(ROOTTEST_GENERATE_EXECUTABLE executable) endif() if(GeneratorNeedsBuildSerialization) - set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + if(GeneratorNeedsResourecLock) + set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + endif() set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() @@ -3241,7 +3257,7 @@ function(ROOTTEST_ADD_TEST testname) if(ARG_DEPENDS) foreach(dep ${ARG_DEPENDS}) if(${dep} MATCHES "[.]C" OR ${dep} MATCHES "[.]cxx" OR ${dep} MATCHES "[.]h") - ROOTTEST_COMPILE_MACRO(${dep}) + ROOTTEST_COMPILE_MACRO(${dep} FIXTURES_SETUP compile-macro-${dep}-fixture) list(APPEND deplist ${COMPILE_MACRO_TEST}) elseif(NOT ${dep} MATCHES "^roottest-") list(APPEND deplist ${testprefix}-${dep}) @@ -3249,6 +3265,16 @@ function(ROOTTEST_ADD_TEST testname) list(APPEND deplist ${dep}) endif() endforeach() + # Now extract all fixtures from the dependencies, so the fixture-providing + # tests get started even if using --rerun-failed, or -R or similar + foreach(dep ${deplist}) + if(TEST ${dep}) + get_test_property(${dep} FIXTURES_SETUP test_fixtures) + if(NOT test_fixtures STREQUAL "NOTFOUND") + list(APPEND fixtures_from_depends_argument ${test_fixtures}) + endif() + endif() + endforeach() endif(ARG_DEPENDS) if(ARG_FAILREGEX) @@ -3316,6 +3342,7 @@ function(ROOTTEST_ADD_TEST testname) if (ARG_FIXTURES_REQUIRED) set(fixtures_required ${ARG_FIXTURES_REQUIRED}) endif() + list(APPEND fixtures_required ${fixtures_from_depends_argument}) if (ARG_RESOURCE_LOCK) set(resource_lock ${ARG_RESOURCE_LOCK}) diff --git a/cmake/modules/SearchInstalledSoftware.cmake b/cmake/modules/SearchInstalledSoftware.cmake index 30a94d1858073..c872043cf5c85 100644 --- a/cmake/modules/SearchInstalledSoftware.cmake +++ b/cmake/modules/SearchInstalledSoftware.cmake @@ -108,10 +108,21 @@ foreach(suffix FOUND INCLUDE_DIR LIBRARY LIBRARY_DEBUG LIBRARY_RELEASE LIBRARIES unset(ZSTD_${suffix} CACHE) endforeach() -ROOT_FIND_REQUIRED_DEP(ZLIB builtin_zlib) +# Request explicit user opt-in for required dependencies +if(asimage) + ROOT_FIND_REQUIRED_DEP(Freetype builtin_freetype) + ROOT_FIND_REQUIRED_DEP(GIF builtin_gif) + ROOT_FIND_REQUIRED_DEP(JPEG builtin_jpeg) + # We cannot PNG here because while searching PNG, CMake will also find ZLIB. + # If found, CMake will define the default variables and target: + # see https://cmake.org/cmake/help/latest/module/FindZLIB.html). + # For this reason, the check has to be put below, after ZLIB is searched for. + #ROOT_FIND_REQUIRED_DEP(PNG builtin_png) +endif() +ROOT_FIND_REQUIRED_DEP(LZ4 builtin_lz4) ROOT_FIND_REQUIRED_DEP(LibLZMA builtin_lzma) +ROOT_FIND_REQUIRED_DEP(ZLIB builtin_zlib) ROOT_FIND_REQUIRED_DEP(ZSTD builtin_zstd) -ROOT_FIND_REQUIRED_DEP(LZ4 builtin_lz4) if(NOT "${MISSING_PACKAGES}" STREQUAL "") message(FATAL_ERROR "The following packages need to be installed or enabled to build ROOT: ${MISSING_PACKAGES}") @@ -142,6 +153,29 @@ endif() if(builtin_zlib) list(APPEND ROOT_BUILTINS ZLIB) add_subdirectory(builtins/zlib) +else() + # If not built-in, check if this is zlib-ng + set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS}) + message(STATUS "Checking whether zlib-ng is provided") + check_c_source_compiles(" + #include + #ifndef ZLIBNG_VERNUM + #error Not zlib-ng + #endif + int main() { return 0; } + " ZLIB_NG) +endif() + +if(ZLIB_NG) + message(STATUS "Zlib-ng detected") +else() + message(STATUS "Zlib detected") +endif() + +if(asimage) + # This check can be added only now because of the reasons explained above, where all + # other required dependencies are checked. + ROOT_FIND_REQUIRED_DEP(PNG builtin_png) endif() #---Check for nlohmann/json.hpp--------------------------------------------------------- @@ -228,70 +262,8 @@ if(NOT builtin_freetype) endif() if(builtin_freetype) - set(freetype_version 2.12.1) - message(STATUS "Building freetype version ${freetype_version} included in ROOT itself") - set(FREETYPE_LIBRARY ${CMAKE_BINARY_DIR}/FREETYPE-prefix/src/FREETYPE/objs/.libs/${CMAKE_STATIC_LIBRARY_PREFIX}freetype${CMAKE_STATIC_LIBRARY_SUFFIX}) - if(WIN32) - set(FREETYPE_LIB_DIR ".") - if(CMAKE_GENERATOR MATCHES Ninja) - set(freetypelib freetype.lib) - if (CMAKE_BUILD_TYPE MATCHES Debug) - set(freetypelib freetyped.lib) - endif() - else() - set(freetypebuild Release) - set(freetypelib freetype.lib) - if(winrtdebug) - set(freetypebuild Debug) - set(freetypelib freetyped.lib) - endif() - set(FREETYPE_LIB_DIR "${freetypebuild}") - set(FREETYPE_EXTRA_BUILD_ARGS --config ${freetypebuild}) - endif() - ExternalProject_Add( - FREETYPE - URL ${CMAKE_SOURCE_DIR}/graf2d/freetype/src/freetype-${freetype_version}.tar.gz - URL_HASH SHA256=efe71fd4b8246f1b0b1b9bfca13cfff1c9ad85930340c27df469733bbb620938 - INSTALL_DIR ${CMAKE_BINARY_DIR} - CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DFT_DISABLE_BZIP2=TRUE - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 - BUILD_COMMAND ${CMAKE_COMMAND} --build . ${FREETYPE_EXTRA_BUILD_ARGS} - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FREETYPE_LIB_DIR}/${freetypelib} ${FREETYPE_LIBRARY} - LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 - BUILD_IN_SOURCE 0 - BUILD_BYPRODUCTS ${FREETYPE_LIBRARY} - TIMEOUT 600 - ) - else() - set(_freetype_cflags -O) - set(_freetype_cc ${CMAKE_C_COMPILER}) - if(CMAKE_SYSTEM_NAME STREQUAL AIX) - set(_freetype_zlib --without-zlib) - endif() - set(_freetype_brotli "--with-brotli=no") - if(CMAKE_OSX_SYSROOT) - set(_freetype_cc "${_freetype_cc} -isysroot ${CMAKE_OSX_SYSROOT}") - endif() - ExternalProject_Add( - FREETYPE - URL ${CMAKE_SOURCE_DIR}/graf2d/freetype/src/freetype-${freetype_version}.tar.gz - URL_HASH SHA256=efe71fd4b8246f1b0b1b9bfca13cfff1c9ad85930340c27df469733bbb620938 - CONFIGURE_COMMAND ./configure --prefix --with-pic - --disable-shared --with-png=no --with-bzip2=no - --with-harfbuzz=no ${_freetype_brotli} ${_freetype_zlib} - "CC=${_freetype_cc}" CFLAGS=${_freetype_cflags} - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_OUTPUT_ON_FAILURE 1 - BUILD_IN_SOURCE 1 - BUILD_BYPRODUCTS ${FREETYPE_LIBRARY} - TIMEOUT 600 - ) - endif() - set(FREETYPE_INCLUDE_DIR ${CMAKE_BINARY_DIR}/FREETYPE-prefix/src/FREETYPE/include) - set(FREETYPE_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIR}) - set(FREETYPE_LIBRARIES ${FREETYPE_LIBRARY}) - set(FREETYPE_TARGET FREETYPE) + list(APPEND ROOT_BUILTINS FREETYPE) + add_subdirectory(builtins/freetype) endif() #---Check for Cocoa/Quartz graphics backend (MacOS X only)--------------------------- @@ -422,6 +394,7 @@ if(asimage) add_subdirectory(builtins/libgif) get_target_property(GIF_INCLUDE_DIR GIF::GIF INTERFACE_INCLUDE_DIRECTORIES) get_target_property(GIF_LIBRARY_LOCATION GIF::GIF IMPORTED_LOCATION) + list(APPEND ASEXTRA_LIBRARIES GIF::GIF) endif() if(NOT builtin_png) @@ -443,6 +416,7 @@ if(asimage) add_subdirectory(builtins/libpng) get_target_property(PNG_INCLUDE_DIR PNG::PNG INTERFACE_INCLUDE_DIRECTORIES) get_target_property(PNG_LIBRARY_LOCATION PNG::PNG IMPORTED_LOCATION) + list(APPEND ASEXTRA_LIBRARIES PNG::PNG) endif() if(NOT builtin_jpeg) @@ -462,6 +436,7 @@ if(asimage) add_subdirectory(builtins/libjpeg) get_target_property(JPEG_INCLUDE_DIR JPEG::JPEG INTERFACE_INCLUDE_DIRECTORIES) get_target_property(JPEG_LIBRARY_LOCATION JPEG::JPEG IMPORTED_LOCATION) + list(APPEND ASEXTRA_LIBRARIES JPEG::JPEG) endif() if(asimage_tiff) @@ -496,8 +471,8 @@ if(asimage) INSTALL_DIR ${CMAKE_BINARY_DIR} CMAKE_ARGS -G ${CMAKE_GENERATOR} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DFREETYPE_INCLUDE_DIR=${FREETYPE_INCLUDE_DIR} - -DZLIB_INCLUDE_DIR=${ZLIB_INCLUDE_DIR} + -DFREETYPE_INCLUDE_DIR=${FREETYPE_INCLUDE_DIRS} + -DZLIB_INCLUDE_DIR=${ZLIB_INCLUDE_DIRS} -DJPEG_INCLUDE_DIR=${JPEG_INCLUDE_DIR} -DJPEG_LIBRARY_LOCATION=${JPEG_LIBRARY_LOCATION} -DPNG_INCLUDE_DIR=${PNG_INCLUDE_DIR} @@ -524,14 +499,14 @@ if(asimage) list(APPEND afterimage_extra_args --without-x) endif() if(builtin_freetype) - list(APPEND afterimage_extra_args --with-ttf-includes=-I${FREETYPE_INCLUDE_DIR}) + list(APPEND afterimage_extra_args --with-ttf-includes=-I${FREETYPE_INCLUDE_DIRS}) set(_after_cflags "${_after_cflags} -DHAVE_FREETYPE_FREETYPE -DPNG_ARM_NEON_OPT=0") endif() if(CMAKE_OSX_SYSROOT) set(_after_cflags "${_after_cflags} -isysroot ${CMAKE_OSX_SYSROOT}") endif() if(builtin_zlib) - set(_after_cflags "${_after_cflags} -I${ZLIB_INCLUDE_DIR}") + set(_after_cflags "${_after_cflags} -I${ZLIB_INCLUDE_DIRS}") endif() if(CMAKE_SYSTEM_NAME MATCHES FreeBSD) set(AFTERIMAGE_LIBRARIES ${CMAKE_BINARY_DIR}/AFTERIMAGE-prefix/src/AFTERIMAGE/libAfterImage${CMAKE_STATIC_LIBRARY_SUFFIX}) @@ -572,7 +547,7 @@ if(asimage) add_dependencies(AFTERIMAGE BUILTIN_LIBPNG) endif() if(builtin_freetype) - add_dependencies(AFTERIMAGE FREETYPE) + add_dependencies(AFTERIMAGE BUILTIN_FREETYPE) endif() set(AFTERIMAGE_TARGET AFTERIMAGE) endif() @@ -754,8 +729,7 @@ if(ssl AND NOT builtin_openssl) message(STATUS "OpenSSL not found, and no internet connection. Disabling the 'ssl' option.") set(ssl OFF CACHE BOOL "Disabled because ssl requested and OpenSSL not found (${builtin_openssl_description}) and there is no internet connection" FORCE) else() - message(STATUS "OpenSSL not found, switching ON 'builtin_openssl' option.") - set(builtin_openssl ON CACHE BOOL "Enabled because ssl requested and OpenSSL not found (${builtin_openssl_description})" FORCE) + message(SEND_ERROR "OpenSSL required but not found. Install it on the system (preferred), or explicitly request the builtin version.") endif() endif() endif() @@ -1512,13 +1486,6 @@ if(tmva) message(STATUS "TMVA: Numpy or Python development package not found for python ${Python3_EXECUTABLE}. Switching off tmva-pymva option") set(tmva-pymva OFF CACHE BOOL "Disabled because Numpy or Python development package were not found (${tmva-pymva_description})" FORCE) endif() - if(testing) - message(STATUS "Looking for BLAS as an optional testing dependency of PyMVA") - find_package(BLAS) - if(NOT BLAS_FOUND) - message(WARNING "BLAS not found: PyMVA will not be fully tested") - endif() - endif() endif() if (R_FOUND) #Rmva is enable when r is found and tmva is on diff --git a/config/RConfigure.in b/config/RConfigure.in index a0e683ede7a8a..f33528d8e1a55 100644 --- a/config/RConfigure.in +++ b/config/RConfigure.in @@ -57,7 +57,7 @@ #@usezlib@ R__HAS_DEFAULT_ZLIB /**/ #@uselzma@ R__HAS_DEFAULT_LZMA /**/ #@usezstd@ R__HAS_DEFAULT_ZSTD /**/ -#@usecloudflarezlib@ R__HAS_CLOUDFLARE_ZLIB /**/ +#@usezlibng@ R__HAS_ZLIB_NG /**/ #@hastmvacpu@ R__HAS_TMVACPU /**/ #@hastmvagpu@ R__HAS_TMVAGPU /**/ diff --git a/core/base/inc/TAttFill.h b/core/base/inc/TAttFill.h index 32c64d778b3d5..5fc086efbcac5 100644 --- a/core/base/inc/TAttFill.h +++ b/core/base/inc/TAttFill.h @@ -32,7 +32,7 @@ class TAttFill { virtual Color_t GetFillColor() const { return fFillColor; } ///< Return the fill area color virtual Style_t GetFillStyle() const { return fFillStyle; } ///< Return the fill area style virtual Bool_t IsTransparent() const; - virtual void ModifyOn(TVirtualPad *pad); + virtual void ModifyOn(TVirtualPad &pad); virtual void Modify(); virtual void ResetAttFill(Option_t *option=""); virtual void SaveFillAttributes(std::ostream &out, const char *name, Int_t coldef=1, Int_t stydef=1001); diff --git a/core/base/inc/TAttLine.h b/core/base/inc/TAttLine.h index 7d90ce1af1c4d..246f752589587 100644 --- a/core/base/inc/TAttLine.h +++ b/core/base/inc/TAttLine.h @@ -37,7 +37,7 @@ class TAttLine { virtual Style_t GetLineStyle() const {return fLineStyle;} ///< Return the line style virtual Width_t GetLineWidth() const {return fLineWidth;} ///< Return the line width virtual void Modify(); - virtual void ModifyOn(TVirtualPad *pad); + virtual void ModifyOn(TVirtualPad &pad); virtual void ResetAttLine(Option_t *option=""); virtual void SaveLineAttributes(std::ostream &out, const char *name, Int_t coldef=1, Int_t stydef=1, Int_t widdef=1); virtual void SetLineAttributes(); // *MENU* diff --git a/core/base/inc/TAttMarker.h b/core/base/inc/TAttMarker.h index 46dfb61412aed..765cf574975da 100644 --- a/core/base/inc/TAttMarker.h +++ b/core/base/inc/TAttMarker.h @@ -34,7 +34,7 @@ class TAttMarker { virtual Style_t GetMarkerStyle() const {return fMarkerStyle;} ///< Return the marker style virtual Size_t GetMarkerSize() const {return fMarkerSize;} ///< Return the marker size virtual void Modify(); - virtual void ModifyOn(TVirtualPad *pad); + virtual void ModifyOn(TVirtualPad &pad); virtual void ResetAttMarker(Option_t *toption=""); virtual void SaveMarkerAttributes(std::ostream &out, const char *name, Int_t coldef=1, Int_t stydef=1, Int_t sizdef=1); virtual void SetMarkerAttributes(); // *MENU* diff --git a/core/base/inc/TAttText.h b/core/base/inc/TAttText.h index ce46d6523f4a6..d857a257facc0 100644 --- a/core/base/inc/TAttText.h +++ b/core/base/inc/TAttText.h @@ -39,7 +39,7 @@ class TAttText { virtual Float_t GetTextSize() const {return fTextSize;} ///< Return the text size virtual Float_t GetTextSizePercent(Float_t size); ///< Return the text in percent of the pad size virtual void Modify(); - virtual void ModifyOn(TVirtualPad *pad); + virtual void ModifyOn(TVirtualPad &pad); virtual void ResetAttText(Option_t *toption=""); virtual void SaveTextAttributes(std::ostream &out, const char *name, Int_t alidef=12, Float_t angdef=0, Int_t coldef=1, Int_t fondef=61, Float_t sizdef=1); virtual void SetTextAttributes(); // *MENU* diff --git a/core/base/inc/TVirtualPadPainter.h b/core/base/inc/TVirtualPadPainter.h index 830d0bc8acb4e..d4109a95c27ff 100644 --- a/core/base/inc/TVirtualPadPainter.h +++ b/core/base/inc/TVirtualPadPainter.h @@ -13,8 +13,14 @@ #define ROOT_TVirtualPadPainter #include "Rtypes.h" +#include "GuiTypes.h" class TVirtualPad; +class TVirtualPS; +class TAttFill; +class TAttLine; +class TAttMarker; +class TAttText; class TVirtualPadPainter { public: @@ -75,6 +81,11 @@ class TVirtualPadPainter { virtual void SetMarkerStyle(Style_t /* mstyle */ = 1) {} virtual void SetMarkerSize(Size_t /* msize */ = 1) {} + virtual void SetAttFill(const TAttFill &att); + virtual void SetAttLine(const TAttLine &att); + virtual void SetAttMarker(const TAttMarker &att); + virtual void SetAttText(const TAttText &att); + //This part is an interface to X11 pixmap management and to save sub-pads off-screens for OpenGL. //Currently, must be implemented only for X11/GDI virtual Int_t CreateDrawable(UInt_t w, UInt_t h) = 0;//gVirtualX->OpenPixmap @@ -83,6 +94,11 @@ class TVirtualPadPainter { virtual void CopyDrawable(Int_t device, Int_t px, Int_t py) = 0; virtual void DestroyDrawable(Int_t device) = 0;//gVirtualX->CloseWindow virtual void SelectDrawable(Int_t device) = 0;//gVirtualX->SelectWindow + virtual void UpdateDrawable(Int_t /* mode */) {} + virtual void SetDrawMode(Int_t /* device */, Int_t /* mode */) {} + virtual void SetDoubleBuffer(Int_t device, Int_t mode); + virtual void SetCursor(Int_t win, ECursor cursor); + //TASImage support. virtual void DrawPixels(const unsigned char *pixelData, UInt_t width, UInt_t height, @@ -127,6 +143,8 @@ class TVirtualPadPainter { virtual Bool_t IsNative() const { return kFALSE; } virtual Bool_t IsCocoa() const { return kFALSE; } + virtual TVirtualPS *GetPS() const { return nullptr; } + virtual Bool_t IsSupportAlpha() const { return kFALSE; } static TVirtualPadPainter *PadPainter(Option_t *opt = ""); diff --git a/core/base/inc/TVirtualX.h b/core/base/inc/TVirtualX.h index 6ab0aea521a81..ddbae75d26dbc 100644 --- a/core/base/inc/TVirtualX.h +++ b/core/base/inc/TVirtualX.h @@ -67,13 +67,26 @@ class TVirtualX : public TNamed, public TAttLine, public TAttFill, public TAttTe virtual void CreateOpenGLContext(Int_t wid=0); virtual void DeleteOpenGLContext(Int_t wid=0); - //---- OpenGL related stuff, required only with R__HAS_COCOA ---- - virtual Double_t GetOpenGLScalingFactor(); - virtual Window_t CreateOpenGLWindow(Window_t parentID, UInt_t width, UInt_t height, const std::vector > &format); - virtual Handle_t CreateOpenGLContext(Window_t windowID, Handle_t sharedContext); - virtual Bool_t MakeOpenGLContextCurrent(Handle_t ctx, Window_t windowID); - virtual Handle_t GetCurrentOpenGLContext(); - virtual void FlushOpenGLBuffer(Handle_t ctx); + + //---- Old graphics interface ----- + + void SetFillColor(Color_t cindex) override; + void SetFillStyle(Style_t style) override; + void SetLineColor(Color_t cindex) override; + virtual void SetLineType(Int_t n, Int_t *dash); + void SetLineStyle(Style_t linestyle) override; + void SetLineWidth(Width_t width) override; + void SetMarkerColor(Color_t cindex) override; + void SetMarkerSize(Float_t markersize) override; + void SetMarkerStyle(Style_t markerstyle) override; + virtual void SetOpacity(Int_t percent); + virtual void SetRGB(Int_t cindex, Float_t r, Float_t g, Float_t b); + void SetTextAlign(Short_t talign=11) override; + void SetTextColor(Color_t cindex) override; + virtual Int_t SetTextFont(char *fontname, ETextSetMode mode); + void SetTextFont(Font_t fontnumber) override; + virtual void SetTextMagnitude(Float_t mgn); + void SetTextSize(Float_t textsize) override; virtual void DrawBox(Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode); virtual void DrawCellArray(Int_t x1, Int_t y1, Int_t x2, Int_t y2, @@ -87,6 +100,37 @@ class TVirtualX : public TNamed, public TAttLine, public TAttFill, public TAttTe ETextMode mode); virtual void DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, const wchar_t *text, ETextMode mode); + + //---- New graphics interface ----- + + virtual WinContext_t GetWindowContext(Int_t wid); + virtual void SetAttFill(WinContext_t wctxt, const TAttFill &att); + virtual void SetAttLine(WinContext_t wctxt, const TAttLine &att); + virtual void SetAttMarker(WinContext_t wctxt, const TAttMarker &att); + virtual void SetAttText(WinContext_t wctxt, const TAttText &att); + virtual void SetDrawModeW(WinContext_t wctxt, EDrawMode mode); + virtual EDrawMode GetDrawModeW(WinContext_t wctxt); + virtual void ClearWindowW(WinContext_t wctxt); + virtual void UpdateWindowW(WinContext_t wctxt, Int_t mode); + + virtual void DrawBoxW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode); + virtual void DrawFillAreaW(WinContext_t wctxt, Int_t n, TPoint *xy); + virtual void DrawLineW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2); + virtual void DrawPolyLineW(WinContext_t wctxt, Int_t n, TPoint *xy); + virtual void DrawLinesSegmentsW(WinContext_t wctxt, Int_t n, TPoint *xy); + virtual void DrawPolyMarkerW(WinContext_t wctxt, Int_t n, TPoint *xy); + virtual void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode); + virtual void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, const wchar_t *text, ETextMode mode); + + + //---- OpenGL related stuff, required only with R__HAS_COCOA ---- + virtual Double_t GetOpenGLScalingFactor(); + virtual Window_t CreateOpenGLWindow(Window_t parentID, UInt_t width, UInt_t height, const std::vector > &format); + virtual Handle_t CreateOpenGLContext(Window_t windowID, Handle_t sharedContext); + virtual Bool_t MakeOpenGLContextCurrent(Handle_t ctx, Window_t windowID); + virtual Handle_t GetCurrentOpenGLContext(); + virtual void FlushOpenGLBuffer(Handle_t ctx); + virtual UInt_t ExecCommand(TGWin32Command *code); virtual void GetCharacterUp(Float_t &chupx, Float_t &chupy); EDrawMode GetDrawMode() { return fDrawMode; } @@ -129,23 +173,6 @@ class TVirtualX : public TNamed, public TAttLine, public TAttFill, public TAttTe virtual void SetDoubleBufferOFF(); virtual void SetDoubleBufferON(); virtual void SetDrawMode(EDrawMode mode); - void SetFillColor(Color_t cindex) override; - void SetFillStyle(Style_t style) override; - void SetLineColor(Color_t cindex) override; - virtual void SetLineType(Int_t n, Int_t *dash); - void SetLineStyle(Style_t linestyle) override; - void SetLineWidth(Width_t width) override; - void SetMarkerColor(Color_t cindex) override; - void SetMarkerSize(Float_t markersize) override; - void SetMarkerStyle(Style_t markerstyle) override; - virtual void SetOpacity(Int_t percent); - virtual void SetRGB(Int_t cindex, Float_t r, Float_t g, Float_t b); - void SetTextAlign(Short_t talign=11) override; - void SetTextColor(Color_t cindex) override; - virtual Int_t SetTextFont(char *fontname, ETextSetMode mode); - void SetTextFont(Font_t fontnumber) override; - virtual void SetTextMagnitude(Float_t mgn); - void SetTextSize(Float_t textsize) override; virtual void Sync(Int_t mode); virtual void UpdateWindow(Int_t mode); virtual void Warp(Int_t ix, Int_t iy, Window_t id = 0); diff --git a/core/base/src/TAttFill.cxx b/core/base/src/TAttFill.cxx index c639697e0e9eb..257318a061268 100644 --- a/core/base/src/TAttFill.cxx +++ b/core/base/src/TAttFill.cxx @@ -215,19 +215,18 @@ void TAttFill::Copy(TAttFill &attfill) const void TAttFill::Modify() { - ModifyOn(gPad); + if (gPad) + ModifyOn(*gPad); } //////////////////////////////////////////////////////////////////////////////// /// Change current fill area attributes on speicifed pad -void TAttFill::ModifyOn(TVirtualPad *pad) +void TAttFill::ModifyOn(TVirtualPad &pad) { - auto pp = pad ? pad->GetPainter() : nullptr; - if (!pp) - return; - pp->SetFillColor(fFillColor); - pp->SetFillStyle(fFillStyle); + auto pp = pad.GetPainter(); + if (pp) + pp->SetAttFill(*this); } //////////////////////////////////////////////////////////////////////////////// diff --git a/core/base/src/TAttLine.cxx b/core/base/src/TAttLine.cxx index 23dae3e5c3386..e001ca4ac20c8 100644 --- a/core/base/src/TAttLine.cxx +++ b/core/base/src/TAttLine.cxx @@ -245,22 +245,33 @@ Int_t TAttLine::DistancetoLine(Int_t px, Int_t py, Double_t xp1, Double_t yp1, D void TAttLine::Modify() { - ModifyOn(gPad); + if (gPad) + ModifyOn(*gPad); } //////////////////////////////////////////////////////////////////////////////// /// Change current line attributes on specified pad -void TAttLine::ModifyOn(TVirtualPad *pad) +void TAttLine::ModifyOn(TVirtualPad &pad) { - auto pp = pad ? pad->GetPainter() : nullptr; + auto pp = pad.GetPainter(); if (!pp) return; - pp->SetLineColor(fLineColor); - pp->SetLineStyle((fLineStyle > 0 && fLineStyle < 30) ? fLineStyle : 1); - pp->SetLineWidth(std::abs(fLineWidth % 100)); -} + Bool_t normal_style = (fLineStyle > 0) && (fLineStyle < 30); + Bool_t normal_width = (fLineWidth >= 0) && (fLineWidth < 100); + + if (normal_style && normal_width) + pp->SetAttLine(*this); + else { + TAttLine att1(*this); + if (!normal_style) + att1.SetLineStyle(1); + if (!normal_width) + att1.SetLineWidth(std::abs(fLineWidth % 100)); + pp->SetAttLine(att1); + } +} //////////////////////////////////////////////////////////////////////////////// /// Reset this line attributes to default values. diff --git a/core/base/src/TAttMarker.cxx b/core/base/src/TAttMarker.cxx index da84a7266dc22..97b85c36aa3bd 100644 --- a/core/base/src/TAttMarker.cxx +++ b/core/base/src/TAttMarker.cxx @@ -321,21 +321,18 @@ Width_t TAttMarker::GetMarkerLineWidth(Style_t style) void TAttMarker::Modify() { - ModifyOn(gPad); + if (gPad) + ModifyOn(*gPad); } //////////////////////////////////////////////////////////////////////////////// /// Change current marker attributes if necessary on specified pad. -void TAttMarker::ModifyOn(TVirtualPad *pad) +void TAttMarker::ModifyOn(TVirtualPad &pad) { - auto pp = pad ? pad->GetPainter() : nullptr; - if (!pp) - return; - - pp->SetMarkerColor(fMarkerColor); - pp->SetMarkerSize (fMarkerSize); - pp->SetMarkerStyle(fMarkerStyle); + auto pp = pad.GetPainter(); + if (pp) + pp->SetAttMarker(*this); } diff --git a/core/base/src/TAttText.cxx b/core/base/src/TAttText.cxx index 2aee8a5a5cd78..2e078add3b152 100644 --- a/core/base/src/TAttText.cxx +++ b/core/base/src/TAttText.cxx @@ -326,51 +326,49 @@ Float_t TAttText::GetTextSizePercent(Float_t size) void TAttText::Modify() { - ModifyOn(gPad); + if (gPad) + ModifyOn(*gPad); } - //////////////////////////////////////////////////////////////////////////////// /// Change current text attributes if necessary on specified pad. -void TAttText::ModifyOn(TVirtualPad *pad) +void TAttText::ModifyOn(TVirtualPad &pad) { - auto pp = pad ? pad->GetPainter() : nullptr; + auto pp = pad.GetPainter(); if (!pp) return; + Float_t tsize0 = fTextSize; Float_t tsize = fTextSize; // there was difference in text size handling, keep it in one place if (pp->IsNative()) { if (fTextFont % 10 <= 2) { - auto wh = pad->XtoPixel(pad->GetX2()); - auto hh = pad->YtoPixel(pad->GetY1()); + auto wh = pad.XtoPixel(pad.GetX2()); + auto hh = pad.YtoPixel(pad.GetY1()); if (wh < hh) tsize *= wh; else tsize *= hh; } } else { if (fTextFont % 10 > 2) { - Float_t wh = pad->XtoPixel(pad->GetX2()); - Float_t hh = pad->YtoPixel(pad->GetY1()); + Float_t wh = pad.XtoPixel(pad.GetX2()); + Float_t hh = pad.YtoPixel(pad.GetY1()); if (wh < hh) { - Float_t dy = pad->AbsPixeltoX(Int_t(tsize)) - pad->AbsPixeltoX(0); - tsize = dy/(pad->GetX2() - pad->GetX1()); + Float_t dy = pad.AbsPixeltoX(Int_t(tsize)) - pad.AbsPixeltoX(0); + tsize = dy/(pad.GetX2() - pad.GetX1()); } else { - Float_t dy = pad->AbsPixeltoY(0) - pad->AbsPixeltoY(Int_t(tsize)); - tsize = dy/(pad->GetY2() - pad->GetY1()); + Float_t dy = pad.AbsPixeltoY(0) - pad.AbsPixeltoY(Int_t(tsize)); + tsize = dy/(pad.GetY2() - pad.GetY1()); } } } - pp->SetTextAngle(fTextAngle); - if (pp->GetTextFont() != fTextFont) { - pp->SetTextFont(fTextFont); - pp->SetTextSize(tsize); - } else if (pp->GetTextSize() != tsize) { - pp->SetTextSize(tsize); - } - pp->SetTextAlign(fTextAlign); - pp->SetTextColor(fTextColor); + + fTextSize = tsize; + + pp->SetAttText(*this); + + fTextSize = tsize0; } diff --git a/core/base/src/TDirectory.cxx b/core/base/src/TDirectory.cxx index a8bcee69e8db1..e33d82a3694d8 100644 --- a/core/base/src/TDirectory.cxx +++ b/core/base/src/TDirectory.cxx @@ -207,8 +207,10 @@ void TDirectory::Append(TObject *obj, Bool_t replace /* = kFALSE */) if (replace && obj->GetName() && obj->GetName()[0]) { TObject *old; while (nullptr != (old = GetList()->FindObject(obj->GetName()))) { - Warning("Append","Replacing existing %s: %s (Potential memory leak).", - obj->IsA()->GetName(),obj->GetName()); + if (obj != old) { + Warning("Append", "Replacing existing %s: %s (Potential memory leak).", obj->IsA()->GetName(), + obj->GetName()); + } ROOT::DirAutoAdd_t func = old->IsA()->GetDirectoryAutoAdd(); if (func) { func(old,nullptr); diff --git a/core/base/src/TVirtualPadPainter.cxx b/core/base/src/TVirtualPadPainter.cxx index b8d5fe05fbe8a..b1e22c286d97f 100644 --- a/core/base/src/TVirtualPadPainter.cxx +++ b/core/base/src/TVirtualPadPainter.cxx @@ -11,6 +11,11 @@ #include "TVirtualPadPainter.h" #include "TPluginManager.h" +#include "TAttFill.h" +#include "TAttLine.h" +#include "TAttMarker.h" +#include "TAttText.h" +#include "TVirtualX.h" /** \class TVirtualPadPainter @@ -96,3 +101,64 @@ void TVirtualPadPainter::DrawTextUrl(Double_t x, Double_t y, const char *text, c { DrawText(x, y, text, kClear); } + +//////////////////////////////////////////////////////////////////////////////// +/// Set fill attributes + +void TVirtualPadPainter::SetAttFill(const TAttFill &att) +{ + SetFillColor(att.GetFillColor()); + SetFillStyle(att.GetFillStyle()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set line attributes + +void TVirtualPadPainter::SetAttLine(const TAttLine &att) +{ + SetLineColor(att.GetLineColor()); + SetLineStyle(att.GetLineStyle()); + SetLineWidth(att.GetLineWidth()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set marker attributes + +void TVirtualPadPainter::SetAttMarker(const TAttMarker &att) +{ + SetMarkerColor(att.GetMarkerColor()); + SetMarkerSize(att.GetMarkerSize()); + SetMarkerStyle(att.GetMarkerStyle()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set text attributes + +void TVirtualPadPainter::SetAttText(const TAttText &att) +{ + SetTextAlign(att.GetTextAlign()); + SetTextAngle(att.GetTextAngle()); + SetTextColor(att.GetTextColor()); + SetTextSize(att.GetTextSize()); + SetTextFont(att.GetTextFont()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set double buffer mode for specified device, redirect to gVirtualX + +void TVirtualPadPainter::SetDoubleBuffer(Int_t device, Int_t mode) +{ + // TODO: move to actual painter classes, call only for selected device + if (gVirtualX) + gVirtualX->SetDoubleBuffer(device, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set cursor for specified device, redirect to gVirtualX + +void TVirtualPadPainter::SetCursor(Int_t device, ECursor cursor) +{ + // TODO: move to actual painter classes, call only for selected device + if (gVirtualX) + gVirtualX->SetCursor(device, cursor); +} diff --git a/core/base/src/TVirtualX.cxx b/core/base/src/TVirtualX.cxx index 3de70acf015be..3c0f39558e328 100644 --- a/core/base/src/TVirtualX.cxx +++ b/core/base/src/TVirtualX.cxx @@ -378,6 +378,152 @@ void TVirtualX::DrawText(Int_t /*x*/, Int_t /*y*/, Float_t /*angle*/, { } +//////////////////////////////////////////////////////////////////////////////// +/// Get window drawing context +/// Should remain valid until window exists + +WinContext_t TVirtualX::GetWindowContext(Int_t /* wid */) +{ + return (WinContext_t) 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set fill attributes for specified window + +void TVirtualX::SetAttFill(WinContext_t /* wctxt */, const TAttFill &att) +{ + SetFillColor(att.GetFillColor()); + SetFillStyle(att.GetFillStyle()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set line attributes for specified window + +void TVirtualX::SetAttLine(WinContext_t /* wctxt */, const TAttLine &att) +{ + SetLineColor(att.GetLineColor()); + SetLineStyle(att.GetLineStyle()); + SetLineWidth(att.GetLineWidth()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set marker attributes for specified window + +void TVirtualX::SetAttMarker(WinContext_t /* wctxt */, const TAttMarker &att) +{ + SetMarkerColor(att.GetMarkerColor()); + SetMarkerSize(att.GetMarkerSize()); + SetMarkerStyle(att.GetMarkerStyle()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set text attributes for specified window + +void TVirtualX::SetAttText(WinContext_t /* wctxt */, const TAttText &att) +{ + SetTextAlign(att.GetTextAlign()); + SetTextAngle(att.GetTextAngle()); + SetTextColor(att.GetTextColor()); + SetTextSize(att.GetTextSize()); + SetTextFont(att.GetTextFont()); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set window draw mode + +void TVirtualX::SetDrawModeW(WinContext_t /* wctxt */, EDrawMode mode) +{ + SetDrawMode(mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns window draw mode + +TVirtualX::EDrawMode TVirtualX::GetDrawModeW(WinContext_t /* wctxt */) +{ + return GetDrawMode(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Clear specified window + +void TVirtualX::ClearWindowW(WinContext_t /* wctxt */) +{ + ClearWindow(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Update specified window + +void TVirtualX::UpdateWindowW(WinContext_t /* wctxt */, Int_t mode) +{ + UpdateWindow(mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw box on specified window + +void TVirtualX::DrawBoxW(WinContext_t /* wctxt */, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) +{ + DrawBox(x1, y1, x2, y2, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw fill area on specified window + +void TVirtualX::DrawFillAreaW(WinContext_t /* wctxt */, Int_t n, TPoint *xy) +{ + DrawFillArea(n, xy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw line on specified window + +void TVirtualX::DrawLineW(WinContext_t /* wctxt */, Int_t x1, Int_t y1, Int_t x2, Int_t y2) +{ + DrawLine(x1, y1, x2, y2); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw poly line on specified window + +void TVirtualX::DrawPolyLineW(WinContext_t /* wctxt */, Int_t n, TPoint *xy) +{ + DrawPolyLine(n, xy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw line segments on specified window + +void TVirtualX::DrawLinesSegmentsW(WinContext_t /* wctxt */, Int_t n, TPoint *xy) +{ + DrawLinesSegments(n, xy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw poly marker on specified window + +void TVirtualX::DrawPolyMarkerW(WinContext_t /* wctxt */, Int_t n, TPoint *xy) +{ + DrawPolyMarker(n, xy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw text on specified window + +void TVirtualX::DrawTextW(WinContext_t /* wctxt */, Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode) +{ + DrawText(x, y, angle, mgn, text, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw wtext on specified window + +void TVirtualX::DrawTextW(WinContext_t /* wctxt */, Int_t x, Int_t y, Float_t angle, Float_t mgn, const wchar_t *text, ETextMode mode) +{ + DrawText(x, y, angle, mgn, text, mode); +} + //////////////////////////////////////////////////////////////////////////////// /// Executes the command "code" coming from the other threads (Win32) diff --git a/core/clingutils/res/TClingUtils.h b/core/clingutils/res/TClingUtils.h index 650146571c703..37ede524c7920 100644 --- a/core/clingutils/res/TClingUtils.h +++ b/core/clingutils/res/TClingUtils.h @@ -203,6 +203,7 @@ class AnnotatedRecordDecl { bool fRequestOnlyTClass; int fRequestedVersionNumber; int fRequestedRNTupleSerializationMode; + std::string fRequestedRNTupleSoARecord; // clang-format on public: @@ -228,6 +229,7 @@ class AnnotatedRecordDecl { bool rRequestOnlyTClass, int rRequestedVersionNumber, int rRequestedRNTupleSerializationMode, + const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpret, const TNormalizedCtxt &normCtxt); @@ -240,6 +242,7 @@ class AnnotatedRecordDecl { bool rRequestOnlyTClass, int rRequestedVersionNumber, int rRequestedRNTupleSerializationMode, + const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpret, const TNormalizedCtxt &normCtxt); @@ -253,6 +256,7 @@ class AnnotatedRecordDecl { bool rRequestOnlyTClass, int rRequestedVersionNumber, int rRequestedRNTupleSerializationMode, + const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpret, const TNormalizedCtxt &normCtxt); @@ -267,6 +271,7 @@ class AnnotatedRecordDecl { bool rRequestOnlyTClass, int rRequestedVersionNumber, int rRequestedRNTupleSerializationMode, + const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpret, const TNormalizedCtxt &normCtxt); // clang-format on @@ -292,6 +297,7 @@ class AnnotatedRecordDecl { bool RequestOnlyTClass() const { return fRequestOnlyTClass; } int RequestedVersionNumber() const { return fRequestedVersionNumber; } int RequestedRNTupleSerializationMode() const { return fRequestedRNTupleSerializationMode; } + const std::string &RequestedRNTupleSoARecord() const { return fRequestedRNTupleSoARecord; } // clang-format on int RootFlag() const { // Return the request (streamerInfo, has_version, etc.) combined in a single diff --git a/core/clingutils/src/RStl.cxx b/core/clingutils/src/RStl.cxx index b14fe3f0ac440..7baad1ee29030 100644 --- a/core/clingutils/src/RStl.cxx +++ b/core/clingutils/src/RStl.cxx @@ -101,6 +101,7 @@ void ROOT::Internal::RStl::GenerateTClassFor(const clang::QualType &type, const false, -1, 0, + "", interp, normCtxt)); // clang-format on @@ -163,6 +164,7 @@ void ROOT::Internal::RStl::GenerateTClassFor(const char *requestedName, const cl false, -1, 0, + "", interp, normCtxt)); // clang-format on diff --git a/core/clingutils/src/TClingUtils.cxx b/core/clingutils/src/TClingUtils.cxx index ca92e458ffa34..34b16cb62d2dd 100644 --- a/core/clingutils/src/TClingUtils.cxx +++ b/core/clingutils/src/TClingUtils.cxx @@ -386,6 +386,7 @@ AnnotatedRecordDecl::AnnotatedRecordDecl(long index, bool rRequestOnlyTClass, int rRequestedVersionNumber, int rRequestedRNTupleSerializationMode, + const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt) : fRuleIndex(index), @@ -395,7 +396,8 @@ AnnotatedRecordDecl::AnnotatedRecordDecl(long index, fRequestNoInputOperator(rRequestNoInputOperator), fRequestOnlyTClass(rRequestOnlyTClass), fRequestedVersionNumber(rRequestedVersionNumber), - fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode) + fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode), + fRequestedRNTupleSoARecord(rRequestedRNTupleSoARecord) // clang-format on { TMetaUtils::GetNormalizedName(fNormalizedName, decl->getASTContext().getTypeDeclType(decl), interpreter,normCtxt); @@ -417,6 +419,7 @@ AnnotatedRecordDecl::AnnotatedRecordDecl(long index, bool rRequestOnlyTClass, int rRequestVersionNumber, int rRequestedRNTupleSerializationMode, + const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt) : fRuleIndex(index), @@ -427,7 +430,8 @@ AnnotatedRecordDecl::AnnotatedRecordDecl(long index, fRequestNoInputOperator(rRequestNoInputOperator), fRequestOnlyTClass(rRequestOnlyTClass), fRequestedVersionNumber(rRequestVersionNumber), - fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode) + fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode), + fRequestedRNTupleSoARecord(rRequestedRNTupleSoARecord) // clang-format on { // For comparison purposes. @@ -456,6 +460,7 @@ AnnotatedRecordDecl::AnnotatedRecordDecl(long index, bool rRequestOnlyTClass, int rRequestVersionNumber, int rRequestedRNTupleSerializationMode, + const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt) : fRuleIndex(index), @@ -466,7 +471,8 @@ AnnotatedRecordDecl::AnnotatedRecordDecl(long index, fRequestNoInputOperator(rRequestNoInputOperator), fRequestOnlyTClass(rRequestOnlyTClass), fRequestedVersionNumber(rRequestVersionNumber), - fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode) + fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode), + fRequestedRNTupleSoARecord(rRequestedRNTupleSoARecord) // clang-format on { // For comparison purposes. @@ -490,6 +496,7 @@ AnnotatedRecordDecl::AnnotatedRecordDecl(long index, bool rRequestOnlyTClass, int rRequestVersionNumber, int rRequestedRNTupleSerializationMode, + const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt) : fRuleIndex(index), @@ -500,7 +507,8 @@ AnnotatedRecordDecl::AnnotatedRecordDecl(long index, fRequestNoInputOperator(rRequestNoInputOperator), fRequestOnlyTClass(rRequestOnlyTClass), fRequestedVersionNumber(rRequestVersionNumber), - fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode) + fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode), + fRequestedRNTupleSoARecord(rRequestedRNTupleSoARecord) // clang-format on { // const clang::ClassTemplateSpecializationDecl *tmplt_specialization = llvm::dyn_cast (decl); @@ -1979,7 +1987,7 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, if (HasCustomStreamerMemberFunction(cl, decl, interp, normCtxt)) { rootflag = rootflag | TClassTable__kHasCustomStreamerMember; } - finalString << "isa_proxy, " << rootflag << "," << "\n" << " sizeof(" << csymbol << ") );" << "\n"; + finalString << "isa_proxy, " << rootflag << "," << "\n" << " sizeof(" << csymbol << "), alignof(" << csymbol << ") );" << "\n"; if (HasIOConstructor(decl, args, ctorTypes, interp)) { finalString << " instance.SetNew(&new_" << mappedname.c_str() << ");" << "\n"; if (args.size()==0 && NeedDestructor(decl, interp)) @@ -2041,6 +2049,10 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, // FIXME Workaround: for the moment we do not generate coll proxies with unique ptrs since // they imply copies and therefore do not compile. auto classNameForIO = TClassEdit::GetNameForIO(classname); + + finalString << " static_assert(alignof(" << csymbol << "::value_type) <= 4096,\n"; + finalString << " \"Class with alignment strictly greater than 4096 are currently not supported in CollectionProxy. \"\n"; + finalString << " \"Please report this case to the developers\");\n"; finalString << " instance.AdoptCollectionProxyInfo(TCollectionProxyInfo::Generate(TCollectionProxyInfo::" << methodTCP << "< " << classNameForIO.c_str() << " >()));" << "\n"; needCollectionProxy = true; @@ -2063,6 +2075,14 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, } + //--------------------------------------------------------------------------- + // Register underlying SoA record for RNTuple SoA layouts + ///////////////////////////////////////////////////////////////////////////// + + if (!cl.RequestedRNTupleSoARecord().empty()) { + finalString << " instance.SetRNTupleSoARecord(\"" << cl.RequestedRNTupleSoARecord() << "\");" << "\n"; + } + //--------------------------------------------------------------------------- // Pass the schema evolution rules to TGenericClassInfo ///////////////////////////////////////////////////////////////////////////// diff --git a/core/dictgen/res/ClassSelectionRule.h b/core/dictgen/res/ClassSelectionRule.h index b6c37b154bd15..0c42f1eac1c0a 100644 --- a/core/dictgen/res/ClassSelectionRule.h +++ b/core/dictgen/res/ClassSelectionRule.h @@ -47,6 +47,8 @@ class ClassSelectionRule final : public BaseSelectionRule int fRequestedVersionNumber; // Explicit request for a specific version number (default to no request with -1). // Explicit request for unsplit (-1) or split (=1), defaults to unset (=0) int fRequestedRNTupleSerializationMode = 0; + // If not empty, marks the class as an RNTuple SoA layout for the underlying given record class + std::string fRequestedRNTupleSoARecord; // clang-format on public: @@ -83,6 +85,7 @@ class ClassSelectionRule final : public BaseSelectionRule void SetRequestPrivate(bool val); void SetRequestedVersionNumber(int version); void SetRequestedRNTupleSerializationMode(int serializationMode); + void SetRequestedRNTupleSoARecord(const std::string &recordName); bool RequestOnlyTClass() const; // True if the user want the TClass intiliazer but *not* the interpreter meta data bool RequestNoStreamer() const; // Request no Streamer function in the dictionary @@ -92,6 +95,7 @@ class ClassSelectionRule final : public BaseSelectionRule bool RequestPrivate() const; int RequestedVersionNumber() const; int RequestedRNTupleSerializationMode() const; + const std::string &RequestedRNTupleSoARecord() const; }; #endif diff --git a/core/dictgen/src/ClassSelectionRule.cxx b/core/dictgen/src/ClassSelectionRule.cxx index fd0d7382c71ac..1d446c75efd9b 100644 --- a/core/dictgen/src/ClassSelectionRule.cxx +++ b/core/dictgen/src/ClassSelectionRule.cxx @@ -162,6 +162,11 @@ void ClassSelectionRule::SetRequestedRNTupleSerializationMode(int serializationM fRequestedRNTupleSerializationMode = serializationMode; } +void ClassSelectionRule::SetRequestedRNTupleSoARecord(const std::string &recordName) +{ + fRequestedRNTupleSoARecord = recordName; +} + bool ClassSelectionRule::RequestOnlyTClass() const { return fRequestOnlyTClass; @@ -186,3 +191,8 @@ int ClassSelectionRule::RequestedRNTupleSerializationMode() const { return fRequestedRNTupleSerializationMode; } + +const std::string &ClassSelectionRule::RequestedRNTupleSoARecord() const +{ + return fRequestedRNTupleSoARecord; +} diff --git a/core/dictgen/src/LinkdefReader.cxx b/core/dictgen/src/LinkdefReader.cxx index 0cb4ebdca6793..fe9a0fa01503a 100644 --- a/core/dictgen/src/LinkdefReader.cxx +++ b/core/dictgen/src/LinkdefReader.cxx @@ -27,6 +27,7 @@ #include #include +#include #include "LinkdefReader.h" #include "SelectionRules.h" #include "RConversionRuleParser.h" @@ -57,6 +58,7 @@ struct LinkdefReader::Options { }; int fVersionNumber; int fRNTupleSerializationMode; // 0: unset, -1: enforce streamed, 1: enforce native + std::string fRNTupleSoARecord; }; /* @@ -409,6 +411,7 @@ bool LinkdefReader::AddRule(const std::string& ruletype, if (options->fVersionNumber >= 0) csr.SetRequestedVersionNumber(options->fVersionNumber); if (options->fRNTupleSerializationMode != 0) csr.SetRequestedRNTupleSerializationMode(options->fRNTupleSerializationMode); + csr.SetRequestedRNTupleSoARecord(options->fRNTupleSoARecord); } if (csr.RequestStreamerInfo() && csr.RequestNoStreamer()) { std::cerr << "Warning: " << localIdentifier << " option + mutual exclusive with -, + prevails\n"; @@ -663,7 +666,8 @@ class LinkdefReaderPragmaHandler : public clang::PragmaHandler { * nomap: (ignored by roocling; prevents entry in ROOT's rootmap file) * stub: (ignored by rootcling was a directly for CINT code generation) * version(x): sets the version number of the class to x - * rntuple[un]split: enforce split/unsplit encoding in RNTuple + * rntupleStreamerMode(true|false): enforce/prevent use of the RNTuple streamer field + * rntupleSoARecord(x): marks the class as an RNTuple SoA layout for the underlying record class x */ // We assume that the first toke in option or options @@ -718,11 +722,36 @@ class LinkdefReaderPragmaHandler : public clang::PragmaHandler { "(either rntupleStreamerMode(true) or rntupleStreamerMode(false))", boolval, PP); } else { + if (!options.fRNTupleSoARecord.empty()) { + Error("Error: rntupleStreamerMode(true) and rntupleSoARecord are mutually exclusive", boolval, PP); + } options.fRNTupleSerializationMode = -1; } } else { Error("Error: Malformed rntupleStreamerMode option (either 'true' or 'false').", boolval, PP); } + } else if (tok.getIdentifierInfo()->getName() == "rntupleSoARecord") { + clang::Token start = tok; + PP.Lex(tok); + if (tok.isNot(clang::tok::l_paren)) { + Error("Error: missing left parenthesis after rntupleSoARecord.", start, PP); + return false; + } + PP.Lex(tok); + clang::Token strval = tok; + if (tok.isNot(clang::tok::eod)) + PP.Lex(tok); + if (tok.isNot(clang::tok::r_paren)) { + Error("Error: missing right parenthesis after rntupleSoARecord.", start, PP); + return false; + } + if (!strval.getIdentifierInfo() || strval.getIdentifierInfo()->getName().empty()) { + Error("Error: Malformed rntupleSoARecord option.", strval, PP); + } + if (options.fRNTupleSerializationMode == -1) { + Error("Error: rntupleStreamerMode(true) and rntupleSoARecord are mutually exclusive", strval, PP); + } + options.fRNTupleSoARecord = strval.getIdentifierInfo()->getName(); } else if (tok.getIdentifierInfo()->getName() == "stub") { // This was solely for CINT dictionary, ignore for now. // options.fUseStubs = 1; diff --git a/core/dictgen/src/Scanner.cxx b/core/dictgen/src/Scanner.cxx index 36e0a75802b31..facf26755cff4 100644 --- a/core/dictgen/src/Scanner.cxx +++ b/core/dictgen/src/Scanner.cxx @@ -563,6 +563,7 @@ int RScanner::AddAnnotatedRecordDecl(const ClassSelectionRule* selected, selected->RequestOnlyTClass(), selected->RequestedVersionNumber(), selected->RequestedRNTupleSerializationMode(), + selected->RequestedRNTupleSoARecord(), fInterpreter, fNormCtxt); } else { @@ -574,6 +575,7 @@ int RScanner::AddAnnotatedRecordDecl(const ClassSelectionRule* selected, selected->RequestOnlyTClass(), selected->RequestedVersionNumber(), selected->RequestedRNTupleSerializationMode(), + selected->RequestedRNTupleSoARecord(), fInterpreter, fNormCtxt); } diff --git a/core/dictgen/src/XMLReader.cxx b/core/dictgen/src/XMLReader.cxx index 50a546a520e7c..9d04f9d6a8c5a 100644 --- a/core/dictgen/src/XMLReader.cxx +++ b/core/dictgen/src/XMLReader.cxx @@ -961,6 +961,13 @@ bool XMLReader::Parse(const std::string &fileName, SelectionRules& out) "not both\n", lineNumCharp, iAttrValue.c_str()); } else { + if (!csr->RequestedRNTupleSoARecord().empty()) { + ROOT::TMetaUtils::Error( + nullptr, + "XML at line %s: class attributes rntupleStreamerMode and rntupleSoARecord " + "are mutually exclusive\n", + lineNumCharp, iAttrValue.c_str()); + } csr->SetRequestedRNTupleSerializationMode(-1); } } else { @@ -972,6 +979,18 @@ bool XMLReader::Parse(const std::string &fileName, SelectionRules& out) } } + // request RNTuple SoA record type name + if (tagKind == kClass && csr && "rntupleSoARecord" == iAttrName) { + if (csr->RequestedRNTupleSerializationMode() == -1) { + ROOT::TMetaUtils::Error( + nullptr, + "XML at line %s: class attributes rntupleStreamerMode and rntupleSoARecord " + "are mutually exclusive\n", + lineNumCharp, iAttrValue.c_str()); + } + csr->SetRequestedRNTupleSoARecord(iAttrValue); + } + // request no input operator if (tagKind == kClass && csr && "noInputOperator" == iAttrName){ if (iAttrValue == "true") { diff --git a/core/dictgen/src/rootcling_impl.cxx b/core/dictgen/src/rootcling_impl.cxx index 79707eb2d3901..4d2b03714a358 100644 --- a/core/dictgen/src/rootcling_impl.cxx +++ b/core/dictgen/src/rootcling_impl.cxx @@ -3930,7 +3930,7 @@ int RootClingMain(int argc, const char *etcDir = gDriverConfig->fTROOT__GetEtcDir(); std::string llvmResourceDir = etcDir ? std::string(etcDir) + "/cling" : ""; - + if (gBareClingSubcommand) { std::vector clingArgsC; clingArgsC.push_back(executableFileName); @@ -4106,7 +4106,7 @@ int RootClingMain(int argc, // cling-only arguments if (etcDir) clingArgs.push_back(std::string("-I") + llvm::sys::path::convert_to_slash(etcDir)); - + // We do not want __ROOTCLING__ in the pch! if (!gOptGeneratePCH) { clingArgs.push_back("-D__ROOTCLING__"); @@ -4616,7 +4616,7 @@ int RootClingMain(int argc, // interpPragmaSource and we still need to process it. LinkdefReader ldefr(interp, constructorTypes); - + if (!ldefr.Parse(selectionRules, interpPragmaSource, clingArgs, llvmResourceDir.c_str())) { ROOT::TMetaUtils::Error(nullptr, "Parsing #pragma failed %s\n", linkdefFilename.c_str()); @@ -5532,6 +5532,7 @@ int GenReflexMain(int argc, char **argv) " Default value is 'false'\n" " - rntupleStreamerMode [true/false]: enforce streamed or native writing for RNTuple.\n" " If unset, RNTuple stores classes in split mode or fails if the class cannot be split.\n" + " - rntupleSoARecord [class name]: marks the class as an RNTuple SoA layout for the underlying record\n" " - noInputOperator [true/false]: turns off input operator generation if set\n" " to 'true'. Default value is 'false'\n" " Example XML:\n" @@ -5542,6 +5543,7 @@ int GenReflexMain(int argc, char **argv) " [id=\"xxxx\"] [noStreamer=\"true/false\"]\n" " [noInputOperator=\"true/false\"]\n" " [rntupleStreamerMode=\"true/false\"] />\n" + " [rntupleSoARecord=\"class_name\"] />\n" " \n" " \n" " \n" diff --git a/core/gui/inc/GuiTypes.h b/core/gui/inc/GuiTypes.h index 41052a7a403de..ac522ea3702eb 100644 --- a/core/gui/inc/GuiTypes.h +++ b/core/gui/inc/GuiTypes.h @@ -27,6 +27,7 @@ typedef ULongptr_t Handle_t; ///< Generic resource handle typedef Handle_t Display_t; ///< Display handle typedef Handle_t Visual_t; ///< Visual handle typedef Handle_t Window_t; ///< Window handle +typedef Handle_t WinContext_t; ///< Window drawing context typedef Handle_t Pixmap_t; ///< Pixmap handle typedef Handle_t Drawable_t; ///< Drawable handle typedef Handle_t Region_t; ///< Region handle diff --git a/core/gui/inc/TCanvasImp.h b/core/gui/inc/TCanvasImp.h index 0d8f3205677a3..a411436451770 100644 --- a/core/gui/inc/TCanvasImp.h +++ b/core/gui/inc/TCanvasImp.h @@ -64,7 +64,8 @@ friend class TCanvas; w = h = 0; return 0; } - virtual void Iconify() {} + + virtual void Iconify() {} virtual Int_t InitWindow() { return 0; } virtual void SetStatusText(const char *text = nullptr, Int_t partidx = 0) { (void) text; (void) partidx; } virtual void SetWindowPosition(Int_t x, Int_t y) { (void) x; (void) y; } @@ -87,6 +88,12 @@ friend class TCanvas; virtual Bool_t HasToolBar() const { return kFALSE; } virtual Bool_t HasToolTips() const { return kFALSE; } + virtual void Warp(Int_t ix, Int_t iy); + virtual Int_t RequestLocator(Int_t &x, Int_t &y); + virtual void GetCanvasGeometry(Int_t wid, UInt_t &w, UInt_t &h); + virtual void ResizeCanvasWindow(Int_t wid); + virtual void UpdateDisplay(Int_t mode = 0, Bool_t sleep = kFALSE); + ClassDef(TCanvasImp,0) //ABC describing main window protocol }; diff --git a/core/gui/src/TCanvasImp.cxx b/core/gui/src/TCanvasImp.cxx index 639f34c50cb83..b43a721f6222d 100644 --- a/core/gui/src/TCanvasImp.cxx +++ b/core/gui/src/TCanvasImp.cxx @@ -17,4 +17,52 @@ and a drawing area). */ #include "TCanvasImp.h" +#include "TVirtualX.h" +#include "TSystem.h" + +//////////////////////////////////////////////////////////////////////////////// +/// Change mouse pointer, redirect to gVirtualX + +void TCanvasImp::Warp(Int_t ix, Int_t iy) +{ + if(gVirtualX) + gVirtualX->Warp(ix, iy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Request current mouse pointer, redirect to gVirtualX + +Int_t TCanvasImp::RequestLocator(Int_t &x, Int_t &y) +{ + return gVirtualX ? gVirtualX->RequestLocator(1, 1, x, y) : -1; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Gets the size and position of the canvas paint area. + +void TCanvasImp::GetCanvasGeometry(Int_t wid, UInt_t &w, UInt_t &h) +{ + Int_t x, y; + gVirtualX->GetGeometry(wid, x, y, w, h); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Resize canvas window, redirect to gVirtualX + +void TCanvasImp::ResizeCanvasWindow(Int_t wid) +{ + if (gVirtualX) + gVirtualX->ResizeWindow(wid); //resize canvas and off-screen buffer +} + +//////////////////////////////////////////////////////////////////////////////// +/// Update gVirtualX display, also optionally sleep to wait until operation finished + +void TCanvasImp::UpdateDisplay(Int_t mode, Bool_t sleep) +{ + if (gVirtualX) + gVirtualX->Update(mode); + if (sleep) + gSystem->Sleep(30); +} diff --git a/core/meta/inc/TClass.h b/core/meta/inc/TClass.h index d1e298cddcbe9..fec709736ea2b 100644 --- a/core/meta/inc/TClass.h +++ b/core/meta/inc/TClass.h @@ -248,6 +248,7 @@ friend class TStreamerInfo; //Wrapper around this class custom conversion Streamer member function. ClassConvStreamerFunc_t fConvStreamerFunc = nullptr; Int_t fSizeof = -1; //Sizeof the class. + std::size_t fAlignment = 0; //Alignment of the class (0 for unknown alignment) std::atomic fCanSplit = -1; /// enum EDataType { @@ -45,7 +46,8 @@ class TDataType : public TDictionary { private: TypedefInfo_t *fInfo; /// fReadRules; @@ -80,13 +82,13 @@ namespace ROOT { const char *declFileName, Int_t declFileLine, const std::type_info &info, const Internal::TInitBehavior *action, DictFuncPtr_t dictionary, - TVirtualIsAProxy *isa, Int_t pragmabits, Int_t sizof); + TVirtualIsAProxy *isa, Int_t pragmabits, Int_t sizof, std::size_t alignof_ = 0); TGenericClassInfo(const char *fullClassname, Int_t version, const char *declFileName, Int_t declFileLine, const std::type_info &info, const Internal::TInitBehavior *action, DictFuncPtr_t dictionary, - TVirtualIsAProxy *isa, Int_t pragmabits, Int_t sizof); + TVirtualIsAProxy *isa, Int_t pragmabits, Int_t sizof, std::size_t alignof_ = 0); TGenericClassInfo(const char *fullClassname, Int_t version, const char *declFileName, Int_t declFileLine, @@ -142,6 +144,7 @@ namespace ROOT { void SetStreamerFunc(ClassStreamerFunc_t); void SetConvStreamerFunc(ClassConvStreamerFunc_t); Short_t SetVersion(Short_t version); + void SetRNTupleSoARecord(const std::string &recordName); // protected: private: diff --git a/core/meta/inc/TInterpreter.h b/core/meta/inc/TInterpreter.h index 94980fdb0a7fc..436406f72eaf1 100644 --- a/core/meta/inc/TInterpreter.h +++ b/core/meta/inc/TInterpreter.h @@ -430,6 +430,7 @@ class TInterpreter : public TNamed { virtual void *ClassInfo_New(ClassInfo_t * /* info */, void * /* arena */) const {return nullptr;} virtual Long_t ClassInfo_Property(ClassInfo_t * /* info */) const {return 0;} virtual int ClassInfo_Size(ClassInfo_t * /* info */) const {return 0;} + virtual size_t ClassInfo_AlignOf(ClassInfo_t * /* info */) const {return 0;} virtual Longptr_t ClassInfo_Tagnum(ClassInfo_t * /* info */) const {return 0;} virtual const char *ClassInfo_FileName(ClassInfo_t * /* info */) const {return nullptr;} virtual const char *ClassInfo_FullName(ClassInfo_t * /* info */) const {return nullptr;} diff --git a/core/meta/inc/TVirtualStreamerInfo.h b/core/meta/inc/TVirtualStreamerInfo.h index 24144d85aecfa..9c227e39a0489 100644 --- a/core/meta/inc/TVirtualStreamerInfo.h +++ b/core/meta/inc/TVirtualStreamerInfo.h @@ -177,6 +177,7 @@ class TVirtualStreamerInfo : public TNamed { TVirtualStreamerInfo(); TVirtualStreamerInfo(TClass * /*cl*/); virtual ~TVirtualStreamerInfo(); + virtual size_t GetClassAlignment() const = 0; virtual void Build(Bool_t isTransient = kFALSE) = 0; virtual void BuildCheck(TFile *file = nullptr, Bool_t load = kTRUE) = 0; virtual void BuildEmulated(TFile *file) = 0; diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 137f09b0bcd8c..238ca5ea840bc 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -5753,6 +5753,37 @@ void TClass::SetCurrentStreamerInfo(TVirtualStreamerInfo *info) fCurrentInfo = info; } +//////////////////////////////////////////////////////////////////////////////// +/// Return the alignment requirement (in bytes) for objects of this class. +/// +/// Returns (size_t)-1 if the class info is invalid, 0 for a forward-declared +/// class, an enum, a namespace or or a class with no definition. For all other +/// cases the actual alignment obtained from the dictionary or the clang ASTRecordLayout, +/// or the StreamerInfo (in that order of priority) is returned. +/// +/// Returns `0` when the alignment cannot be determined. + +size_t TClass::GetClassAlignment() const +{ + if (fAlignment != 0) + return fAlignment; + if (fState < kEmulated || Property() & (kIsNamespace|kIsEnum)) + return 0; + if (HasInterpreterInfo()) { + return gCling->ClassInfo_AlignOf(GetClassInfo()); + } + if (fCollectionProxy) { + // If the collection proxy has a dictionary, it will have return earlier, + // so we know that the collection proxy is emulated. + if (fCollectionProxy->GetProperties() & TVirtualCollectionProxy::kIsEmulated) { + } else { + Warning("TClass::GetClassAlignment", "Cannot determine alignment for collection proxy of class %s, returning alignof(std::vector)", GetName()); + } + return alignof(std::vector); + } + return GetStreamerInfo()->GetClassAlignment(); +} + //////////////////////////////////////////////////////////////////////////////// /// Return size of object of this class. diff --git a/core/meta/src/TDataType.cxx b/core/meta/src/TDataType.cxx index 6215649b262f6..e91fd98fa2fd4 100644 --- a/core/meta/src/TDataType.cxx +++ b/core/meta/src/TDataType.cxx @@ -48,6 +48,7 @@ TDataType::TDataType(TypedefInfo_t *info) : TDictionary(), SetTitle("Builtin basic type"); fProperty = 0; fSize = 0; + fAlignOf = 0; fType = kNoType_t; } } @@ -72,6 +73,7 @@ TDataType::TDataType(const TDataType& dt) : TDictionary(dt), fInfo(gCling->TypedefInfo_FactoryCopy(dt.fInfo)), fSize(dt.fSize), + fAlignOf(dt.fAlignOf), fType(dt.fType), fProperty(dt.fProperty), fTrueName(dt.fTrueName), @@ -89,6 +91,7 @@ TDataType& TDataType::operator=(const TDataType& dt) gCling->TypedefInfo_Delete(fInfo); fInfo=gCling->TypedefInfo_FactoryCopy(dt.fInfo); fSize=dt.fSize; + fAlignOf=dt.fAlignOf; fType=dt.fType; fProperty=dt.fProperty; fTrueName=dt.fTrueName; @@ -301,69 +304,89 @@ void TDataType::SetType(const char *name) fTrueName = name; fType = kOther_t; fSize = 0; + fAlignOf = 0; if (name==nullptr) { return; } else if (!strcmp("unsigned int", name)) { fType = kUInt_t; fSize = sizeof(UInt_t); + fAlignOf = alignof(UInt_t); } else if (!strcmp("unsigned", name)) { fType = kUInt_t; fSize = sizeof(UInt_t); + fAlignOf = alignof(UInt_t); } else if (!strcmp("int", name)) { fType = kInt_t; fSize = sizeof(Int_t); + fAlignOf = alignof(Int_t); } else if (!strcmp("unsigned long", name)) { fType = kULong_t; fSize = sizeof(ULong_t); + fAlignOf = alignof(ULong_t); } else if (!strcmp("long", name)) { fType = kLong_t; fSize = sizeof(Long_t); + fAlignOf = alignof(Long_t); } else if (!strcmp("unsigned long long", name) || !strcmp("ULong64_t",name)) { fType = kULong64_t; fSize = sizeof(ULong64_t); + fAlignOf = alignof(ULong64_t); } else if (!strcmp("long long", name) || !strcmp("Long64_t",name)) { fType = kLong64_t; fSize = sizeof(Long64_t); + fAlignOf = alignof(Long64_t); } else if (!strcmp("unsigned short", name)) { fType = kUShort_t; fSize = sizeof(UShort_t); + fAlignOf = alignof(UShort_t); } else if (!strcmp("short", name)) { fType = kShort_t; fSize = sizeof(Short_t); + fAlignOf = alignof(Short_t); } else if (!strcmp("unsigned char", name)) { fType = kUChar_t; fSize = sizeof(UChar_t); + fAlignOf = alignof(UChar_t); } else if (!strcmp("char", name)) { fType = kChar_t; fSize = sizeof(Char_t); + fAlignOf = alignof(Char_t); } else if (!strcmp("bool", name)) { fType = kBool_t; fSize = sizeof(Bool_t); + fAlignOf = alignof(Bool_t); } else if (!strcmp("float", name)) { fType = kFloat_t; fSize = sizeof(Float_t); + fAlignOf = alignof(Float_t); } else if (!strcmp("double", name)) { fType = kDouble_t; fSize = sizeof(Double_t); + fAlignOf = alignof(Double_t); } else if (!strcmp("signed char", name)) { fType = kChar_t; // kDataTypeAliasSignedChar_t; fSize = sizeof(Char_t); + fAlignOf = alignof(Char_t); } else if (!strcmp("void", name)) { fType = kVoid_t; fSize = 0; + fAlignOf = 0; } if (!strcmp("Float16_t", fName.Data())) { fSize = sizeof(Float16_t); + fAlignOf = alignof(Float16_t); fType = kFloat16_t; } if (!strcmp("Double32_t", fName.Data())) { fSize = sizeof(Double32_t); + fAlignOf = alignof(Double32_t); fType = kDouble32_t; } if (!strcmp("char*",fName.Data())) { fType = kCharStar; + fAlignOf = alignof(char*); } // kCounter = 6, kBits = 15 } diff --git a/core/meta/src/TGenericClassInfo.cxx b/core/meta/src/TGenericClassInfo.cxx index c406b977a212e..39eb101db0a54 100644 --- a/core/meta/src/TGenericClassInfo.cxx +++ b/core/meta/src/TGenericClassInfo.cxx @@ -87,7 +87,7 @@ namespace Internal { const char *declFileName, Int_t declFileLine, const std::type_info &info, const Internal::TInitBehavior *action, DictFuncPtr_t dictionary, - TVirtualIsAProxy *isa, Int_t pragmabits, Int_t sizof) + TVirtualIsAProxy *isa, Int_t pragmabits, Int_t sizof, std::size_t alignof_) : fAction(action), fClass(nullptr), fClassName(fullClassname), fDeclFileName(declFileName), fDeclFileLine(declFileLine), fDictionary(dictionary), fInfo(info), @@ -95,7 +95,7 @@ namespace Internal { fIsA(isa), fVersion(1), fMerge(nullptr),fResetAfterMerge(nullptr),fNew(nullptr),fNewArray(nullptr),fDelete(nullptr),fDeleteArray(nullptr),fDestructor(nullptr), fDirAutoAdd(nullptr), fStreamer(nullptr), - fStreamerFunc(nullptr), fConvStreamerFunc(nullptr), fCollectionProxy(nullptr), fSizeof(sizof), fPragmaBits(pragmabits), + fStreamerFunc(nullptr), fConvStreamerFunc(nullptr), fCollectionProxy(nullptr), fSizeof(sizof), fAlignment(alignof_), fPragmaBits(pragmabits), fCollectionProxyInfo(nullptr), fCollectionStreamerInfo(nullptr) { // Constructor. @@ -107,7 +107,7 @@ namespace Internal { const char *declFileName, Int_t declFileLine, const std::type_info &info, const Internal::TInitBehavior *action, DictFuncPtr_t dictionary, - TVirtualIsAProxy *isa, Int_t pragmabits, Int_t sizof) + TVirtualIsAProxy *isa, Int_t pragmabits, Int_t sizof, std::size_t alignof_) : fAction(action), fClass(nullptr), fClassName(fullClassname), fDeclFileName(declFileName), fDeclFileLine(declFileLine), fDictionary(dictionary), fInfo(info), @@ -115,7 +115,7 @@ namespace Internal { fIsA(isa), fVersion(version), fMerge(nullptr),fResetAfterMerge(nullptr),fNew(nullptr),fNewArray(nullptr),fDelete(nullptr),fDeleteArray(nullptr),fDestructor(nullptr), fDirAutoAdd(nullptr), fStreamer(nullptr), - fStreamerFunc(nullptr), fConvStreamerFunc(nullptr), fCollectionProxy(nullptr), fSizeof(sizof), fPragmaBits(pragmabits), + fStreamerFunc(nullptr), fConvStreamerFunc(nullptr), fCollectionProxy(nullptr), fSizeof(sizof), fAlignment(alignof_), fPragmaBits(pragmabits), fCollectionProxyInfo(nullptr), fCollectionStreamerInfo(nullptr) { @@ -137,7 +137,7 @@ namespace Internal { fIsA(nullptr), fVersion(version), fMerge(nullptr),fResetAfterMerge(nullptr),fNew(nullptr),fNewArray(nullptr),fDelete(nullptr),fDeleteArray(nullptr),fDestructor(nullptr), fDirAutoAdd(nullptr), fStreamer(nullptr), - fStreamerFunc(nullptr), fConvStreamerFunc(nullptr), fCollectionProxy(nullptr), fSizeof(0), fPragmaBits(pragmabits), + fStreamerFunc(nullptr), fConvStreamerFunc(nullptr), fCollectionProxy(nullptr), fSizeof(0), fAlignment(0), fPragmaBits(pragmabits), fCollectionProxyInfo(nullptr), fCollectionStreamerInfo(nullptr) { @@ -295,6 +295,7 @@ namespace Internal { } } fClass->SetClassSize(fSizeof); + fClass->SetClassAlignment(fAlignment); //--------------------------------------------------------------------- // Attach the schema evolution information @@ -314,6 +315,11 @@ namespace Internal { fClass->GetAttributeMap()->AddProperty("rntuple.streamerMode", "true"); } } + + if (!fRNTupleSoARecord.empty()) { + fClass->CreateAttributeMap(); + fClass->GetAttributeMap()->AddProperty("rntuple.SoARecord", fRNTupleSoARecord.c_str()); + } } return fClass; } @@ -433,6 +439,11 @@ namespace Internal { return version; } + void TGenericClassInfo::SetRNTupleSoARecord(const std::string &recordName) + { + fRNTupleSoARecord = recordName; + } + void TGenericClassInfo::AdoptAlternate(ROOT::TClassAlt *alt) { fAlternate.push_back(alt); diff --git a/core/meta/src/TStreamerElement.cxx b/core/meta/src/TStreamerElement.cxx index 561fca629178d..f4a60b19b08f6 100644 --- a/core/meta/src/TStreamerElement.cxx +++ b/core/meta/src/TStreamerElement.cxx @@ -307,7 +307,17 @@ Int_t TStreamerElement::GetExecID() const // inheriting from it (see ROOT-7052) const TString clName = ExtractClassName(fTypeName); const auto cl = TClass::GetClass(clName, kFALSE, kTRUE); - if (!cl || (nullptr == cl->GetBaseClass("TRef") && nullptr == cl->GetBaseClass("TRefArray"))) + // FIXME: The check for HasDataMemberInfo() is likely wrong because it could miss inheritance (chains) from TRef. + // However, it is needed to protect against non-loaded classes, previously also present in TClass::GetBaseClass. + if (!cl || !cl->HasDataMemberInfo()) + return 0; + // Classes cannot both inherit from TRef/TRefArray and have a collection proxy. + if (cl->GetCollectionProxy()) + return 0; + // Only classes inheriting from TObject can inherit from TRef. Do not look inside other classes. + if (!cl->IsTObject()) + return 0; + if (!cl->InheritsFrom("TRef") && !cl->InheritsFrom("TRefArray")) return 0; } diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 2184303c5fe51..bc77f94916034 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -8260,6 +8260,14 @@ std::string TCling::CallFunc_GetWrapperCode(CallFunc_t *func) const // ClassInfo interface // +//////////////////////////////////////////////////////////////////////////////// + +size_t TCling::ClassInfo_AlignOf(ClassInfo_t* cinfo) const +{ + TClingClassInfo* TClinginfo = (TClingClassInfo*) cinfo; + return TClinginfo->GetAlignOf(); +} + //////////////////////////////////////////////////////////////////////////////// /// Return true if the entity pointed to by 'declid' is declared in /// the context described by 'info'. If info is null, look into the diff --git a/core/metacling/src/TCling.h b/core/metacling/src/TCling.h index 36b1bce8812e1..66e32f6502516 100644 --- a/core/metacling/src/TCling.h +++ b/core/metacling/src/TCling.h @@ -447,6 +447,7 @@ class TCling final : public TInterpreter { void* ClassInfo_New(ClassInfo_t* info, void* arena) const final; Long_t ClassInfo_Property(ClassInfo_t* info) const final; int ClassInfo_Size(ClassInfo_t* info) const final; + size_t ClassInfo_AlignOf(ClassInfo_t* info) const final; Longptr_t ClassInfo_Tagnum(ClassInfo_t* info) const final; const char* ClassInfo_FileName(ClassInfo_t* info) const final; const char* ClassInfo_FullName(ClassInfo_t* info) const final; diff --git a/core/metacling/src/TClingClassInfo.cxx b/core/metacling/src/TClingClassInfo.cxx index 005c17ffcaeaa..6ca2d2a3a4506 100644 --- a/core/metacling/src/TClingClassInfo.cxx +++ b/core/metacling/src/TClingClassInfo.cxx @@ -1317,6 +1317,12 @@ int TClingClassInfo::RootFlag() const return 0; } +/// Return the size of the class in bytes as reported by clang. +/// +/// Returns -1 if the class info is invalid, 0 for a forward-declared class, +/// an enum, or a class with no definition, and 1 for a namespace (a special +/// value inherited from CINT). For all other cases the actual byte size +/// obtained from the clang ASTRecordLayout is returned. int TClingClassInfo::Size() const { if (!IsValid()) { @@ -1355,6 +1361,47 @@ int TClingClassInfo::Size() const return clang_size; } +/// Return the alignment of the class in bytes as reported by clang. +/// +/// Returns (size_t)-1 if the class info is invalid, 0 for a forward-declared +/// class, an enum, a namespace or or a class with no definition. For all other +/// cases the actual alignment obtained from the clang ASTRecordLayout is +/// returned. +size_t TClingClassInfo::GetAlignOf() const +{ + if (!IsValid()) { + return -1; + } + if (!GetDecl()) { + // A forward declared class. + return 0; + } + + R__LOCKGUARD(gInterpreterMutex); + + Decl::Kind DK = GetDecl()->getKind(); + if (DK == Decl::Namespace) { + return 0; + } + else if (DK == Decl::Enum) { + return 0; + } + const RecordDecl *RD = llvm::dyn_cast(GetDecl()); + if (!RD) { + return -1; + } + if (!RD->getDefinition()) { + // Forward-declared class. + return 0; + } + ASTContext &Context = GetDecl()->getASTContext(); + cling::Interpreter::PushTransactionRAII RAII(fInterp); + const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); + auto align = Layout.getAlignment().getQuantity(); + assert(align > 0); + return align; +} + Longptr_t TClingClassInfo::Tagnum() const { if (!IsValid()) { diff --git a/core/metacling/src/TClingClassInfo.h b/core/metacling/src/TClingClassInfo.h index c08fa1bdae1c9..a4a9e96e0d0f3 100644 --- a/core/metacling/src/TClingClassInfo.h +++ b/core/metacling/src/TClingClassInfo.h @@ -181,6 +181,7 @@ class TClingClassInfo final : public TClingDeclInfo { long Property() const; int RootFlag() const; int Size() const; + size_t GetAlignOf() const; Longptr_t Tagnum() const; const char *FileName(); void FullName(std::string &output, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const; diff --git a/documentation/doxygen/htmlheader.html b/documentation/doxygen/htmlheader.html index 3d7c3a15f41c0..7c995cb74e4f6 100644 --- a/documentation/doxygen/htmlheader.html +++ b/documentation/doxygen/htmlheader.html @@ -1,5 +1,5 @@ - + diff --git a/documentation/doxygen/makeinput.sh b/documentation/doxygen/makeinput.sh index 607672652e981..9cd7cf1697eee 100755 --- a/documentation/doxygen/makeinput.sh +++ b/documentation/doxygen/makeinput.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # This script creates the file Doxyfile_INPUT which defines the list of directories to be # analysed by doxygen. To only build a subset of the documentation it is enough to comment diff --git a/documentation/doxygen/modifyNamespacesWebpage.sh b/documentation/doxygen/modifyNamespacesWebpage.sh index ac6ecabcce5f3..606ee623ddf78 100755 --- a/documentation/doxygen/modifyNamespacesWebpage.sh +++ b/documentation/doxygen/modifyNamespacesWebpage.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # The Python tutorials appear in the Namespaces page. # This script removes the namespace given as input from all the files. diff --git a/graf2d/asimage/src/TASImage.cxx b/graf2d/asimage/src/TASImage.cxx index 6ea60a788019f..b35bcb14f81c1 100644 --- a/graf2d/asimage/src/TASImage.cxx +++ b/graf2d/asimage/src/TASImage.cxx @@ -1534,7 +1534,9 @@ void TASImage::Paint(Option_t *option) int tox = expand ? 0 : int(gPad->UtoPixel(1.) * gPad->GetLeftMargin()); int toy = expand ? 0 : int(gPad->VtoPixel(0.) * gPad->GetTopMargin()); - if (!gROOT->IsBatch()) { + auto ps = gPad->GetPainter()->GetPS(); + + if (!ps) { Window_t wid = (Window_t)gVirtualX->GetWindowID(gPad->GetPixmapID()); Image2Drawable(fScaledImage ? fScaledImage->fImage : fImage, wid, tox, toy); @@ -1559,15 +1561,13 @@ void TASImage::Paint(Option_t *option) pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1), min, max, ndiv, "+L"); } - } - - // loop over pixmap and draw image to PostScript - if (gVirtualPS) { + } else { + // loop over pixmap and draw image to PostScript Bool_t paint_as_png = kFALSE; - if (gVirtualPS->InheritsFrom("TImageDump")) { // PostScript is asimage - TImage *dump = (TImage *)gVirtualPS->GetStream(); + if (ps->InheritsFrom("TImageDump")) { // PostScript is asimage + TImage *dump = (TImage *)ps->GetStream(); if (!dump) return; dump->Merge(fScaledImage ? fScaledImage : this, "alphablend", gPad->XtoAbsPixel(0), gPad->YtoAbsPixel(1)); @@ -1589,15 +1589,15 @@ void TASImage::Paint(Option_t *option) min, max, ndiv, "+L"); } return; - } else if (gVirtualPS->InheritsFrom("TPDF")) { + } else if (ps->InheritsFrom("TPDF")) { Warning("Paint", "PDF not implemented yet"); return; - } else if (gVirtualPS->InheritsFrom("TSVG")) { + } else if (ps->InheritsFrom("TSVG")) { paint_as_png = kTRUE; } - Double_t dx = gPad->GetX2()-gPad->GetX1(); - Double_t dy = gPad->GetY2()-gPad->GetY1(); + Double_t dx = gPad->GetX2() - gPad->GetX1(); + Double_t dy = gPad->GetY2() - gPad->GetY1(); Double_t x1, x2, y1, y2; if (expand) { @@ -1613,10 +1613,10 @@ void TASImage::Paint(Option_t *option) } // get special color cell to be reused during image printing - gVirtualPS->SetFillColor(TColor::GetColor((Float_t) 1., (Float_t) 1., (Float_t) 1.)); - gVirtualPS->SetFillStyle(1001); + ps->SetFillColor(TColor::GetColor((Float_t) 1., (Float_t) 1., (Float_t) 1.)); + ps->SetFillStyle(1001); - gVirtualPS->CellArrayBegin(image->width, image->height, x1, x2, y1, y2); + ps->CellArrayBegin(image->width, image->height, x1, x2, y1, y2); if (paint_as_png) { char *buffer = nullptr; @@ -1628,7 +1628,7 @@ void TASImage::Paint(Option_t *option) if (!params.png.compression) params.png.compression = -1; if (ASImage2PNGBuff(image, (CARD8 **)&buffer, &size, ¶ms)) { - gVirtualPS->CellArrayPng(buffer, size); + ps->CellArrayPng(buffer, size); free(buffer); } } else { @@ -1638,13 +1638,13 @@ void TASImage::Paint(Option_t *option) for (Int_t yt = 0; yt < (Int_t)image->height; yt++) { imdec->decode_image_scanline(imdec); for (Int_t xt = 0; xt < (Int_t)image->width; xt++) - gVirtualPS->CellArrayFill(imdec->buffer.red[xt], - imdec->buffer.green[xt], - imdec->buffer.blue[xt]); + ps->CellArrayFill(imdec->buffer.red[xt], + imdec->buffer.green[xt], + imdec->buffer.blue[xt]); } stop_image_decoding(&imdec); } - gVirtualPS->CellArrayEnd(); + ps->CellArrayEnd(); // print the color bar if (grad_im) { @@ -1654,8 +1654,8 @@ void TASImage::Paint(Option_t *option) x2 = x1 + xconv; y2 = gPad->AbsPixeltoY(pal_Ay); y1 = y2 - yconv; - gVirtualPS->CellArrayBegin(grad_im->width, grad_im->height, - x1, x2, y1, y2); + ps->CellArrayBegin(grad_im->width, grad_im->height, + x1, x2, y1, y2); if (paint_as_png) { char *buffer = nullptr; @@ -1668,7 +1668,7 @@ void TASImage::Paint(Option_t *option) params.png.compression = -1; if (ASImage2PNGBuff(grad_im, (CARD8 **)&buffer, &size, ¶ms)) { - gVirtualPS->CellArrayPng(buffer, size); + ps->CellArrayPng(buffer, size); free(buffer); } } else { @@ -1678,13 +1678,13 @@ void TASImage::Paint(Option_t *option) for (Int_t yt = 0; yt < (Int_t)grad_im->height; yt++) { imdec->decode_image_scanline(imdec); for (Int_t xt = 0; xt < (Int_t)grad_im->width; xt++) - gVirtualPS->CellArrayFill(imdec->buffer.red[xt], - imdec->buffer.green[xt], - imdec->buffer.blue[xt]); + ps->CellArrayFill(imdec->buffer.red[xt], + imdec->buffer.green[xt], + imdec->buffer.blue[xt]); } stop_image_decoding(&imdec); } - gVirtualPS->CellArrayEnd(); + ps->CellArrayEnd(); // values of palette TGaxis axis; @@ -1693,6 +1693,7 @@ void TASImage::Paint(Option_t *option) double max = fMaxValue; axis.SetLineColor(1); // draw black ticks Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w); + // TODO: provide PaintAxisOn method axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h), pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1), min, max, ndiv, "+L"); diff --git a/graf2d/asimage/src/libAfterImage/export.c b/graf2d/asimage/src/libAfterImage/export.c index 099dbe61f9238..9c9de56264d3c 100644 --- a/graf2d/asimage/src/libAfterImage/export.c +++ b/graf2d/asimage/src/libAfterImage/export.c @@ -1120,6 +1120,7 @@ Bool ASImage2gif( ASImage *im, const char *path, ASImageExportParams *params ) if (outfile) { #if (GIFLIB_MAJOR>=5) + errcode=E_GIF_SUCCEEDED; gif = EGifOpenFileHandle(fileno(outfile), &errcode); if (errcode != E_GIF_SUCCEEDED) ASIM_PrintGifError(errcode); diff --git a/graf2d/asimage/src/libAfterImage/win32/config.h b/graf2d/asimage/src/libAfterImage/win32/config.h index f1a288c8a83f5..698e7f88030fa 100644 --- a/graf2d/asimage/src/libAfterImage/win32/config.h +++ b/graf2d/asimage/src/libAfterImage/win32/config.h @@ -28,6 +28,9 @@ typedef unsigned char boolean; /* Define to 1 if you have the header file. */ #define HAVE_FT2BUILD_H 1 +/* Define if libgif is available */ +#define HAVE_GIF + #if _MSC_VER >= 1400 #define NO_DOUBLE_FCLOSE_AFTER_FDOPEN #else diff --git a/graf2d/cocoa/inc/QuartzPixmap.h b/graf2d/cocoa/inc/QuartzPixmap.h index dd6f3f526c413..46cc9ae4d51a8 100644 --- a/graf2d/cocoa/inc/QuartzPixmap.h +++ b/graf2d/cocoa/inc/QuartzPixmap.h @@ -41,6 +41,13 @@ ROOT::MacOSX::Util::CFScopeGuard fContext; CGFloat fScaleFactor; + + TAttLine fAttLine; ///< current line attributes + TAttFill fAttFill; ///< current fill attributes + TAttMarker fAttMarker; ///< current marker attribute + TAttText fAttText; ///< current text attribute + + BOOL fDirectDraw; } - (id) initWithW : (unsigned) width H : (unsigned) height scaleFactor : (CGFloat) scaleFactor; @@ -62,6 +69,16 @@ - (unsigned) fWidth; - (unsigned) fHeight; +//Graphical attributes +@property (nonatomic, readonly) TAttLine *attLine; +@property (nonatomic, readonly) TAttFill *attFill; +@property (nonatomic, readonly) TAttMarker *attMarker; +@property (nonatomic, readonly) TAttText *attText; + +- (void) setDirectDraw : (BOOL) mode; +- (BOOL) isDirectDraw; + + - (void) copy : (NSObject *) src area : (ROOT::MacOSX::X11::Rectangle) area withMask : (QuartzImage *) mask clipOrigin : (ROOT::MacOSX::X11::Point) origin toPoint : (ROOT::MacOSX::X11::Point) dstPoint; @@ -93,6 +110,11 @@ unsigned fWidth; unsigned fHeight; + TAttLine fAttLine; ///< current line attributes + TAttFill fAttFill; ///< current fill attributes + TAttMarker fAttMarker; ///< current marker attribute + TAttText fAttText; ///< current text attribute + ROOT::MacOSX::Util::CFScopeGuard fImage; std::vector fImageData; } @@ -118,6 +140,12 @@ - (unsigned char *) readColorBits : (ROOT::MacOSX::X11::Rectangle) area; +//Graphical attributes +@property (nonatomic, readonly) TAttLine *attLine; +@property (nonatomic, readonly) TAttFill *attFill; +@property (nonatomic, readonly) TAttMarker *attMarker; +@property (nonatomic, readonly) TAttText *attText; + @end diff --git a/graf2d/cocoa/inc/QuartzWindow.h b/graf2d/cocoa/inc/QuartzWindow.h index 7d7e6aba368d2..79c5c3c0d999e 100644 --- a/graf2d/cocoa/inc/QuartzWindow.h +++ b/graf2d/cocoa/inc/QuartzWindow.h @@ -42,7 +42,7 @@ class Command; // // //////////////////////////////////////////////////////////////////////// @interface XorDrawingView: NSView -- (void) setXorOperations : (const std::vector &) primitives; +- (void) addXorCommand : (ROOT::MacOSX::X11::Command *) cmd; @end // XorDrawingWindow is a special window: a transparent @@ -68,11 +68,12 @@ class Command; @private QuartzWindow *fMainWindow; BOOL fHasFocus; - + QuartzView *fContentView; BOOL fDelayedTransient; QuartzImage *fShapeCombineMask; BOOL fIsDeleted; + TVirtualX::EDrawMode fDrawMode; // current draw mode } - (id) initWithContentRect : (NSRect) contentRect styleMask : (NSUInteger) windowStyle @@ -127,11 +128,15 @@ class Command; - (unsigned char *) readColorBits : (ROOT::MacOSX::X11::Rectangle) area; // Trick for crosshair drawing in TCanvas ("pseudo-XOR") -- (void) addXorWindow; -- (void) adjustXorWindowGeometry; +- (XorDrawingWindow *) addXorWindow; +- (XorDrawingWindow *) findXorWindow; - (void) adjustXorWindowGeometry : (XorDrawingWindow *) win; - (void) removeXorWindow; -- (XorDrawingWindow *) findXorWindow; +- (void) addXorLine : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 : (Int_t) y2; +- (void) addXorBox : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 : (Int_t) y2; +- (void) setDrawMode : (TVirtualX::EDrawMode) newMode; +- (TVirtualX::EDrawMode) getDrawMode; + //X11Window protocol. @@ -203,6 +208,13 @@ class Command; unsigned long fBackgroundPixel; BOOL fOverrideRedirect; + TAttLine fAttLine; ///< current line attributes + TAttFill fAttFill; ///< current fill attributes + TAttMarker fAttMarker; ///< current marker attribute + TAttText fAttText; ///< current text attribute + + BOOL fDirectDraw; + BOOL fHasFocus; QuartzView *fParentView; @@ -238,6 +250,18 @@ class Command; - (BOOL) fIsOpenGLWidget; - (CGFloat) fScaleFactor; +//Graphical attributes +@property (nonatomic, readonly) TAttLine *attLine; +@property (nonatomic, readonly) TAttFill *attFill; +@property (nonatomic, readonly) TAttMarker *attMarker; +@property (nonatomic, readonly) TAttText *attText; + +- (void) setDrawMode : (TVirtualX::EDrawMode) newMode; +- (TVirtualX::EDrawMode) getDrawMode; + +- (void) setDirectDraw : (BOOL) mode; +- (BOOL) isDirectDraw; + @property (nonatomic, assign) CGContextRef fContext; //Geometry. diff --git a/graf2d/cocoa/inc/TGCocoa.h b/graf2d/cocoa/inc/TGCocoa.h index 0ec9a188991c5..28f67306a3386 100644 --- a/graf2d/cocoa/inc/TGCocoa.h +++ b/graf2d/cocoa/inc/TGCocoa.h @@ -103,6 +103,13 @@ class TGCocoa : public TVirtualX { Int_t AddWindow(ULong_t qwid, UInt_t w, UInt_t h) override; //-"Qt ROOT". void RemoveWindow(ULong_t qwid) override; //-"Qt ROOT". + //---- Methods used for new graphics ----- + WinContext_t GetWindowContext(Int_t wid) override; + WinContext_t GetSelectedContext(); + void SetDrawModeW(WinContext_t wctxt, EDrawMode mode) override; + EDrawMode GetDrawModeW(WinContext_t wctxt) override; + void ClearWindowW(WinContext_t wctxt) override; + void UpdateWindowW(WinContext_t wctxt, Int_t mode) override; //-Functions used by GUI. Window_t CreateWindow(Window_t parent, Int_t x, Int_t y, diff --git a/graf2d/cocoa/inc/TGQuartz.h b/graf2d/cocoa/inc/TGQuartz.h index 25ca55bdd6276..8c9e8603c557c 100644 --- a/graf2d/cocoa/inc/TGQuartz.h +++ b/graf2d/cocoa/inc/TGQuartz.h @@ -27,9 +27,6 @@ MacOS X, using CoreGraphics (Quartz). class TGQuartz : public TGCocoa { private: - enum EAlign {kNone, kTLeft, kTCenter, kTRight, kMLeft, - kMCenter, kMRight, kBLeft, kBCenter, kBRight}; - FT_Vector fAlign; // alignment vector public: TGQuartz(); @@ -78,6 +75,22 @@ class TGQuartz : public TGCocoa { Int_t GetFontDescent(const char *text) const override; Float_t GetTextMagnitude() override; + //---- Methods used for new graphics ----- + void SetAttFill(WinContext_t wctxt, const TAttFill &att) override; + void SetAttLine(WinContext_t wctxt, const TAttLine &att) override; + void SetAttMarker(WinContext_t wctxt, const TAttMarker &att) override; + void SetAttText(WinContext_t wctxt, const TAttText &att) override; + + void DrawBoxW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) override; + void DrawFillAreaW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawLineW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2) override; + void DrawPolyLineW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawLinesSegmentsW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawPolyMarkerW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode) override; + void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, const wchar_t *text, ETextMode mode) override; + + private: //Unfortunately, I have to convert from @@ -93,13 +106,17 @@ class TGQuartz : public TGCocoa { bool fUseAA; bool fUseFAAA; - void AlignTTFString(); - Bool_t IsTTFStringVisible(Int_t x, Int_t y, UInt_t w, UInt_t h); - void RenderTTFString(Int_t x, Int_t y, ETextMode mode); + void AlignTTFString(WinContext_t wctxt); + Bool_t IsTTFStringVisible(WinContext_t wctxt, Int_t x, Int_t y, UInt_t w, UInt_t h); + void RenderTTFString(WinContext_t wctxt, Int_t x, Int_t y, ETextMode mode); void DrawFTGlyphIntoPixmap(void *pixmap, FT_Bitmap *source, ULong_t fore, ULong_t back, Int_t bx, Int_t by); void SetAA(); - void *GetSelectedDrawableChecked(const char *calledFrom) const; + TAttFill &GetAttFill(WinContext_t wctxt); + TAttLine &GetAttLine(WinContext_t wctxt); + TAttMarker &GetAttMarker(WinContext_t wctxt); + TAttText &GetAttText(WinContext_t wctxt); + void *GetPixmapDrawable(void *drawable0, const char *calledFrom) const; TGQuartz(const TGQuartz &rhs); TGQuartz &operator = (const TGQuartz &rhs); diff --git a/graf2d/cocoa/inc/X11Buffer.h b/graf2d/cocoa/inc/X11Buffer.h index 635a017102083..75c52cc3b7bde 100644 --- a/graf2d/cocoa/inc/X11Buffer.h +++ b/graf2d/cocoa/inc/X11Buffer.h @@ -245,8 +245,6 @@ class CommandBuffer { std::vector fCommands; std::vector fViewBranch; - - std::vector fXorOps; public: typedef std::vector::size_type size_type; @@ -264,22 +262,15 @@ class CommandBuffer { void AddUpdateWindow(QuartzView *view); void AddDeletePixmap(Pixmap_t pixmap); - //'XOR' graphics for canvas. - void AddDrawBoxXor(Window_t windowID, Int_t x1, Int_t y1, Int_t x2, Int_t y2); - void AddDrawLineXor(Window_t windowID, Int_t x1, Int_t y1, Int_t x2, Int_t y2); - void Flush(Details::CocoaPrivate *impl); - void FlushXOROps(Details::CocoaPrivate *impl); void RemoveOperationsForDrawable(Drawable_t wid); void RemoveGraphicsOperationsForWindow(Window_t wid); - void RemoveXORGraphicsOperationsForWindow(Window_t wid); size_type BufferSize()const { return fCommands.size(); } - void ClearXOROperations(); private: void ClearCommands(); //Clip related stuff. diff --git a/graf2d/cocoa/inc/X11Drawable.h b/graf2d/cocoa/inc/X11Drawable.h index ee969ef271033..63997bea1f94f 100644 --- a/graf2d/cocoa/inc/X11Drawable.h +++ b/graf2d/cocoa/inc/X11Drawable.h @@ -53,6 +53,18 @@ - (unsigned) fWidth; - (unsigned) fHeight; +//Graphical attributes +@property (nonatomic, readonly) TAttLine *attLine; +@property (nonatomic, readonly) TAttFill *attFill; +@property (nonatomic, readonly) TAttMarker *attMarker; +@property (nonatomic, readonly) TAttText *attText; + +// new graphics methods +- (void) setDrawMode : (TVirtualX::EDrawMode) newMode; +- (TVirtualX::EDrawMode) getDrawMode; +- (void) setDirectDraw : (BOOL) mode; +- (BOOL) isDirectDraw; + //Functions to copy one drawable into another. - (void) copy : (NSObject *) src area : (ROOT::MacOSX::X11::Rectangle) area withMask : (QuartzImage *)mask clipOrigin : (ROOT::MacOSX::X11::Point) origin toPoint : (ROOT::MacOSX::X11::Point) dstPoint; diff --git a/graf2d/cocoa/src/QuartzPixmap.mm b/graf2d/cocoa/src/QuartzPixmap.mm index ce26cb7df1642..91e756e14e7c1 100644 --- a/graf2d/cocoa/src/QuartzPixmap.mm +++ b/graf2d/cocoa/src/QuartzPixmap.mm @@ -39,6 +39,7 @@ - (id) initWithW : (unsigned) width H : (unsigned) height scaleFactor : (CGFloat if (self = [super init]) { fWidth = 0; fHeight = 0; + fDirectDraw = NO; if (![self resizeW : width H : height scaleFactor : scaleFactor]) { [self release]; @@ -187,6 +188,42 @@ - (unsigned) fHeight return fHeight; } +//______________________________________________________________________________ +- (TAttLine *) attLine +{ + return &fAttLine; +} + +//______________________________________________________________________________ +- (TAttFill *) attFill +{ + return &fAttFill; +} + +//______________________________________________________________________________ +- (TAttMarker *) attMarker +{ + return &fAttMarker; +} + +//______________________________________________________________________________ +- (TAttText *) attText +{ + return &fAttText; +} + +//______________________________________________________________________________ +- (void) setDirectDraw : (BOOL) mode +{ + fDirectDraw = mode; +} + +//______________________________________________________________________________ +- (BOOL) isDirectDraw +{ + return fDirectDraw; +} + //______________________________________________________________________________ - (void) copyImage : (QuartzImage *) srcImage area : (X11::Rectangle) area withMask : (QuartzImage *) mask clipOrigin : (X11::Point) clipXY toPoint : (X11::Point) dstPoint @@ -410,6 +447,7 @@ - (void) addPixel : (const unsigned char *) rgb @end + @implementation QuartzImage @synthesize fIsStippleMask; @@ -758,6 +796,30 @@ - (unsigned) fHeight return fHeight; } +//______________________________________________________________________________ +- (TAttLine *) attLine +{ + return &fAttLine; +} + +//______________________________________________________________________________ +- (TAttFill *) attFill +{ + return &fAttFill; +} + +//______________________________________________________________________________ +- (TAttMarker *) attMarker +{ + return &fAttMarker; +} + +//______________________________________________________________________________ +- (TAttText *) attText +{ + return &fAttText; +} + //______________________________________________________________________________ - (CGImageRef) fImage { diff --git a/graf2d/cocoa/src/QuartzWindow.mm b/graf2d/cocoa/src/QuartzWindow.mm index 7a137011bc9aa..46a260a6fde8e 100644 --- a/graf2d/cocoa/src/QuartzWindow.mm +++ b/graf2d/cocoa/src/QuartzWindow.mm @@ -1127,9 +1127,9 @@ @implementation XorDrawingView std::vector xorOps; } -- (void) setXorOperations : (const std::vector &) primitives +- (void) addXorCommand : (ROOT::MacOSX::X11::Command *) cmd { - xorOps = primitives; + xorOps.push_back(cmd); } - (void) drawRect : (NSRect) dirtyRect @@ -1224,6 +1224,7 @@ - (id) initWithContentRect : (NSRect) contentRect styleMask : (NSUInteger) windo fIsDeleted = NO; fHasFocus = NO; + fDrawMode = TVirtualX::kCopy; } return self; @@ -1254,6 +1255,7 @@ - (id) initWithGLView : (ROOTOpenGLView *) glView fDelayedTransient = NO; fIsDeleted = NO; fHasFocus = NO; + fDrawMode = TVirtualX::kCopy; } return self; @@ -1506,22 +1508,18 @@ - (unsigned char *) readColorBits : (X11::Rectangle) area #pragma mark - XorDrawinWindow/View //______________________________________________________________________________ -- (void) addXorWindow +- (XorDrawingWindow *) addXorWindow { - if ([self findXorWindow]) - return; - - XorDrawingWindow *special = [[XorDrawingWindow alloc] init]; - [self adjustXorWindowGeometry:special]; - [self addChildWindow : special ordered : NSWindowAbove]; - [special release]; -} + auto res = [self findXorWindow]; + if (res) + return res; -//______________________________________________________________________________ -- (void) adjustXorWindowGeometry -{ - if (auto win = [self findXorWindow]) - [self adjustXorWindowGeometry:win]; + XorDrawingWindow *special = [[XorDrawingWindow alloc] init]; + res = special; + [self adjustXorWindowGeometry : special]; + [self addChildWindow : special ordered : NSWindowAbove]; + [special release]; + return res; } //______________________________________________________________________________ @@ -1536,12 +1534,12 @@ - (void) adjustXorWindowGeometry : (XorDrawingWindow *) win //______________________________________________________________________________ - (void) removeXorWindow { - if (auto win = [self findXorWindow]) { - // For some reason, without ordeing out, the crosshair window's content stays - // in the parent's window. Thus we first have to order out the crosshair window. - [win orderOut:nil]; - [self removeChildWindow : win]; - } + if (auto win = [self findXorWindow]) { + // For some reason, without ordeing out, the crosshair window's content stays + // in the parent's window. Thus we first have to order out the crosshair window. + [win orderOut:nil]; + [self removeChildWindow : win]; + } } //______________________________________________________________________________ @@ -1555,6 +1553,59 @@ - (XorDrawingWindow *) findXorWindow return nil; } +//______________________________________________________________________________ +- (void) addXorLine : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 : (Int_t) y2 +{ + auto xorWindow = [self addXorWindow]; + + try { + std::unique_ptr cmd(new ROOT::MacOSX::X11::DrawLineXor(-1, ROOT::MacOSX::X11::Point(x1, y1), ROOT::MacOSX::X11::Point(x2, y2))); + cmd->setView(view); + + auto cv = (XorDrawingView *)xorWindow.contentView; + [cv addXorCommand : cmd.get()]; + cmd.release(); + [cv setNeedsDisplay : YES]; + + } catch (const std::exception &) { + throw; + } +} + +//______________________________________________________________________________ +- (void) addXorBox : (QuartzView *) view : (Int_t) x1 : (Int_t) y1 : (Int_t) x2 : (Int_t) y2 +{ + auto xorWindow = [self addXorWindow]; + + try { + std::unique_ptr cmd(new ROOT::MacOSX::X11::DrawBoxXor(-1, ROOT::MacOSX::X11::Point(x1, y1), ROOT::MacOSX::X11::Point(x2, y2))); + cmd->setView(view); + + auto cv = (XorDrawingView *)xorWindow.contentView; + [cv addXorCommand : cmd.get()]; + cmd.release(); + [cv setNeedsDisplay : YES]; + } catch (const std::exception &) { + throw; + } +} + + +//______________________________________________________________________________ +- (void) setDrawMode : (TVirtualX::EDrawMode) newMode +{ + if (fDrawMode == TVirtualX::kInvert && newMode != TVirtualX::kInvert) + [self removeXorWindow]; + + fDrawMode = newMode; +} + +//______________________________________________________________________________ +- (TVirtualX::EDrawMode) getDrawMode +{ + return fDrawMode; +} + #pragma mark - X11Window protocol's implementation. @@ -1982,6 +2033,8 @@ - (id) initWithFrame : (NSRect) frame windowAttributes : (const SetWindowAttribu fBackBuffer = nil; fID = 0; + fDirectDraw = NO; + //Passive grab parameters. fPassiveGrabButton = -1;//0 is kAnyButton. fPassiveGrabEventMask = 0; @@ -2123,6 +2176,58 @@ - (void) setDrawableSize : (NSSize) newSize self.frame = frame; } +//______________________________________________________________________________ +- (TAttLine *) attLine +{ + return &fAttLine; +} + +//______________________________________________________________________________ +- (TAttFill *) attFill +{ + return &fAttFill; +} + +//______________________________________________________________________________ +- (TAttMarker *) attMarker +{ + return &fAttMarker; +} + +//______________________________________________________________________________ +- (TAttText *) attText +{ + return &fAttText; +} + +//______________________________________________________________________________ +- (void) setDrawMode : (TVirtualX::EDrawMode) newMode +{ + [self.fQuartzWindow setDrawMode:newMode]; +} + +//______________________________________________________________________________ +- (TVirtualX::EDrawMode) getDrawMode +{ + return [self.fQuartzWindow getDrawMode]; +} + +//______________________________________________________________________________ +- (void) setDirectDraw : (BOOL) mode +{ + fDirectDraw = mode; + + // while painting operates with the pixmap, set direct flag for it too + if (fBackBuffer) + [fBackBuffer setDirectDraw : mode]; +} + +//______________________________________________________________________________ +- (BOOL) isDirectDraw +{ + return fDirectDraw; +} + //______________________________________________________________________________ - (void) setX : (int) x Y : (int) y width : (unsigned) w height : (unsigned) h { diff --git a/graf2d/cocoa/src/TGCocoa.mm b/graf2d/cocoa/src/TGCocoa.mm index ca61ca61b694a..004ea9586eabe 100644 --- a/graf2d/cocoa/src/TGCocoa.mm +++ b/graf2d/cocoa/src/TGCocoa.mm @@ -585,8 +585,12 @@ void FixAscii(std::vector &text) fPimpl->fX11CommandBuffer.Flush(fPimpl.get()); } - if (fDirectDraw && mode != 2) - fPimpl->fX11CommandBuffer.FlushXOROps(fPimpl.get()); + if (fDirectDraw && mode != 2) { + // here was flushing of XOR operation + // now XOR operations collected directly by correspondent view and + // updated asynchronousely by calling view.setNeedsDisplay(YES) + // so there is no need for central instance + } } //______________________________________________________________________________ @@ -676,13 +680,42 @@ void FixAscii(std::vector &text) } //______________________________________________________________________________ -void TGCocoa::ClearWindow() +WinContext_t TGCocoa::GetWindowContext(Int_t wid) { - //Clear the selected drawable OR pixmap (the name - from TVirtualX interface - is bad). - assert(fSelectedDrawable > fPimpl->GetRootWindowID() && - "ClearWindow, fSelectedDrawable is invalid"); + if (!wid) + return (WinContext_t) 0; + auto drawable = fPimpl->GetDrawable(wid); + return (WinContext_t) drawable; +} + +//______________________________________________________________________________ +WinContext_t TGCocoa::GetSelectedContext() +{ + return GetWindowContext(fSelectedDrawable); +} + +//______________________________________________________________________________ +void TGCocoa::SetDrawModeW(WinContext_t wctxt, EDrawMode mode) +{ + auto drawable = (NSObject *) wctxt; + + // here XOR window and XOR operations can be removed if necessary + [drawable setDrawMode : mode]; +} + +//______________________________________________________________________________ +TVirtualX::EDrawMode TGCocoa::GetDrawModeW(WinContext_t wctxt) +{ + auto drawable = (NSObject *) wctxt; + + return [drawable getDrawMode]; +} + +//______________________________________________________________________________ +void TGCocoa::ClearWindowW(WinContext_t wctxt) +{ + auto drawable = (NSObject * const) wctxt; - NSObject * const drawable = fPimpl->GetDrawable(fSelectedDrawable); if (drawable.fIsPixmap) { //Pixmaps are white by default. //This is bad - we can not have transparent sub-pads (in TCanvas) @@ -697,10 +730,50 @@ void FixAscii(std::vector &text) CGContextClearRect(pixmapCtx, CGRectMake(0, 0, drawable.fWidth, drawable.fHeight)); } else { //For a window ClearArea with w == 0 and h == 0 means the whole window. - ClearArea(fSelectedDrawable, 0, 0, 0, 0); + ClearArea(drawable.fID, 0, 0, 0, 0); } } +//______________________________________________________________________________ +void TGCocoa::UpdateWindowW(WinContext_t wctxt, Int_t /* mode */) +{ + auto window = (NSObject * const) wctxt; + + //Have no idea, why this can happen with ROOT - done by TGDNDManager :( + if (window.fIsPixmap == YES) + return; + + if (QuartzPixmap * const pixmap = window.fBackBuffer) { + assert([window.fContentView isKindOfClass : [QuartzView class]] && "UpdateWindow, content view is not a QuartzView"); + QuartzView *dstView = (QuartzView *)window.fContentView; + + if (dstView.fIsOverlapped) + return; + + if (dstView.fContext) { + //We can draw directly. + const X11::Rectangle copyArea(0, 0, pixmap.fWidth, pixmap.fHeight); + [dstView copy : pixmap area : copyArea withMask : nil clipOrigin : X11::Point() toPoint : X11::Point()]; + } else { + //Have to wait. + fPimpl->fX11CommandBuffer.AddUpdateWindow(dstView); + Update(1); + } + } +} + + + +//______________________________________________________________________________ +void TGCocoa::ClearWindow() +{ + //Clear the selected drawable OR pixmap (the name - from TVirtualX interface - is bad). + assert(fSelectedDrawable > fPimpl->GetRootWindowID() && + "ClearWindow, fSelectedDrawable is invalid"); + + ClearWindowW((WinContext_t)fPimpl->GetDrawable(fSelectedDrawable)); +} + //______________________________________________________________________________ void TGCocoa::GetGeometry(Int_t windowID, Int_t & x, Int_t &y, UInt_t &w, UInt_t &h) { @@ -789,7 +862,7 @@ void FixAscii(std::vector &text) } //______________________________________________________________________________ -void TGCocoa::UpdateWindow(Int_t /*mode*/) +void TGCocoa::UpdateWindow(Int_t mode) { //This function is used by TCanvas/TPad: //draw "back buffer" image into the view. @@ -806,25 +879,7 @@ void FixAscii(std::vector &text) if (fPimpl->GetDrawable(fSelectedDrawable).fIsPixmap == YES) return; - NSObject * const window = fPimpl->GetWindow(fSelectedDrawable); - - if (QuartzPixmap * const pixmap = window.fBackBuffer) { - assert([window.fContentView isKindOfClass : [QuartzView class]] && "UpdateWindow, content view is not a QuartzView"); - QuartzView *dstView = (QuartzView *)window.fContentView; - - if (dstView.fIsOverlapped) - return; - - if (dstView.fContext) { - //We can draw directly. - const X11::Rectangle copyArea(0, 0, pixmap.fWidth, pixmap.fHeight); - [dstView copy : pixmap area : copyArea withMask : nil clipOrigin : X11::Point() toPoint : X11::Point()]; - } else { - //Have to wait. - fPimpl->fX11CommandBuffer.AddUpdateWindow(dstView); - Update(1); - } - } + UpdateWindowW((WinContext_t) fPimpl->GetWindow(fSelectedDrawable), mode); } //______________________________________________________________________________ @@ -3454,6 +3509,17 @@ void FixAscii(std::vector &text) void TGCocoa::SetDoubleBufferOFF() { fDirectDraw = true; + + assert(fSelectedDrawable > fPimpl->GetRootWindowID() && + "SetDoubleBufferON, called, but no correct window was selected before"); + + NSObject * const window = fPimpl->GetWindow(fSelectedDrawable); + if (!window) return; + + assert(window.fIsPixmap == NO && + "SetDoubleBufferON, selected drawable is a pixmap, can not attach pixmap to pixmap"); + + [window setDirectDraw : YES]; } //______________________________________________________________________________ @@ -3466,12 +3532,13 @@ void FixAscii(std::vector &text) "SetDoubleBufferON, called, but no correct window was selected before"); NSObject * const window = fPimpl->GetWindow(fSelectedDrawable); - if (!window) return; assert(window.fIsPixmap == NO && "SetDoubleBufferON, selected drawable is a pixmap, can not attach pixmap to pixmap"); + [window setDirectDraw : NO]; + const unsigned currW = window.fWidth; const unsigned currH = window.fHeight; @@ -3492,17 +3559,12 @@ void FixAscii(std::vector &text) //______________________________________________________________________________ void TGCocoa::SetDrawMode(EDrawMode mode) { - // Sets the drawing mode. + // Sets the drawing mode for all windows. // - //EDrawMode{kCopy, kXor, kInvert}; - if (fDrawMode == kInvert && mode != kInvert) { - // Remove previously added CrosshairWindow. - auto windows = NSApplication.sharedApplication.windows; - for (NSWindow *candidate : windows) { - if ([candidate isKindOfClass:QuartzWindow.class]) - [(QuartzWindow *)candidate removeXorWindow]; - } - fPimpl->fX11CommandBuffer.ClearXOROperations(); + auto windows = NSApplication.sharedApplication.windows; + for (NSWindow *candidate : windows) { + if ([candidate isKindOfClass:QuartzWindow.class]) + [(QuartzWindow *)candidate setDrawMode : mode]; } fDrawMode = mode; diff --git a/graf2d/cocoa/src/TGQuartz.mm b/graf2d/cocoa/src/TGQuartz.mm index 72cbf2dd97017..fab43c8649a02 100644 --- a/graf2d/cocoa/src/TGQuartz.mm +++ b/graf2d/cocoa/src/TGQuartz.mm @@ -113,27 +113,31 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector * const) wctxt; + if (!drawable0) + return; + //Check some conditions first. - if (fDirectDraw) { - if (!fPimpl->GetDrawable(fSelectedDrawable).fIsPixmap) { - QuartzView * const view = (QuartzView *)fPimpl->GetWindow(fSelectedDrawable).fContentView; + if ([drawable0 isDirectDraw]) { + if (!drawable0.fIsPixmap) { + QuartzView * const view = (QuartzView *)fPimpl->GetWindow(drawable0.fID).fContentView; if (!view) { - ::Warning("DrawLine", "Invalid view/window for XOR-mode"); + ::Warning("DrawBoxW", "Invalid view/window for XOR-mode"); return; } - if (![view.fQuartzWindow findXorWindow]) - [view.fQuartzWindow addXorWindow]; - fPimpl->fX11CommandBuffer.AddDrawBoxXor(fSelectedDrawable, x1, y1, x2, y2); + [view.fQuartzWindow addXorBox: view : x1 : y1 : x2 : y2]; } return; } - NSObject * const drawable = (NSObject *)GetSelectedDrawableChecked("DrawBox"); + auto &attline = GetAttLine(wctxt); + auto &attfill = GetAttFill(wctxt); + + auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "DrawBoxW"); if (!drawable) return; @@ -146,7 +150,7 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector(gROOT->GetColor(GetFillColor()))) { + if (const TColorGradient * const gradient = dynamic_cast(gROOT->GetColor(attfill.GetFillColor()))) { //Draw a box with a gradient fill and a shadow. //Ignore all fill styles and EBoxMode, use a gradient fill. TPoint polygon[4]; @@ -158,31 +162,37 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector * const) wctxt; + if (!drawable0) return; - //Do some checks first. - if (fDirectDraw)//To avoid warnings from Quartz - no context at the moment! + if (n < 3) + return; + // No fill area with direct drawing + if ([drawable0 isDirectDraw]) return; - NSObject * const drawable = - (NSObject *)GetSelectedDrawableChecked("DrawFillArea"); + auto &attfill = GetAttFill(wctxt); + auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "DrawFillAreaW"); if (!drawable) return; @@ -218,9 +230,9 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vectorGetColor(GetFillColor()); + const TColor * const fillColor = gROOT->GetColor(attfill.GetFillColor()); if (!fillColor) { - Error("DrawFillArea", "Could not find TColor for index %d", GetFillColor()); + Error("DrawFillAreaW", "Could not find TColor for index %d", attfill.GetFillColor()); return; } @@ -229,16 +241,25 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector * const) wctxt; + if (!drawable0) + return; - if (fDirectDraw) { - if (!fPimpl->GetDrawable(fSelectedDrawable).fIsPixmap) { - QuartzView * const view = (QuartzView *)fPimpl->GetWindow(fSelectedDrawable).fContentView; + if ([drawable0 isDirectDraw]) { + if (!drawable0.fIsPixmap) { + QuartzView * const view = (QuartzView *)fPimpl->GetWindow(drawable0.fID).fContentView; if (!view) { - ::Warning("DrawLine", "Invalid view/window for XOR-mode"); + ::Warning("DrawLineW", "Invalid view/window for XOR-mode"); return; } - if (![view.fQuartzWindow findXorWindow]) - [view.fQuartzWindow addXorWindow]; - fPimpl->fX11CommandBuffer.AddDrawLineXor(fSelectedDrawable, x1, y1, x2, y2); + [view.fQuartzWindow addXorLine: view : x1 : y1 : x2 : y2]; } return; } - //Do some checks first: - assert(fSelectedDrawable > fPimpl->GetRootWindowID() && "DrawLine, bad drawable is selected"); - NSObject * const drawable = - (NSObject *)GetSelectedDrawableChecked("DrawLine"); + auto &attline = GetAttLine(wctxt); + + auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "DrawLineW"); if (!drawable) return; @@ -282,34 +299,45 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector fPimpl->GetRootWindowID() && "DrawLine, bad drawable is selected"); + + DrawLineW(GetSelectedContext(), x1, y1, x2, y2); +} + //______________________________________________________________________________ -void TGQuartz::DrawPolyLine(Int_t n, TPoint *xy) +void TGQuartz::DrawPolyLineW(WinContext_t wctxt, Int_t n, TPoint *xy) { - //Comment from TVirtualX: - // Draw a line through all points. - // n : number of points - // xy : list of points - //End of comment. + auto drawable0 = (NSObject * const) wctxt; + if (!drawable0) + return; //Some checks first. - if (fDirectDraw)//To avoid warnings from Quartz - no context at the moment! + if ([drawable0 isDirectDraw]) return; - NSObject * const drawable = - (NSObject *)GetSelectedDrawableChecked("DrawPolyLine"); + auto &attline = GetAttLine(wctxt); + + auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "DrawPolyLineW"); if (!drawable) return; @@ -318,13 +346,13 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector fPimpl->GetRootWindowID() && "DrawPolyLine, bad drawable is selected"); + + DrawPolyLineW(GetSelectedContext(), n, xy); +} + +//______________________________________________________________________________ +void TGQuartz::DrawLinesSegmentsW(WinContext_t wctxt, Int_t n, TPoint *xy) +{ + for(Int_t i = 0; i < 2*n; i += 2) + DrawPolyLineW(wctxt, 2, &xy[i]); +} + +//______________________________________________________________________________ +void TGQuartz::DrawPolyMarkerW(WinContext_t wctxt, Int_t n, TPoint *xy) +{ + auto drawable0 = (NSObject * const) wctxt; + if (!drawable0) + return; + //Do some checks first. - if (fDirectDraw)//To avoid warnings from Quartz - no context at the moment! + if ([drawable0 isDirectDraw]) return; - NSObject * const drawable = - (NSObject *)GetSelectedDrawableChecked("DrawPolyMarker"); + auto &attmark = GetAttMarker(wctxt); + + auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "DrawPolyMarkerW"); if (!drawable) return; @@ -361,21 +408,21 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector 1.) CGContextScaleCTM(ctx, 1. / drawable.fScaleFactor, 1. / drawable.fScaleFactor); - Style_t markerstyle = TAttMarker::GetMarkerStyleBase(GetMarkerStyle()); + Style_t markerstyle = TAttMarker::GetMarkerStyleBase(attmark.GetMarkerStyle()); // The fast pixel markers need to be treated separately if (markerstyle == 1 || markerstyle == 6 || markerstyle == 7) { @@ -386,29 +433,46 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector * const) wctxt; + if (!drawable0) + return; + + if ([drawable0 isDirectDraw]) return; if (!text || !text[0])//Can this ever happen? TPad::PaintText does not check this. return; - if (GetTextSize()<1.5)//Do not draw anything, or CoreText will create some small (but not of size 0 font). + auto &atttext = GetAttText(wctxt); + + if (atttext.GetTextSize() < 1.5)//Do not draw anything, or CoreText will create some small (but not of size 0 font). return; - NSObject * const drawable = - (NSObject *)GetSelectedDrawableChecked("DrawText"); + auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "DrawTextW"); if (!drawable) return; @@ -419,8 +483,8 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vectorfFontManager.SelectFont(GetTextFont(), kScale*GetTextSize())) { - const unsigned fontIndex = GetTextFont() / 10; + if (CTFontRef currentFont = fPimpl->fFontManager.SelectFont(atttext.GetTextFont(), kScale * atttext.GetTextSize())) { + const unsigned fontIndex = atttext.GetTextFont() / 10; if (fontIndex == 12 || fontIndex == 15) {//Greek and math symbols. //This is a hack. Correct way is to extract glyphs from symbol.ttf, //find correct mapping, place this glyphs. This requires manual layout though (?), @@ -433,44 +497,51 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector * const) wctxt; + UInt_t width = 0; UInt_t height = 0; Int_t xy = 0; - GetWindowSize(GetCurrentWindow(), xy, xy, width, height); + GetWindowSize((Drawable_t) drawable.fID, xy, xy, width, height); // If string falls outside window, there is probably no need to draw it. if (x + int(w) <= 0 || x >= int(width)) @@ -808,7 +944,7 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector * const drawable = (NSObject *)GetSelectedDrawableChecked("DrawText"); + auto drawable0 = (NSObject * const) wctxt; + if (!drawable0) + return; + + if ([drawable0 isDirectDraw]) + return; + + auto &atttext = GetAttText(wctxt); + + if (!atttext.GetTextSize())//Do not draw anything, or CoreText will create some small (but not of size 0 font). + return; + + auto drawable = (NSObject * const) GetPixmapDrawable(drawable0, "RenderTTFString"); if (!drawable) return; @@ -829,7 +977,7 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector pixmap([[QuartzPixmap alloc] initWithW : w H : h scaleFactor : 1.f]); if (!pixmap.Get()) { - Error("DrawText", "pixmap creation failed"); + Error("RenderTTFString", "pixmap creation failed"); return; } @@ -867,7 +1015,7 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vectorleft + xOff; const Int_t by = h - bitmap->top - yOff; - DrawFTGlyphIntoPixmap(pixmap.Get(), source, TGCocoa::GetPixel(GetTextColor()), + DrawFTGlyphIntoPixmap(pixmap.Get(), source, TGCocoa::GetPixel(atttext.GetTextColor()), mode == kClear ? ULong_t(-1) : 0xffffff, bx, by); } @@ -1060,31 +1208,73 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector fPimpl->GetRootWindowID() && "GetSelectedDrawableChecked, bad drawable is selected"); + // attributes stored in direct drawable (view) and not in underlying pixmap + auto drawable = (NSObject *) wctxt; + if (!drawable || !drawable.attFill) + return *this; + return *drawable.attFill; +} - NSObject *drawable = fPimpl->GetDrawable(fSelectedDrawable); +//______________________________________________________________________________ +TAttLine &TGQuartz::GetAttLine(WinContext_t wctxt) +{ + // attributes stored in direct drawable (view) and not in underlying pixmap + auto drawable = (NSObject *) wctxt; + if (!drawable || !drawable.attLine) + return *this; + return *drawable.attLine; +} + +//______________________________________________________________________________ +TAttMarker &TGQuartz::GetAttMarker(WinContext_t wctxt) +{ + // attributes stored in direct drawable (view) and not in underlying pixmap + auto drawable = (NSObject *) wctxt; + if (!drawable || !drawable.attMarker) + return *this; + return *drawable.attMarker; +} + +//______________________________________________________________________________ +TAttText &TGQuartz::GetAttText(WinContext_t wctxt) +{ + // attributes stored in direct drawable (view) and not in underlying pixmap + auto drawable = (NSObject *) wctxt; + if (!drawable || !drawable.attText) + return *this; + return *drawable.attText; +} + +//______________________________________________________________________________ +void *TGQuartz::GetPixmapDrawable(void *drawable0, const char *calledFrom) const +{ + assert(calledFrom != 0 && "GetDrawableChecked, calledFrom parameter is null"); + + if (!drawable0) + return nullptr; + + auto drawable = (NSObject *) drawable0; if (!drawable.fIsPixmap) { //TPad/TCanvas ALWAYS draw only into a pixmap. if ([drawable isKindOfClass : [QuartzView class]]) { QuartzView *view = (QuartzView *)drawable; if (!view.fBackBuffer) { Error(calledFrom, "Selected window is not double buffered"); - return 0; + return nullptr; } drawable = view.fBackBuffer; } else { Error(calledFrom, "Selected drawable is neither a pixmap, nor a double buffered window"); - return 0; + return nullptr; } } if (!drawable.fContext) { Error(calledFrom, "Context is null"); - return 0; + return nullptr; } return drawable; diff --git a/graf2d/cocoa/src/X11Buffer.mm b/graf2d/cocoa/src/X11Buffer.mm index 77a91211f9f50..76cc0ed250f1e 100644 --- a/graf2d/cocoa/src/X11Buffer.mm +++ b/graf2d/cocoa/src/X11Buffer.mm @@ -335,7 +335,6 @@ CommandBuffer::~CommandBuffer() { ClearCommands(); - ClearXOROperations(); } //______________________________________________________________________________ @@ -499,30 +498,6 @@ } } -//______________________________________________________________________________ -void CommandBuffer::AddDrawBoxXor(Window_t windowID, Int_t x1, Int_t y1, Int_t x2, Int_t y2) -{ - try { - std::unique_ptr cmd(new DrawBoxXor(windowID, Point(x1, y1), Point(x2, y2))); - fXorOps.push_back(cmd.get()); - cmd.release(); - } catch (const std::exception &) { - throw; - } -} - -//______________________________________________________________________________ -void CommandBuffer::AddDrawLineXor(Window_t windowID, Int_t x1, Int_t y1, Int_t x2, Int_t y2) -{ - try { - std::unique_ptr cmd(new DrawLineXor(windowID, Point(x1, y1), Point(x2, y2))); - fXorOps.push_back(cmd.get()); - cmd.release(); - } catch (const std::exception &) { - throw; - } -} - //______________________________________________________________________________ void CommandBuffer::Flush(Details::CocoaPrivate *impl) { @@ -605,42 +580,6 @@ ClearCommands(); } -//______________________________________________________________________________ -void CommandBuffer::FlushXOROps(Details::CocoaPrivate *impl) -{ - // The only XOR operations we ever had to support were the drawing - // of lines for a crosshair in a TCanvas and drawing the histogram's - // range when using a fit panel/interactive fitting. In the past - // we were using a deprecated (since 10.14) trick with locking - // a focus on a view, drawing, flushing CGContext and then unlocking. - // This stopped working since 10.15. So now the only thing - // we can do is to draw into a special transparent window which is - // attached on top of the canvas. - if (!fXorOps.size()) - return; - - assert(impl != 0 && "FlushXOROps, impl parameter is null"); - - NSObject * const drawable = impl->GetDrawable(fXorOps.back()->fID); - assert([drawable isKindOfClass : [QuartzView class]] && - "FlushXOROps, drawable must be of type QuartzView"); - QuartzView * const view = (QuartzView *)drawable; - for (auto *op : fXorOps) - op->setView(view); - QuartzWindow * const window = view.fQuartzWindow; - auto xorWindow = [window findXorWindow]; - if (!xorWindow) { - ::Warning("FlushXOROps", "No XorDrawingWindow found to draw into"); - ClearXOROperations(); - return; - } - - XorDrawingView *cv = (XorDrawingView *)xorWindow.contentView; - [cv setXorOperations: fXorOps]; // Pass the ownership of those objects. - fXorOps.clear(); // A view will free the memory. - [cv setNeedsDisplay : YES]; -} - //______________________________________________________________________________ void CommandBuffer::RemoveOperationsForDrawable(Drawable_t drawable) { @@ -650,13 +589,6 @@ fCommands[i] = 0; } } - - for (size_type i = 0; i < fXorOps.size(); ++i) { - if (fXorOps[i] && fXorOps[i]->HasOperand(drawable)) { - delete fXorOps[i]; - fXorOps[i] = 0; - } - } } //______________________________________________________________________________ @@ -672,17 +604,6 @@ } } -//______________________________________________________________________________ -void CommandBuffer::RemoveXORGraphicsOperationsForWindow(Window_t wid) -{ - for (size_type i = 0; i < fCommands.size(); ++i) { - if (fXorOps[i] && fXorOps[i]->HasOperand(wid)) { - delete fXorOps[i]; - fXorOps[i] = 0; - } - } -} - //______________________________________________________________________________ void CommandBuffer::ClearCommands() { @@ -692,15 +613,6 @@ fCommands.clear(); } -//______________________________________________________________________________ -void CommandBuffer::ClearXOROperations() -{ - for (size_type i = 0, e = fXorOps.size(); i < e; ++i) - delete fXorOps[i]; - - fXorOps.clear(); -} - //Clipping machinery. namespace { diff --git a/graf2d/freetype/src/freetype-2.12.1.tar.gz b/graf2d/freetype/src/freetype-2.12.1.tar.gz deleted file mode 100644 index 0d1b56dcbe394..0000000000000 Binary files a/graf2d/freetype/src/freetype-2.12.1.tar.gz and /dev/null differ diff --git a/graf2d/freetype/src/win/freetype.dep b/graf2d/freetype/src/win/freetype.dep deleted file mode 100644 index 99997fa9e5f32..0000000000000 --- a/graf2d/freetype/src/win/freetype.dep +++ /dev/null @@ -1,396 +0,0 @@ -# Microsoft Developer Studio Generated Dependency File, included by freetype.mak - -..\..\..\src\autofit\autofit.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\autofit\afangles.c"\ - "..\..\..\src\autofit\afcjk.c"\ - "..\..\..\src\autofit\afcjk.h"\ - "..\..\..\src\autofit\afdummy.c"\ - "..\..\..\src\autofit\afdummy.h"\ - "..\..\..\src\autofit\aferrors.h"\ - "..\..\..\src\autofit\afglobal.c"\ - "..\..\..\src\autofit\afglobal.h"\ - "..\..\..\src\autofit\afhints.c"\ - "..\..\..\src\autofit\afhints.h"\ - "..\..\..\src\autofit\afindic.c"\ - "..\..\..\src\autofit\afindic.h"\ - "..\..\..\src\autofit\aflatin.c"\ - "..\..\..\src\autofit\aflatin.h"\ - "..\..\..\src\autofit\aflatin2.c"\ - "..\..\..\src\autofit\aflatin2.h"\ - "..\..\..\src\autofit\afloader.c"\ - "..\..\..\src\autofit\afloader.h"\ - "..\..\..\src\autofit\afmodule.c"\ - "..\..\..\src\autofit\afmodule.h"\ - "..\..\..\src\autofit\aftypes.h"\ - "..\..\..\src\autofit\afwarp.c"\ - "..\..\..\src\autofit\afwarp.h"\ - - -..\..\..\src\bdf\bdf.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\bdf\bdf.h"\ - "..\..\..\src\bdf\bdfdrivr.c"\ - "..\..\..\src\bdf\bdfdrivr.h"\ - "..\..\..\src\bdf\bdferror.h"\ - "..\..\..\src\bdf\bdflib.c"\ - - -..\..\..\src\cff\cff.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\cff\cffcmap.c"\ - "..\..\..\src\cff\cffcmap.h"\ - "..\..\..\src\cff\cffdrivr.c"\ - "..\..\..\src\cff\cffdrivr.h"\ - "..\..\..\src\cff\cfferrs.h"\ - "..\..\..\src\cff\cffgload.c"\ - "..\..\..\src\cff\cffgload.h"\ - "..\..\..\src\cff\cffload.c"\ - "..\..\..\src\cff\cffload.h"\ - "..\..\..\src\cff\cffobjs.c"\ - "..\..\..\src\cff\cffobjs.h"\ - "..\..\..\src\cff\cffparse.c"\ - "..\..\..\src\cff\cffparse.h"\ - "..\..\..\src\cff\cfftoken.h"\ - "..\..\..\src\cff\cfftypes.h"\ - - -..\..\..\src\base\ftbase.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\base\ftcalc.c"\ - "..\..\..\src\base\ftdbgmem.c"\ - "..\..\..\src\base\ftgloadr.c"\ - "..\..\..\src\base\ftobjs.c"\ - "..\..\..\src\base\ftoutln.c"\ - "..\..\..\src\base\ftrfork.c"\ - "..\..\..\src\base\ftstream.c"\ - "..\..\..\src\base\fttrigon.c"\ - "..\..\..\src\base\ftutil.c"\ - - -..\..\..\src\base\ftbbox.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftbdf.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftbitmap.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\cache\ftcache.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\cache\ftcbasic.c"\ - "..\..\..\src\cache\ftccache.c"\ - "..\..\..\src\cache\ftccache.h"\ - "..\..\..\src\cache\ftccback.h"\ - "..\..\..\src\cache\ftccmap.c"\ - "..\..\..\src\cache\ftcerror.h"\ - "..\..\..\src\cache\ftcglyph.c"\ - "..\..\..\src\cache\ftcglyph.h"\ - "..\..\..\src\cache\ftcimage.c"\ - "..\..\..\src\cache\ftcimage.h"\ - "..\..\..\src\cache\ftcmanag.c"\ - "..\..\..\src\cache\ftcmanag.h"\ - "..\..\..\src\cache\ftcmru.c"\ - "..\..\..\src\cache\ftcmru.h"\ - "..\..\..\src\cache\ftcsbits.c"\ - "..\..\..\src\cache\ftcsbits.h"\ - - -..\ftdebug.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftgasp.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftglyph.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftgxval.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\gzip\ftgzip.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\gzip\adler32.c"\ - "..\..\..\src\gzip\infblock.c"\ - "..\..\..\src\gzip\infblock.h"\ - "..\..\..\src\gzip\infcodes.c"\ - "..\..\..\src\gzip\infcodes.h"\ - "..\..\..\src\gzip\inffixed.h"\ - "..\..\..\src\gzip\inflate.c"\ - "..\..\..\src\gzip\inftrees.c"\ - "..\..\..\src\gzip\inftrees.h"\ - "..\..\..\src\gzip\infutil.c"\ - "..\..\..\src\gzip\infutil.h"\ - "..\..\..\src\gzip\zconf.h"\ - "..\..\..\src\gzip\zlib.h"\ - "..\..\..\src\gzip\zutil.c"\ - "..\..\..\src\gzip\zutil.h"\ - - -..\..\..\src\base\ftinit.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\lzw\ftlzw.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\lzw\ftzopen.c"\ - "..\..\..\src\lzw\ftzopen.h"\ - - -..\..\..\src\base\ftmm.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftotval.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftpfr.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftstroke.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftsynth.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftsystem.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\fttype1.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftwinfnt.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\base\ftxf86.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - - -..\..\..\src\pcf\pcf.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\pcf\pcf.h"\ - "..\..\..\src\pcf\pcfdrivr.c"\ - "..\..\..\src\pcf\pcfdrivr.h"\ - "..\..\..\src\pcf\pcferror.h"\ - "..\..\..\src\pcf\pcfread.c"\ - "..\..\..\src\pcf\pcfread.h"\ - "..\..\..\src\pcf\pcfutil.c"\ - "..\..\..\src\pcf\pcfutil.h"\ - - -..\..\..\src\pfr\pfr.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\pfr\pfrcmap.c"\ - "..\..\..\src\pfr\pfrcmap.h"\ - "..\..\..\src\pfr\pfrdrivr.c"\ - "..\..\..\src\pfr\pfrdrivr.h"\ - "..\..\..\src\pfr\pfrerror.h"\ - "..\..\..\src\pfr\pfrgload.c"\ - "..\..\..\src\pfr\pfrgload.h"\ - "..\..\..\src\pfr\pfrload.c"\ - "..\..\..\src\pfr\pfrload.h"\ - "..\..\..\src\pfr\pfrobjs.c"\ - "..\..\..\src\pfr\pfrobjs.h"\ - "..\..\..\src\pfr\pfrsbit.c"\ - "..\..\..\src\pfr\pfrsbit.h"\ - "..\..\..\src\pfr\pfrtypes.h"\ - - -..\..\..\src\psaux\psaux.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\psaux\afmparse.c"\ - "..\..\..\src\psaux\afmparse.h"\ - "..\..\..\src\psaux\psauxerr.h"\ - "..\..\..\src\psaux\psauxmod.c"\ - "..\..\..\src\psaux\psauxmod.h"\ - "..\..\..\src\psaux\psconv.c"\ - "..\..\..\src\psaux\psconv.h"\ - "..\..\..\src\psaux\psobjs.c"\ - "..\..\..\src\psaux\psobjs.h"\ - "..\..\..\src\psaux\t1cmap.c"\ - "..\..\..\src\psaux\t1cmap.h"\ - "..\..\..\src\psaux\t1decode.c"\ - "..\..\..\src\psaux\t1decode.h"\ - - -..\..\..\src\pshinter\pshinter.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\pshinter\pshalgo.c"\ - "..\..\..\src\pshinter\pshalgo.h"\ - "..\..\..\src\pshinter\pshglob.c"\ - "..\..\..\src\pshinter\pshglob.h"\ - "..\..\..\src\pshinter\pshmod.c"\ - "..\..\..\src\pshinter\pshnterr.h"\ - "..\..\..\src\pshinter\pshrec.c"\ - "..\..\..\src\pshinter\pshrec.h"\ - - -..\..\..\src\psnames\psmodule.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\psnames\psmodule.h"\ - "..\..\..\src\psnames\psnamerr.h"\ - "..\..\..\src\psnames\pstables.h"\ - - -..\..\..\src\raster\raster.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\raster\ftmisc.h"\ - "..\..\..\src\raster\ftraster.c"\ - "..\..\..\src\raster\ftraster.h"\ - "..\..\..\src\raster\ftrend1.c"\ - "..\..\..\src\raster\ftrend1.h"\ - "..\..\..\src\raster\rasterrs.h"\ - - -..\..\..\src\sfnt\sfnt.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\sfnt\sfdriver.c"\ - "..\..\..\src\sfnt\sfdriver.h"\ - "..\..\..\src\sfnt\sferrors.h"\ - "..\..\..\src\sfnt\sfobjs.c"\ - "..\..\..\src\sfnt\sfobjs.h"\ - "..\..\..\src\sfnt\ttbdf.c"\ - "..\..\..\src\sfnt\ttbdf.h"\ - "..\..\..\src\sfnt\ttcmap.c"\ - "..\..\..\src\sfnt\ttcmap.h"\ - "..\..\..\src\sfnt\ttkern.c"\ - "..\..\..\src\sfnt\ttkern.h"\ - "..\..\..\src\sfnt\ttload.c"\ - "..\..\..\src\sfnt\ttload.h"\ - "..\..\..\src\sfnt\ttmtx.c"\ - "..\..\..\src\sfnt\ttmtx.h"\ - "..\..\..\src\sfnt\ttpost.c"\ - "..\..\..\src\sfnt\ttpost.h"\ - "..\..\..\src\sfnt\ttsbit.c"\ - "..\..\..\src\sfnt\ttsbit.h"\ - - -..\..\..\src\smooth\smooth.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\smooth\ftgrays.c"\ - "..\..\..\src\smooth\ftgrays.h"\ - "..\..\..\src\smooth\ftsmerrs.h"\ - "..\..\..\src\smooth\ftsmooth.c"\ - "..\..\..\src\smooth\ftsmooth.h"\ - - -..\..\..\src\truetype\truetype.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\truetype\ttdriver.c"\ - "..\..\..\src\truetype\ttdriver.h"\ - "..\..\..\src\truetype\tterrors.h"\ - "..\..\..\src\truetype\ttgload.c"\ - "..\..\..\src\truetype\ttgload.h"\ - "..\..\..\src\truetype\ttgxvar.c"\ - "..\..\..\src\truetype\ttgxvar.h"\ - "..\..\..\src\truetype\ttinterp.c"\ - "..\..\..\src\truetype\ttinterp.h"\ - "..\..\..\src\truetype\ttobjs.c"\ - "..\..\..\src\truetype\ttobjs.h"\ - "..\..\..\src\truetype\ttpload.c"\ - "..\..\..\src\truetype\ttpload.h"\ - - -..\..\..\src\type1\type1.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\type1\t1afm.c"\ - "..\..\..\src\type1\t1afm.h"\ - "..\..\..\src\type1\t1driver.c"\ - "..\..\..\src\type1\t1driver.h"\ - "..\..\..\src\type1\t1errors.h"\ - "..\..\..\src\type1\t1gload.c"\ - "..\..\..\src\type1\t1gload.h"\ - "..\..\..\src\type1\t1load.c"\ - "..\..\..\src\type1\t1load.h"\ - "..\..\..\src\type1\t1objs.c"\ - "..\..\..\src\type1\t1objs.h"\ - "..\..\..\src\type1\t1parse.c"\ - "..\..\..\src\type1\t1parse.h"\ - "..\..\..\src\type1\t1tokens.h"\ - - -..\..\..\src\cid\type1cid.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\cid\ciderrs.h"\ - "..\..\..\src\cid\cidgload.c"\ - "..\..\..\src\cid\cidgload.h"\ - "..\..\..\src\cid\cidload.c"\ - "..\..\..\src\cid\cidload.h"\ - "..\..\..\src\cid\cidobjs.c"\ - "..\..\..\src\cid\cidobjs.h"\ - "..\..\..\src\cid\cidparse.c"\ - "..\..\..\src\cid\cidparse.h"\ - "..\..\..\src\cid\cidriver.c"\ - "..\..\..\src\cid\cidriver.h"\ - "..\..\..\src\cid\cidtoken.h"\ - - -..\..\..\src\type42\type42.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\type42\t42drivr.c"\ - "..\..\..\src\type42\t42drivr.h"\ - "..\..\..\src\type42\t42error.h"\ - "..\..\..\src\type42\t42objs.c"\ - "..\..\..\src\type42\t42objs.h"\ - "..\..\..\src\type42\t42parse.c"\ - "..\..\..\src\type42\t42parse.h"\ - "..\..\..\src\type42\t42types.h"\ - - -..\..\..\src\winfonts\winfnt.c : \ - "..\..\..\include\freetype\config\ftheader.h"\ - "..\..\..\include\ft2build.h"\ - "..\..\..\src\winfonts\fnterrs.h"\ - "..\..\..\src\winfonts\winfnt.h"\ - diff --git a/graf2d/freetype/src/win/freetype.mak b/graf2d/freetype/src/win/freetype.mak deleted file mode 100644 index d58effbe84b0d..0000000000000 --- a/graf2d/freetype/src/win/freetype.mak +++ /dev/null @@ -1,662 +0,0 @@ -# Microsoft Developer Studio Generated NMAKE File, Based on freetype.dsp -!IF "$(CFG)" == "" -CFG=freetype - Win32 Debug Singlethreaded -!MESSAGE No configuration specified. Defaulting to freetype - Win32 Debug Singlethreaded. -!ENDIF - -!IF "$(CFG)" != "freetype - Win32 Release" && "$(CFG)" != "freetype - Win32 Debug" && "$(CFG)" != "freetype - Win32 Debug Multithreaded" && "$(CFG)" != "freetype - Win32 Release Multithreaded" && "$(CFG)" != "freetype - Win32 Release Singlethreaded" && "$(CFG)" != "freetype - Win32 Debug Singlethreaded" -!MESSAGE Invalid configuration "$(CFG)" specified. -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "freetype.mak" CFG="freetype - Win32 Debug Singlethreaded" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "freetype - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "freetype - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE "freetype - Win32 Debug Multithreaded" (based on "Win32 (x86) Static Library") -!MESSAGE "freetype - Win32 Release Multithreaded" (based on "Win32 (x86) Static Library") -!MESSAGE "freetype - Win32 Release Singlethreaded" (based on "Win32 (x86) Static Library") -!MESSAGE "freetype - Win32 Debug Singlethreaded" (based on "Win32 (x86) Static Library") -!MESSAGE -!ERROR An invalid configuration is specified. -!ENDIF - -!IF "$(OS)" == "Windows_NT" -NULL= -!ELSE -NULL=nul -!ENDIF - -!IF "$(CFG)" == "freetype - Win32 Release" -OUTDIR=.\..\..\..\objs\release -INTDIR=.\..\..\..\objs\release -CPP_SWITCHES=$(NMAKECXXFLAGS) /Z7 /O2 /I "..\\..\\..\\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c -!ELSEIF "$(CFG)" == "freetype - Win32 Debug" -OUTDIR=.\..\..\..\objs\debug -INTDIR=.\..\..\..\objs\debug -CPP_SWITCHES=$(NMAKECXXFLAGS) /Z7 /Od /I "..\\..\\..\\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT_DEBUG_LEVEL_ERROR" /D "FT_DEBUG_LEVEL_TRACE" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c -!ELSEIF "$(CFG)" == "freetype - Win32 Debug Multithreaded" -OUTDIR=.\..\..\..\objs\debug_mt -INTDIR=.\..\..\..\objs\debug_mt -CPP_SWITCHES=$(NMAKECXXFLAGS) /Z7 /Od /I "..\\..\\..\\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT_DEBUG_LEVEL_ERROR" /D "FT_DEBUG_LEVEL_TRACE" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c -!ELSEIF "$(CFG)" == "freetype - Win32 Release Multithreaded" -OUTDIR=.\..\..\..\objs\release_mt -INTDIR=.\..\..\..\objs\release_mt -CPP_SWITCHES=$(NMAKECXXFLAGS) /Z7 /O2 /I "..\\..\\..\\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c -!ELSEIF "$(CFG)" == "freetype - Win32 Release Singlethreaded" -OUTDIR=.\..\..\..\objs\release_st -INTDIR=.\..\..\..\objs\release_st -CPP_SWITCHES=$(NMAKECXXFLAGS) /Z7 /O2 /I "..\\..\\..\\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c -!ELSEIF "$(CFG)" == "freetype - Win32 Debug Singlethreaded" -OUTDIR=.\..\..\..\objs\debug_st -INTDIR=.\..\..\..\objs\debug_st -CPP_SWITCHES=$(NMAKECXXFLAGS) /Z7 /Od /I "..\\..\\..\\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT_DEBUG_LEVEL_ERROR" /D "FT_DEBUG_LEVEL_TRACE" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c -!ENDIF - -LIB32_OBJS= \ - "$(INTDIR)\autofit.obj" \ - "$(INTDIR)\bdf.obj" \ - "$(INTDIR)\cff.obj" \ - "$(INTDIR)\ftbase.obj" \ - "$(INTDIR)\ftbbox.obj" \ - "$(INTDIR)\ftbdf.obj" \ - "$(INTDIR)\ftbitmap.obj" \ - "$(INTDIR)\ftcache.obj" \ - "$(INTDIR)\ftdebug.obj" \ - "$(INTDIR)\ftfstype.obj" \ - "$(INTDIR)\ftgasp.obj" \ - "$(INTDIR)\ftglyph.obj" \ - "$(INTDIR)\ftgxval.obj" \ - "$(INTDIR)\ftgzip.obj" \ - "$(INTDIR)\ftinit.obj" \ - "$(INTDIR)\ftlcdfil.obj" \ - "$(INTDIR)\ftlzw.obj" \ - "$(INTDIR)\ftmm.obj" \ - "$(INTDIR)\ftotval.obj" \ - "$(INTDIR)\ftpatent.obj" \ - "$(INTDIR)\ftpfr.obj" \ - "$(INTDIR)\ftstroke.obj" \ - "$(INTDIR)\ftsynth.obj" \ - "$(INTDIR)\ftsystem.obj" \ - "$(INTDIR)\fttype1.obj" \ - "$(INTDIR)\ftwinfnt.obj" \ - "$(INTDIR)\pcf.obj" \ - "$(INTDIR)\pfr.obj" \ - "$(INTDIR)\psaux.obj" \ - "$(INTDIR)\pshinter.obj" \ - "$(INTDIR)\psmodule.obj" \ - "$(INTDIR)\raster.obj" \ - "$(INTDIR)\sfnt.obj" \ - "$(INTDIR)\smooth.obj" \ - "$(INTDIR)\truetype.obj" \ - "$(INTDIR)\type1.obj" \ - "$(INTDIR)\type1cid.obj" \ - "$(INTDIR)\type42.obj" \ - "$(INTDIR)\winfnt.obj" - -!IF "$(CFG)" == "freetype - Win32 Release" - -ALL : "..\..\..\objs\freetype261.lib" - -CLEAN : - -@erase /q "$(INTDIR)\*.obj" >nul 2>&1 - -@erase /q "$(INTDIR)\*.idb" >nul 2>&1 - -@erase /q "..\..\..\objs\freetype261.lib" >nul 2>&1 - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -CPP=cl.exe -CPP_PROJ=$(NMAKECXXFLAGS) /Z7 /O2 /I "..\\..\\..\\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c - -.c{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.c{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -RSC=rc.exe -BSC32=bscmake.exe -BSC32_FLAGS=/nologo /o"$(OUTDIR)\freetype.bsc" -BSC32_SBRS= \ - -LIB32=link.exe -lib -LIB32_FLAGS=/nologo /out:"..\..\..\objs\freetype261.lib" - -"..\..\..\objs\freetype261.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) - $(LIB32) @<< - $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) -<< - -!ELSEIF "$(CFG)" == "freetype - Win32 Debug" - -ALL : "..\..\..\objs\freetype261_D.lib" - -CLEAN : - -@erase /q "$(INTDIR)\*.obj" >nul 2>&1 - -@erase /q "$(INTDIR)\*.idb" >nul 2>&1 - -@erase /q "..\..\..\objs\freetype261_D.lib" >nul 2>&1 - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -CPP=cl.exe -CPP_PROJ=$(NMAKECXXFLAGS) /Z7 /Od /I "..\\..\\..\\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT_DEBUG_LEVEL_ERROR" /D "FT_DEBUG_LEVEL_TRACE" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c - -.c{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.c{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -RSC=rc.exe -BSC32=bscmake.exe -BSC32_FLAGS=/nologo /o"$(OUTDIR)\freetype.bsc" -BSC32_SBRS= \ - -LIB32=link.exe -lib -LIB32_FLAGS=/nologo /out:"..\..\..\objs\freetype261_D.lib" - -"..\..\..\objs\freetype261_D.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) - $(LIB32) @<< - $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) -<< - -!ELSEIF "$(CFG)" == "freetype - Win32 Debug Multithreaded" - -ALL : "..\..\..\objs\freetype261MT_D.lib" - -CLEAN : - -@erase /q "$(INTDIR)\*.obj" >nul 2>&1 - -@erase /q "$(INTDIR)\*.idb" >nul 2>&1 - -@erase /q "..\..\..\objs\freetype261MT_D.lib" >nul 2>&1 - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -CPP=cl.exe -CPP_PROJ=$(NMAKECXXFLAGS) /Z7 /Od /I "..\\..\\..\\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT_DEBUG_LEVEL_ERROR" /D "FT_DEBUG_LEVEL_TRACE" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c - -.c{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.c{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -RSC=rc.exe -BSC32=bscmake.exe -BSC32_FLAGS=/nologo /o"$(OUTDIR)\freetype.bsc" -BSC32_SBRS= \ - -LIB32=link.exe -lib -LIB32_FLAGS=/nologo /out:"..\..\..\objs\freetype261MT_D.lib" - -"..\..\..\objs\freetype261MT_D.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) - $(LIB32) @<< - $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) -<< - -!ELSEIF "$(CFG)" == "freetype - Win32 Release Multithreaded" - -ALL : "..\..\..\objs\freetype261MT.lib" - -CLEAN : - -@erase /q "$(INTDIR)\*.obj" >nul 2>&1 - -@erase /q "$(INTDIR)\*.idb" >nul 2>&1 - -@erase /q "..\..\..\objs\freetype261MT.lib" >nul 2>&1 - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -CPP=cl.exe -CPP_PROJ=$(NMAKECXXFLAGS) /Z7 /O2 /I "..\\..\\..\\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c - -.c{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.c{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -RSC=rc.exe -BSC32=bscmake.exe -BSC32_FLAGS=/nologo /o"$(OUTDIR)\freetype.bsc" -BSC32_SBRS= \ - -LIB32=link.exe -lib -LIB32_FLAGS=/nologo /out:"..\..\..\objs\freetype261MT.lib" - -"..\..\..\objs\freetype261MT.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) - $(LIB32) @<< - $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) -<< - -!ELSEIF "$(CFG)" == "freetype - Win32 Release Singlethreaded" - -ALL : "..\..\..\objs\freetype261ST.lib" - -CLEAN : - -@erase /q "$(INTDIR)\*.obj" >nul 2>&1 - -@erase /q "$(INTDIR)\*.idb" >nul 2>&1 - -@erase /q "..\..\..\objs\freetype261ST.lib" >nul 2>&1 - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -CPP=cl.exe -CPP_PROJ=$(NMAKECXXFLAGS) /Z7 /O2 /I "..\\..\\..\\include" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c - -.c{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.c{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -RSC=rc.exe -BSC32=bscmake.exe -BSC32_FLAGS=/nologo /o"$(OUTDIR)\freetype.bsc" -BSC32_SBRS= \ - -LIB32=link.exe -lib -LIB32_FLAGS=/out:"..\..\..\objs\freetype261ST.lib" - -"..\..\..\objs\freetype261ST.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) - $(LIB32) @<< - $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) -<< - -!ELSEIF "$(CFG)" == "freetype - Win32 Debug Singlethreaded" - -ALL : "..\..\..\objs\freetype261ST_D.lib" - -CLEAN : - -@erase /q "$(INTDIR)\*.obj" >nul 2>&1 - -@erase /q "$(INTDIR)\*.idb" >nul 2>&1 - -@erase /q "..\..\..\objs\freetype261ST_D.lib" >nul 2>&1 - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -CPP=cl.exe -CPP_PROJ=$(NMAKECXXFLAGS) /Z7 /Od /I "..\\..\\..\\include" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "FT_DEBUG_LEVEL_ERROR" /D "FT_DEBUG_LEVEL_TRACE" /D "FT2_BUILD_LIBRARY" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c - -.c{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.obj:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.c{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cpp{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -.cxx{$(INTDIR)}.sbr:: - $(CPP) -nologo @<< - $(CPP_PROJ) $< -<< - -RSC=rc.exe -BSC32=bscmake.exe -BSC32_FLAGS=/nologo /o"$(OUTDIR)\freetype.bsc" -BSC32_SBRS= \ - -LIB32=link.exe -lib -LIB32_FLAGS=/nologo /out:"..\..\..\objs\freetype261ST_D.lib" - -"..\..\..\objs\freetype261ST_D.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) - $(LIB32) @<< - $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) -<< - -!ENDIF - - -!IF "$(NO_EXTERNAL_DEPS)" != "1" -!IF EXISTS("freetype.dep") -!INCLUDE "freetype.dep" -!ELSE -!MESSAGE Warning: cannot find "freetype.dep" -!ENDIF -!ENDIF - -SOURCE=..\..\..\src\autofit\autofit.c -"$(INTDIR)\autofit.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\bdf\bdf.c -"$(INTDIR)\bdf.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\cff\cff.c -"$(INTDIR)\cff.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\ftbase.c -"$(INTDIR)\ftbase.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\ftbbox.c -"$(INTDIR)\ftbbox.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftbdf.c -"$(INTDIR)\ftbdf.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftbitmap.c -"$(INTDIR)\ftbitmap.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\cache\ftcache.c -"$(INTDIR)\ftcache.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\ftdebug.c -"$(INTDIR)\ftdebug.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\ftfstype.c -"$(INTDIR)\ftfstype.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\ftgasp.c -"$(INTDIR)\ftgasp.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftglyph.c -"$(INTDIR)\ftglyph.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\ftgxval.c -"$(INTDIR)\ftgxval.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - - -SOURCE=..\..\..\src\gzip\ftgzip.c -"$(INTDIR)\ftgzip.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftinit.c -"$(INTDIR)\ftinit.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\ftlcdfil.c -"$(INTDIR)\ftlcdfil.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\lzw\ftlzw.c -"$(INTDIR)\ftlzw.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftmm.c -"$(INTDIR)\ftmm.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\ftotval.c -"$(INTDIR)\ftotval.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftpatent.c -"$(INTDIR)\ftpatent.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\ftpfr.c -"$(INTDIR)\ftpfr.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftstroke.c -"$(INTDIR)\ftstroke.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftsynth.c -"$(INTDIR)\ftsynth.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftsystem.c -"$(INTDIR)\ftsystem.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\base\fttype1.c -"$(INTDIR)\fttype1.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\base\ftwinfnt.c -"$(INTDIR)\ftwinfnt.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\pcf\pcf.c -"$(INTDIR)\pcf.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\pfr\pfr.c -"$(INTDIR)\pfr.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - -SOURCE=..\..\..\src\psaux\psaux.c -"$(INTDIR)\psaux.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\pshinter\pshinter.c -"$(INTDIR)\pshinter.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\psnames\psmodule.c -"$(INTDIR)\psmodule.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\raster\raster.c -"$(INTDIR)\raster.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\sfnt\sfnt.c -"$(INTDIR)\sfnt.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\smooth\smooth.c -"$(INTDIR)\smooth.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\truetype\truetype.c -"$(INTDIR)\truetype.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\type1\type1.c -"$(INTDIR)\type1.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\cid\type1cid.c -"$(INTDIR)\type1cid.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\type42\type42.c -"$(INTDIR)\type42.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - -SOURCE=..\..\..\src\winfonts\winfnt.c -"$(INTDIR)\winfnt.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) -nologo @<< - $(CPP_SWITCHES) $(SOURCE) -<< - diff --git a/graf2d/gpad/inc/TPadPainter.h b/graf2d/gpad/inc/TPadPainter.h index 24c762ed47ede..9b6a6fb6a1c86 100644 --- a/graf2d/gpad/inc/TPadPainter.h +++ b/graf2d/gpad/inc/TPadPainter.h @@ -13,6 +13,7 @@ #define ROOT_TPadPainter #include "TVirtualPadPainter.h" +#include "GuiTypes.h" /* TVirtualPadPainter is an attempt to abstract @@ -24,6 +25,10 @@ or gl pad painter. class TVirtualPad; class TPadPainter : public TVirtualPadPainter { + WinContext_t fWinContext; + Int_t fSetLineWidth = 0; ///< remember set width to optimize some painting + Style_t fSetFillStyle = 0; ///< remember set fill style to optimize painting + public: TPadPainter(); //Final overriders for TVirtualPadPainter pure virtual functions. @@ -71,6 +76,12 @@ class TPadPainter : public TVirtualPadPainter { void SetMarkerStyle(Style_t mstyle) override; void SetMarkerSize(Size_t msize) override; + //Overall attributes + void SetAttFill(const TAttFill &att) override; + void SetAttLine(const TAttLine &att) override; + void SetAttMarker(const TAttMarker &att) override; + void SetAttText(const TAttText &att) override; + //2. "Off-screen management" part. Int_t CreateDrawable(UInt_t w, UInt_t h) override; void ClearDrawable() override; @@ -78,6 +89,9 @@ class TPadPainter : public TVirtualPadPainter { void CopyDrawable(Int_t device, Int_t px, Int_t py) override; void DestroyDrawable(Int_t device) override; void SelectDrawable(Int_t device) override; + void UpdateDrawable(Int_t mode) override; + void SetDrawMode(Int_t device, Int_t mode) override; + //TASImage support (noop for a non-gl pad). void DrawPixels(const unsigned char *pixelData, UInt_t width, UInt_t height, @@ -116,6 +130,7 @@ class TPadPainter : public TVirtualPadPainter { Bool_t IsCocoa() const override; + Bool_t IsSupportAlpha() const override; private: //Let's make this clear: diff --git a/graf2d/gpad/inc/TPadPainterPS.h b/graf2d/gpad/inc/TPadPainterPS.h index 1a463af1ff214..9a37ec658d9ab 100644 --- a/graf2d/gpad/inc/TPadPainterPS.h +++ b/graf2d/gpad/inc/TPadPainterPS.h @@ -121,6 +121,9 @@ class TPadPainterPS : public TVirtualPadPainter { void OnPad(TVirtualPad *pad) override { fPad = pad; } + TVirtualPS *GetPS() const override { return fPS; } + + private: //Let's make this clear: TPadPainterPS(const TPadPainterPS &) = delete; diff --git a/graf2d/gpad/src/TCanvas.cxx b/graf2d/gpad/src/TCanvas.cxx index 3f93d1e548dd6..25964427aaf91 100644 --- a/graf2d/gpad/src/TCanvas.cxx +++ b/graf2d/gpad/src/TCanvas.cxx @@ -610,14 +610,13 @@ void TCanvas::Build() } else { //normal mode with a screen window // Set default physical canvas attributes - //Should be done via gVirtualX, not via fPainter (at least now). No changes here. - gVirtualX->SelectWindow(fCanvasID); - gVirtualX->SetFillColor(1); //Set color index for fill area - gVirtualX->SetLineColor(1); //Set color index for lines - gVirtualX->SetMarkerColor(1); //Set color index for markers - gVirtualX->SetTextColor(1); //Set color index for text + fPainter->SelectDrawable(fCanvasID); + fPainter->SetAttFill({1, 1001}); //Set color index for fill area + fPainter->SetAttLine({1, 1, 1}); //Set color index for lines + fPainter->SetAttMarker({1, 1, 1}); //Set color index for markers + fPainter->SetAttText({22, 0., 1, 42, 12}); //Set color index for text // Clear workstation - gVirtualX->ClearWindow(); + fPainter->ClearDrawable(); // Set Double Buffer on by default SetDoubleBuffer(1); @@ -627,8 +626,7 @@ void TCanvas::Build() fWindowWidth, fWindowHeight); // Get effective canvas parameters without borders - Int_t dum1, dum2; - gVirtualX->GetGeometry(fCanvasID, dum1, dum2, fCw, fCh); + fCanvasImp->GetCanvasGeometry(fCanvasID, fCw, fCh); fContextMenu = new TContextMenu("ContextMenu"); } @@ -722,8 +720,8 @@ TVirtualPad *TCanvas::cd(Int_t subpadnumber) TPad::cd(subpadnumber); // in case doublebuffer is off, draw directly onto display window - if (!IsBatch() && !IsWeb() && !fDoubleBuffer) - gVirtualX->SelectWindow(fCanvasID);//Ok, does not matter for glpad. + if (!IsBatch() && !IsWeb() && !fDoubleBuffer && fPainter) + fPainter->SelectDrawable(fCanvasID);//Ok, does not matter for glpad. return gPad; } @@ -804,7 +802,9 @@ void TCanvas::Close(Option_t *option) TPad::Close(option); if (!IsBatch() && !IsWeb()) { - gVirtualX->SelectWindow(fCanvasID); //select current canvas + //select current canvas + if (fPainter) + fPainter->SelectDrawable(fCanvasID); DeleteCanvasPainter(); @@ -835,7 +835,7 @@ void TCanvas::Close(Option_t *option) void TCanvas::CopyPixmaps() { if (!IsBatch()) { - CopyPixmap(); + TPad::CopyPixmap(); TPad::CopyPixmaps(); } } @@ -1126,16 +1126,13 @@ void TCanvas::ExecuteEvent(Int_t event, Int_t px, Int_t py) void TCanvas::FeedbackMode(Bool_t set) { - if (IsWeb()) + if (IsWeb() || (fCanvasID == -1)) return; - if (set) { - SetDoubleBuffer(0); // turn off double buffer mode - gVirtualX->SetDrawMode(TVirtualX::kInvert); // set the drawing mode to XOR mode - } else { - SetDoubleBuffer(1); // turn on double buffer mode - gVirtualX->SetDrawMode(TVirtualX::kCopy); // set drawing mode back to normal (copy) mode - } + SetDoubleBuffer(set ? 0 : 1); // switch double buffer + + if (fPainter) + fPainter->SetDrawMode(fCanvasID, set ? TVirtualX::kInvert : TVirtualX::kCopy); } //////////////////////////////////////////////////////////////////////////////// @@ -1148,10 +1145,10 @@ void TCanvas::Flush() TContext ctxt(this, kTRUE); if (!IsBatch()) { if (!UseGL() || fGLDevice == -1) { - gVirtualX->SelectWindow(fCanvasID); + fPainter->SelectDrawable(fCanvasID); gPad = ctxt.GetSaved(); //don't do cd() because than also the pixmap is changed CopyPixmaps(); - gVirtualX->UpdateWindow(1); + fPainter->UpdateDrawable(1); } else { TVirtualPS *tvps = gVirtualPS; gVirtualPS = nullptr; @@ -1312,8 +1309,7 @@ void TCanvas::HandleInput(EEventType event, Int_t px, Int_t py) gPad = fSelectedPad; fSelected->ExecuteEvent(event, px, py); - if (!IsWeb()) - gVirtualX->Update(); + fCanvasImp->UpdateDisplay(0); if (fSelected && !fSelected->InheritsFrom(TAxis::Class())) { Bool_t resize = kFALSE; if (fSelected->InheritsFrom(TBox::Class())) @@ -1666,7 +1662,8 @@ void TCanvas::ProcessedEvent(Int_t event, Int_t x, Int_t y, TObject *obj) void TCanvas::Resize(Option_t *) { - if (fCanvasID == -1) return; + if (fCanvasID == -1) + return; if (!gROOT->IsLineProcessing() && !gVirtualX->IsCmdThread()) { gInterpreter->Execute(this, IsA(), "Resize", ""); @@ -1678,16 +1675,17 @@ void TCanvas::Resize(Option_t *) TContext ctxt(this, kTRUE); if (!IsBatch() && !IsWeb()) { - gVirtualX->SelectWindow(fCanvasID); //select current canvas - gVirtualX->ResizeWindow(fCanvasID); //resize canvas and off-screen buffer + // SL: do we need it here? + fPainter->SelectDrawable(fCanvasID); //select current canvas for painting??? + + fCanvasImp->ResizeCanvasWindow(fCanvasID); //resize canvas and off-screen buffer // Get effective window parameters including menubar and borders fCanvasImp->GetWindowGeometry(fWindowTopX, fWindowTopY, fWindowWidth, fWindowHeight); // Get effective canvas parameters without borders - Int_t dum1, dum2; - gVirtualX->GetGeometry(fCanvasID, dum1, dum2, fCw, fCh); + fCanvasImp->GetCanvasGeometry(fCanvasID, fCw, fCh); } if (fXsizeUser && fYsizeUser) { @@ -1732,7 +1730,7 @@ void TCanvas::Resize(Option_t *) fYsizeReal = fXsizeReal*Double_t(fCh)/Double_t(fCw); } -//*-*- Loop on all pads to recompute conversion coefficients + //*-*- Loop on all pads to recompute conversion coefficients TPad::ResizePad(); } @@ -1965,8 +1963,8 @@ void TCanvas::SetCanvasSize(UInt_t ww, UInt_t wh) void TCanvas::SetCursor(ECursor cursor) { - if (!IsBatch() && !IsWeb()) - gVirtualX->SetCursor(fCanvasID, cursor); + if (!IsBatch() && !IsWeb() && fCanvasID != -1) + fPainter->SetCursor(fCanvasID, cursor); } //////////////////////////////////////////////////////////////////////////////// @@ -1977,7 +1975,8 @@ void TCanvas::SetDoubleBuffer(Int_t mode) if (IsBatch() || IsWeb()) return; fDoubleBuffer = mode; - gVirtualX->SetDoubleBuffer(fCanvasID, mode); + if (fCanvasID != -1) + fPainter->SetDoubleBuffer(fCanvasID, mode); // depending of the buffer mode set the drawing window to either // the canvas pixmap or to the canvas on-screen window @@ -2476,8 +2475,10 @@ void TCanvas::ToggleToolTips() Bool_t TCanvas::SupportAlpha() { - return gPad && (gVirtualX->InheritsFrom("TGQuartz") || - (gPad->GetGLDevice() != -1) || (gPad->GetCanvas() && gPad->GetCanvas()->IsWeb())); + if (gPad) + if (auto pp = gPad->GetPainter()) + return pp->IsSupportAlpha(); + return kFALSE; } extern "C" void ROOT_TCanvas_Update(void* TheCanvas) { @@ -2597,7 +2598,7 @@ void TCanvas::CreatePainter() { //Even for batch mode painter is still required, just to delegate //some calls to batch "virtual X". - if (!UseGL() || fBatch) { + if (!UseGL() || fBatch || IsWeb()) { fPainter = nullptr; if (fCanvasImp) fPainter = fCanvasImp->CreatePadPainter(); if (!fPainter) fPainter = new TPadPainter; // Do not need plugin manager for this! @@ -2632,7 +2633,7 @@ Bool_t TCanvas::EnsurePSPainter(Bool_t create, TVirtualPadPainter *&oldp) return kFALSE; } - if (!gVirtualPS || !IsBatch()) + if (!gVirtualPS /* || !IsBatch() */) return kFALSE; diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index ca927656b89a6..a91c9deda7c7c 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -46,10 +46,12 @@ #include "TMethod.h" #include "TDataType.h" #include "TFrame.h" +#include "TWbox.h" #include "TExec.h" #include "TDatime.h" #include "TColor.h" #include "TCanvas.h" +#include "TCanvasImp.h" #include "TPluginManager.h" #include "TEnv.h" #include "TImage.h" @@ -1146,10 +1148,9 @@ void TPad::CopyPixmap() int px, py; XYtoAbsPixel(fX1, fY2, px, py); - if (fPixmapID != -1 && GetPainter()) - GetPainter()->CopyDrawable(fPixmapID, px, py); - - if (this == gPad) HighLight(gPad->GetHighLightColor()); + if (fPixmapID != -1) + if (auto pp = GetPainter()) + pp->CopyDrawable(fPixmapID, px, py); } //////////////////////////////////////////////////////////////////////////////// @@ -1157,14 +1158,18 @@ void TPad::CopyPixmap() void TPad::CopyPixmaps() { - if (!fPrimitives) fPrimitives = new TList; - TIter next(GetListOfPrimitives()); + if (!fPrimitives) + fPrimitives = new TList; + TIter next(GetListOfPrimitives()); while (auto obj = next()) { - if (obj->InheritsFrom(TPad::Class())) { - ((TPad*)obj)->CopyPixmap(); - ((TPad*)obj)->CopyPixmaps(); + if (auto pad = dynamic_cast(obj)) { + pad->CopyPixmap(); + pad->CopyPixmaps(); } } + + if (this == gPad) + HighLight(GetHighLightColor()); } //////////////////////////////////////////////////////////////////////////////// @@ -1750,44 +1755,58 @@ void TPad::DrawClassObject(const TObject *classobj, Option_t *option) void TPad::DrawCrosshair() { - if (!gPad || (gPad->GetEvent() == kMouseEnter)) return; + if (!gPad || (gPad->GetEvent() == kMouseEnter)) + return; TPad *cpad = (TPad*)gPad; TCanvas *canvas = cpad->GetCanvas(); + // switch off double buffer and select canvas drawable canvas->FeedbackMode(kTRUE); + auto pp = GetPainter(); + //erase old position and draw a line at current position - Int_t pxmin,pxmax,pymin,pymax,px,py; -#ifndef R__HAS_COCOA - Int_t pxold = fCrosshairPos%10000; - Int_t pyold = fCrosshairPos/10000; -#endif // R__HAS_COCOA - px = cpad->GetEventX(); - py = cpad->GetEventY()+1; + Double_t umin, umax, vmin, vmax, u, v; + Int_t px = cpad->GetEventX(); + Int_t py = cpad->GetEventY() + 1; if (canvas->GetCrosshair() > 1) { //crosshair only in the current pad - pxmin = cpad->XtoAbsPixel(fX1); - pxmax = cpad->XtoAbsPixel(fX2); - pymin = cpad->YtoAbsPixel(fY1); - pymax = cpad->YtoAbsPixel(fY2); + umin = GetAbsXlowNDC(); + umax = GetAbsXlowNDC() + GetAbsWNDC(); + vmin = GetAbsYlowNDC(); + vmax = GetAbsYlowNDC() + GetAbsHNDC(); } else { //default; crosshair spans the full canvas - pxmin = 0; - pxmax = canvas->GetWw(); - pymin = 0; - pymax = cpad->GetWh(); - } -#ifndef R__HAS_COCOA - // Not needed, no XOR with Cocoa. - if(pxold) gVirtualX->DrawLine(pxold,pymin,pxold,pymax); - if(pyold) gVirtualX->DrawLine(pxmin,pyold,pxmax,pyold); -#endif // R__HAS_COCOA + umin = 0; + umax = 1; + vmin = 0; + vmax = 1; + } + + TContext ctxt(canvas); + + pp->SetAttLine({1,1,1}); + + if ((fCrosshairPos != 0) && !pp->IsCocoa()) { + // xor does not supported on Cocoa, implemented differently + Int_t pxold = fCrosshairPos % 10000; + Int_t pyold = fCrosshairPos / 10000; + u = 1. * pxold / canvas->GetWw(); + v = 1. - 1. * pyold / canvas->GetWh(); + pp->DrawLineNDC(umin, v, umax, v); + pp->DrawLineNDC(u, vmin, u, vmax); + } + if (cpad->GetEvent() == kButton1Down || cpad->GetEvent() == kButton1Up || cpad->GetEvent() == kMouseLeave) { fCrosshairPos = 0; return; } - gVirtualX->DrawLine(px,pymin,px,pymax); - gVirtualX->DrawLine(pxmin,py,pxmax,py); + + u = 1. * px / canvas->GetWw(); + v = 1. - 1. * py / canvas->GetWh(); + pp->DrawLineNDC(umin, v, umax, v); + pp->DrawLineNDC(u, vmin, u, vmax); + fCrosshairPos = px + 10000*py; } @@ -2002,8 +2021,39 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) return; default: break; + } + if (newcode) + return; + + auto pp = GetPainter(); + + auto action = [this,pp,parent](Bool_t paint, Int_t _x1, Int_t _y1, Int_t _x2, Int_t _y2) { + auto x1 = AbsPixeltoX(_x1); + auto y1 = AbsPixeltoY(_y1); + auto x2 = AbsPixeltoX(_x2); + auto y2 = AbsPixeltoY(_y2); + if (paint) { + pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); + } else { + // Get parent corners pixels coordinates + Int_t parentpx1 = fMother->XtoAbsPixel(parent->GetX1()); + Int_t parentpx2 = fMother->XtoAbsPixel(parent->GetX2()); + Int_t parentpy1 = fMother->YtoAbsPixel(parent->GetY1()); + Int_t parentpy2 = fMother->YtoAbsPixel(parent->GetY2()); + + // Get pad new corners pixels coordinates + Int_t apx1 = XtoAbsPixel(x1); if (apx1 < parentpx1) {apx1 = parentpx1; } + Int_t apx2 = XtoAbsPixel(x2); if (apx2 > parentpx2) {apx2 = parentpx2; } + Int_t apy1 = YtoAbsPixel(y1); if (apy1 > parentpy1) {apy1 = parentpy1; } + Int_t apy2 = YtoAbsPixel(y2); if (apy2 < parentpy2) {apy2 = parentpy2; } + + // Compute new pad positions in the NDC space of parent + fXlowNDC = Double_t(apx1 - parentpx1)/Double_t(parentpx2 - parentpx1); + fYlowNDC = Double_t(apy1 - parentpy1)/Double_t(parentpy2 - parentpy1); + fWNDC = Double_t(apx2 - apx1)/Double_t(parentpx2 - parentpx1); + fHNDC = Double_t(apy2 - apy1)/Double_t(parentpy2 - parentpy1); } - if (newcode) return; + }; switch (event) { @@ -2017,14 +2067,7 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) fXUpNDC = fXlowNDC + fWNDC; fYUpNDC = fYlowNDC + fHNDC; - - GetPainter()->SetLineColor(-1); - TAttLine::Modify(); //Change line attributes only if necessary - if (GetFillColor()) - GetPainter()->SetLineColor(GetFillColor()); - else - GetPainter()->SetLineColor(1); - GetPainter()->SetLineWidth(2); + pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); // No break !!! @@ -2126,11 +2169,9 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) SetCursor(kCross); } - fResizing = kFALSE; - if (pA || pB || pC || pD || pTop || pL || pR || pBot) - fResizing = kTRUE; + fResizing = pA || pB || pC || pD || pTop || pL || pR || pBot; - if (!pA && !pB && !pC && !pD && !pTop && !pL && !pR && !pBot && !pINSIDE) + if (!fResizing && !pINSIDE) SetCursor(kCross); break; @@ -2142,7 +2183,7 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) wx = wy = 0; if (pA) { - if (!ropaque) gVirtualX->DrawBox(pxold, pyt, pxt, pyold, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxold, pyt, pxt, pyold); if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; } if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; } if (px < pxlp) { px = pxlp; wx = px; } @@ -2160,10 +2201,10 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) wx = wy = 0; } - if (!ropaque) gVirtualX->DrawBox(px, pyt, pxt, py, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px, pyt, pxt, py); } if (pB) { - if (!ropaque) gVirtualX->DrawBox(pxl , pyt, pxold, pyold, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxl, pyt, pxold, pyold); if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; } if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; } if (px > pxtp) { px = pxtp; wx = px; } @@ -2181,10 +2222,10 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) wx = wy = 0; } - if (!ropaque) gVirtualX->DrawBox(pxl , pyt, px , py, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxl, pyt, px, py); } if (pC) { - if (!ropaque) gVirtualX->DrawBox(pxl , pyl, pxold, pyold, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxl, pyl, pxold, pyold); if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; } if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; } if (px > pxtp) { px = pxtp; wx = px; } @@ -2202,10 +2243,10 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) wx = wy = 0; } - if (!ropaque) gVirtualX->DrawBox(pxl, pyl, px, py, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxl, pyl, px, py); } if (pD) { - if (!ropaque) gVirtualX->DrawBox(pxold, pyold, pxt, pyl, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxold, pyold, pxt, pyl); if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; } if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; } if (px < pxlp) { px = pxlp; wx = px; } @@ -2223,10 +2264,10 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) wx = wy = 0; } - if (!ropaque) gVirtualX->DrawBox(px, py, pxt, pyl, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px, py, pxt, pyl); } if (pTop) { - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); py2 += py - pyold; if (py2 > py1-kMinSize) { py2 = py1-kMinSize; wy = py2; } if (py2 < py2p) { py2 = py2p; wy = py2; } @@ -2239,10 +2280,10 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) else px2 = npx2; } - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); } if (pBot) { - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); py1 += py - pyold; if (py1 < py2+kMinSize) { py1 = py2+kMinSize; wy = py1; } if (py1 > py1p) { py1 = py1p; wy = py1; } @@ -2255,10 +2296,10 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) else px2 = npx2; } - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); } if (pL) { - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); px1 += px - pxold; if (px1 > px2-kMinSize) { px1 = px2-kMinSize; wx = px1; } if (px1 < px1p) { px1 = px1p; wx = px1; } @@ -2272,10 +2313,10 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) else py2 = npy2; } - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); } if (pR) { - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); px2 += px - pxold; if (px2 < px1+kMinSize) { px2 = px1+kMinSize; wx = px2; } if (px2 > px2p) { px2 = px2p; wx = px2; } @@ -2289,10 +2330,10 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) else py2 = npy2; } - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); } if (pINSIDE) { - if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); // draw the old box + if (!opaque) action(kTRUE, px1, py1, px2, py2); // draw the old box Int_t dx = px - pxold; Int_t dy = py - pyold; px1 += dx; py1 += dy; px2 += dx; py2 += dy; @@ -2300,72 +2341,30 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (px2 > px2p) { dx = px2 - px2p; px1 -= dx; px2 -= dx; wx = px-dx; } if (py1 > py1p) { dy = py1 - py1p; py1 -= dy; py2 -= dy; wy = py-dy; } if (py2 < py2p) { dy = py2p - py2; py1 += dy; py2 += dy; wy = py+dy; } - if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); // draw the new box + if (!opaque) action(kTRUE, px1, py1, px2, py2); // draw the new box } if (wx || wy) { if (wx) px = wx; if (wy) py = wy; - gVirtualX->Warp(px, py); + GetCanvasImp()->Warp(px, py); } pxold = px; pyold = py; - Double_t x1, y1, x2, y2; - x1 = x2 = y1 = y2 = 0; - if ((!fResizing && opaque) || (fResizing && ropaque)) { - if (pA) { - x1 = AbsPixeltoX(pxold); - y1 = AbsPixeltoY(pyt); - x2 = AbsPixeltoX(pxt); - y2 = AbsPixeltoY(pyold); - } - if (pB) { - x1 = AbsPixeltoX(pxl); - y1 = AbsPixeltoY(pyt); - x2 = AbsPixeltoX(pxold); - y2 = AbsPixeltoY(pyold); - } - if (pC) { - x1 = AbsPixeltoX(pxl); - y1 = AbsPixeltoY(pyold); - x2 = AbsPixeltoX(pxold); - y2 = AbsPixeltoY(pyl); - } - if (pD) { - x1 = AbsPixeltoX(pxold); - y1 = AbsPixeltoY(pyold); - x2 = AbsPixeltoX(pxt); - y2 = AbsPixeltoY(pyl); - } - if (pTop || pBot || pL || pR || pINSIDE) { - x1 = AbsPixeltoX(px1); - y1 = AbsPixeltoY(py1); - x2 = AbsPixeltoX(px2); - y2 = AbsPixeltoY(py2); - } - if (px != pxorg || py != pyorg) { - - // Get parent corners pixels coordinates - Int_t parentpx1 = fMother->XtoAbsPixel(parent->GetX1()); - Int_t parentpx2 = fMother->XtoAbsPixel(parent->GetX2()); - Int_t parentpy1 = fMother->YtoAbsPixel(parent->GetY1()); - Int_t parentpy2 = fMother->YtoAbsPixel(parent->GetY2()); - - // Get pad new corners pixels coordinates - Int_t apx1 = XtoAbsPixel(x1); if (apx1 < parentpx1) {apx1 = parentpx1; } - Int_t apx2 = XtoAbsPixel(x2); if (apx2 > parentpx2) {apx2 = parentpx2; } - Int_t apy1 = YtoAbsPixel(y1); if (apy1 > parentpy1) {apy1 = parentpy1; } - Int_t apy2 = YtoAbsPixel(y2); if (apy2 < parentpy2) {apy2 = parentpy2; } - - // Compute new pad positions in the NDC space of parent - fXlowNDC = Double_t(apx1 - parentpx1)/Double_t(parentpx2 - parentpx1); - fYlowNDC = Double_t(apy1 - parentpy1)/Double_t(parentpy2 - parentpy1); - fWNDC = Double_t(apx2 - apx1)/Double_t(parentpx2 - parentpx1); - fHNDC = Double_t(apy2 - apy1)/Double_t(parentpy2 - parentpy1); + if (pA) + action(kFALSE, pxold, pyt, pxt, pyold); + if (pB) + action(kFALSE, pxl, pyt, pxold, pyold); + if (pC) + action(kFALSE, pxl, pyold, pxold, pyl); + if (pD) + action(kFALSE, pxold, pyold, pxt, pyl); + if (pTop || pBot || pL || pR || pINSIDE) + action(kFALSE, px1, py1, px2, py2); } // Reset pad parameters and recompute conversion coefficients @@ -2396,70 +2395,25 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (opaque||ropaque) { ShowGuidelines(this, event); } else { - x1 = x2 = y1 = y2 = 0; - - if (pA) { - x1 = AbsPixeltoX(pxold); - y1 = AbsPixeltoY(pyt); - x2 = AbsPixeltoX(pxt); - y2 = AbsPixeltoY(pyold); - } - if (pB) { - x1 = AbsPixeltoX(pxl); - y1 = AbsPixeltoY(pyt); - x2 = AbsPixeltoX(pxold); - y2 = AbsPixeltoY(pyold); - } - if (pC) { - x1 = AbsPixeltoX(pxl); - y1 = AbsPixeltoY(pyold); - x2 = AbsPixeltoX(pxold); - y2 = AbsPixeltoY(pyl); - } - if (pD) { - x1 = AbsPixeltoX(pxold); - y1 = AbsPixeltoY(pyold); - x2 = AbsPixeltoX(pxt); - y2 = AbsPixeltoY(pyl); - } - if (pTop || pBot || pL || pR || pINSIDE) { - x1 = AbsPixeltoX(px1); - y1 = AbsPixeltoY(py1); - x2 = AbsPixeltoX(px2); - y2 = AbsPixeltoY(py2); - } + if (pA) + action(kFALSE, pxold, pyt, pxt, pyold); + if (pB) + action(kFALSE, pxl, pyt, pxold, pyold); + if (pC) + action(kFALSE, pxl, pyold, pxold, pyl); + if (pD) + action(kFALSE, pxold, pyold, pxt, pyl); + if (pTop || pBot || pL || pR || pINSIDE) + action(kFALSE, px1, py1, px2, py2); if (pA || pB || pC || pD || pTop || pL || pR || pBot) Modified(kTRUE); - gVirtualX->SetLineColor(-1); - gVirtualX->SetLineWidth(-1); - - if (px != pxorg || py != pyorg) { - - // Get parent corners pixels coordinates - Int_t parentpx1 = fMother->XtoAbsPixel(parent->GetX1()); - Int_t parentpx2 = fMother->XtoAbsPixel(parent->GetX2()); - Int_t parentpy1 = fMother->YtoAbsPixel(parent->GetY1()); - Int_t parentpy2 = fMother->YtoAbsPixel(parent->GetY2()); - - // Get pad new corners pixels coordinates - Int_t apx1 = XtoAbsPixel(x1); if (apx1 < parentpx1) {apx1 = parentpx1; } - Int_t apx2 = XtoAbsPixel(x2); if (apx2 > parentpx2) {apx2 = parentpx2; } - Int_t apy1 = YtoAbsPixel(y1); if (apy1 > parentpy1) {apy1 = parentpy1; } - Int_t apy2 = YtoAbsPixel(y2); if (apy2 < parentpy2) {apy2 = parentpy2; } - - // Compute new pad positions in the NDC space of parent - fXlowNDC = Double_t(apx1 - parentpx1)/Double_t(parentpx2 - parentpx1); - fYlowNDC = Double_t(apy1 - parentpy1)/Double_t(parentpy2 - parentpy1); - fWNDC = Double_t(apx2 - apx1)/Double_t(parentpx2 - parentpx1); - fHNDC = Double_t(apy2 - apy1)/Double_t(parentpy2 - parentpy1); - } + pp->SetAttLine({-1, 1, -1}); // Reset pad parameters and recompute conversion coefficients ResizePad(); - // emit signal RangeChanged(); } @@ -2472,7 +2426,7 @@ void TPad::ExecuteEvent(Int_t event, Int_t px, Int_t py) while (true) { px = py = 0; - event = gVirtualX->RequestLocator(1, 1, px, py); + event = GetCanvasImp()->RequestLocator(px, py); ExecuteEvent(kButton1Motion, px, py); @@ -2513,7 +2467,7 @@ void TPad::ExecuteEventAxis(Int_t event, Int_t px, Int_t py, TAxis *axis) TView *view = GetView(); static Int_t axisNumber; static Double_t ratio1, ratio2; - static Int_t px1old, py1old, px2old, py2old; + static Double_t px1old, py1old, px2old, py2old; Int_t nbd, inc, bin1, bin2, first, last; Double_t temp, xmin,xmax; Bool_t opaque = gPad->OpaqueMoving(); @@ -2531,56 +2485,53 @@ void TPad::ExecuteEventAxis(Int_t event, Int_t px, Int_t py, TAxis *axis) kCont4 = kTRUE; } + auto pp = GetPainter(); + switch (event) { case kButton1Down: axisNumber = 1; - if (!strcmp(axis->GetName(),"xaxis")) { - axisNumber = 1; - if (!IsVertical()) axisNumber = 2; - } - if (!strcmp(axis->GetName(),"yaxis")) { - axisNumber = 2; - if (!IsVertical()) axisNumber = 1; - } - if (!strcmp(axis->GetName(),"zaxis")) { + if (!strcmp(axis->GetName(),"xaxis")) + axisNumber = IsVertical() ? 1 : 2; + if (!strcmp(axis->GetName(),"yaxis")) + axisNumber = IsVertical() ? 2 : 1; + if (!strcmp(axis->GetName(),"zaxis")) axisNumber = 3; - } if (view) { view->GetDistancetoAxis(axisNumber, px, py, ratio1); } else { if (axisNumber == 1) { ratio1 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin()); - px1old = XtoAbsPixel(GetUxmin()+ratio1*(GetUxmax() - GetUxmin())); - py1old = YtoAbsPixel(GetUymin()); + px1old = GetUxmin()+ratio1*(GetUxmax() - GetUxmin()); + py1old = GetUymin(); px2old = px1old; - py2old = YtoAbsPixel(GetUymax()); + py2old = GetUymax(); } else if (axisNumber == 2) { ratio1 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin()); - py1old = YtoAbsPixel(GetUymin()+ratio1*(GetUymax() - GetUymin())); - px1old = XtoAbsPixel(GetUxmin()); - px2old = XtoAbsPixel(GetUxmax()); + py1old = GetUymin()+ratio1*(GetUymax() - GetUymin()); + px1old = GetUxmin(); + px2old = GetUxmax(); py2old = py1old; } else { ratio1 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin()); - py1old = YtoAbsPixel(GetUymin()+ratio1*(GetUymax() - GetUymin())); - px1old = XtoAbsPixel(GetUxmax()); + py1old = GetUymin()+ratio1*(GetUymax() - GetUymin()); + px1old = GetUxmax(); px2old = XtoAbsPixel(GetX2()); py2old = py1old; } if (!opaque) { - gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow); + pp->DrawBox(px1old, py1old, px2old, py2old, TVirtualPadPainter::kHollow); } else { if (axisNumber == 1) { - zbx1 = AbsPixeltoX(px1old); - zbx2 = AbsPixeltoX(px2old); + zbx1 = px1old; + zbx2 = px2old; zby1 = GetUymin(); zby2 = GetUymax(); } else if (axisNumber == 2) { zbx1 = GetUxmin(); zbx2 = GetUxmax(); - zby1 = AbsPixeltoY(py1old); - zby2 = AbsPixeltoY(py2old); + zby1 = py1old; + zby2 = py2old; } if (GetLogx()) { zbx1 = TMath::Power(10,zbx1); @@ -2593,42 +2544,46 @@ void TPad::ExecuteEventAxis(Int_t event, Int_t px, Int_t py, TAxis *axis) zoombox = std::make_unique(zbx1, zby1, zbx2, zby2); Int_t ci = TColor::GetColor("#7d7dff"); TColor *zoomcolor = gROOT->GetColor(ci); - if (!TCanvas::SupportAlpha() || !zoomcolor) zoombox->SetFillStyle(3002); - else zoomcolor->SetAlpha(0.5); + if (!pp->IsSupportAlpha() || !zoomcolor) + zoombox->SetFillStyle(3002); + else + zoomcolor->SetAlpha(0.5); zoombox->SetFillColor(ci); zoombox->Draw(); gPad->Modified(); gPad->Update(); } } - if (!opaque) gVirtualX->SetLineColor(-1); + if (!opaque) + pp->SetAttLine({-1, 1, 1}); // No break !!! case kButton1Motion: if (view) { view->GetDistancetoAxis(axisNumber, px, py, ratio2); } else { - if (!opaque) gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow); + if (!opaque) + pp->DrawBox(px1old, py1old, px2old, py2old, TVirtualPadPainter::kHollow); if (axisNumber == 1) { ratio2 = (AbsPixeltoX(px) - GetUxmin())/(GetUxmax() - GetUxmin()); - px2old = XtoAbsPixel(GetUxmin()+ratio2*(GetUxmax() - GetUxmin())); + px2old = GetUxmin()+ratio2*(GetUxmax() - GetUxmin()); } else { ratio2 = (AbsPixeltoY(py) - GetUymin())/(GetUymax() - GetUymin()); - py2old = YtoAbsPixel(GetUymin()+ratio2*(GetUymax() - GetUymin())); + py2old = GetUymin()+ratio2*(GetUymax() - GetUymin()); } if (!opaque) { - gVirtualX->DrawBox(px1old, py1old, px2old, py2old, TVirtualX::kHollow); + pp->DrawBox(px1old, py1old, px2old, py2old, TVirtualPadPainter::kHollow); } else { if (axisNumber == 1) { - zbx1 = AbsPixeltoX(px1old); - zbx2 = AbsPixeltoX(px2old); + zbx1 = px1old; + zbx2 = px2old; zby1 = GetUymin(); zby2 = GetUymax(); } else if (axisNumber == 2) { zbx1 = GetUxmin(); zbx2 = GetUxmax(); - zby1 = AbsPixeltoY(py1old); - zby2 = AbsPixeltoY(py2old); + zby1 = py1old; + zby2 = py2old; } if (GetLogx()) { zbx1 = TMath::Power(10,zbx1); @@ -2675,7 +2630,8 @@ void TPad::ExecuteEventAxis(Int_t event, Int_t px, Int_t py, TAxis *axis) if (bin2>bin1) { axis->SetRange(bin1,bin2); } - if (resetAxisRange) axis->ResetBit(TAxis::kAxisRange); + if (resetAxisRange) + axis->ResetBit(TAxis::kAxisRange); if (bin2>bin1) { gPad->Modified(); gPad->Update(); @@ -2831,7 +2787,7 @@ void TPad::ExecuteEventAxis(Int_t event, Int_t px, Int_t py, TAxis *axis) } } if (!opaque) { - gVirtualX->SetLineColor(-1); + pp->SetAttLine({-1, 1, 1}); } else { if (zoombox) { zoombox.reset(); @@ -3828,99 +3784,20 @@ void TPad::PaintBorder(Color_t color, Bool_t /* tops */) if (IsTransparent()) return; - // then paint 3d frame (depending on bordermode) - // Paint a 3D frame around the pad. - if (fBorderMode == 0) return; - Int_t bordersize = fBorderSize; - if (bordersize <= 0) - bordersize = 2; - - Double_t ww = GetWw(), wh = GetWh(); - - if (!pp->IsNative()) { - // SL: need to calculate page size to get real coordiantes for border - // TODO: Code can be removed if border not need to be exact pixel size - Float_t xsize = 20, ysize = 26; - gStyle->GetPaperSize(xsize, ysize); - Double_t ratio = wh/ww; - if (xsize * ratio > ysize) - xsize = ysize/ratio; - else - ysize = xsize*ratio; - ww = 72 / 2.54 * xsize; - wh = 72 / 2.54 * ysize; - } - - const Double_t realBsX = bordersize / (GetAbsWNDC() * ww) * (fX2 - fX1); - const Double_t realBsY = bordersize / (GetAbsHNDC() * wh) * (fY2 - fY1); - - - // GetColorDark() and GetColorBright() use GetFillColor() - Color_t oldfillcolor = pp->GetFillColor(); - Color_t light = !color ? 0 : TColor::GetColorBright(color); - Color_t dark = !color ? 0 : TColor::GetColorDark(color); - Double_t xl, xt, yl, yt; - - // Compute real left bottom & top right of the box in pixels - if (XtoPixel(fX1) < XtoPixel(fX2)) { - xl = fX1; - xt = fX2; - } else { - xl = fX2; - xt = fX1; - } - if (YtoPixel(fY1) > YtoPixel(fY2)) { - yl = fY1; - yt = fY2; - } else { - yl = fY2; - yt = fY1; - } - - Double_t frameXs[7] = {}, frameYs[7] = {}; - - // Draw top&left part of the box - frameXs[0] = xl; frameYs[0] = yl; - frameXs[1] = xl + realBsX; frameYs[1] = yl + realBsY; - frameXs[2] = frameXs[1]; frameYs[2] = yt - realBsY; - frameXs[3] = xt - realBsX; frameYs[3] = frameYs[2]; - frameXs[4] = xt; frameYs[4] = yt; - frameXs[5] = xl; frameYs[5] = yt; - frameXs[6] = xl; frameYs[6] = yl; - - pp->SetFillColor(fBorderMode == -1 ? dark : light); - pp->DrawFillArea(7, frameXs, frameYs); - - // Draw bottom&right part of the box - frameXs[0] = xl; frameYs[0] = yl; - frameXs[1] = xl + realBsX; frameYs[1] = yl + realBsY; - frameXs[2] = xt - realBsX; frameYs[2] = frameYs[1]; - frameXs[3] = frameXs[2]; frameYs[3] = yt - realBsY; - frameXs[4] = xt; frameYs[4] = yt; - frameXs[5] = xt; frameYs[5] = yl; - frameXs[6] = xl; frameYs[6] = yl; - - pp->SetFillColor(fBorderMode == -1 ? light : dark); - pp->DrawFillArea(7, frameXs, frameYs); - - // If this pad is a button, highlight it - if (InheritsFrom(TButton::Class()) && fBorderMode == -1) { - if (TestBit(kFraming)) { // bit set in TButton::SetFraming - Color_t oldlinecolor = pp->GetLineColor(); - pp->SetLineColor(GetFillColor() != 2 ? 2 : 4); - pp->DrawBox(xl + realBsX, yl + realBsY, xt - realBsX, yt - realBsY, TVirtualPadPainter::kHollow); - pp->SetLineColor(oldlinecolor); - } - } - pp->SetFillColor(oldfillcolor); + // then paint 3d frame (depending on bordermode) + // Paint a 3D frame around the pad. - // No need to use PaintBorderPS, it is already performed via pad painter done! + TWbox box; + box.SetFillColor(color); + box.SetFillStyle(GetFillStyle()); + TAttLine::Copy(box); - //if (tops) - // PaintBorderPS(xl, yl, xt, yt, fBorderMode, bordersize, dark, light); + box.PaintBorderOn(this, fX1, fY1, fX2, fY2, + fBorderSize, fBorderMode, + InheritsFrom(TButton::Class()) && fBorderMode == -1 && TestBit(kFraming)); } //////////////////////////////////////////////////////////////////////////////// @@ -4334,13 +4211,16 @@ void TPad::PaintFillAreaHatches(Int_t nn, Double_t *xx, Double_t *yy, Int_t Fill void TPad::PaintHatches(Double_t dy, Double_t angle, Int_t nn, Double_t *xx, Double_t *yy) { - Int_t i, i1, i2, nbi, m, inv; + Int_t i, i1, i2, nbi; Double_t ratiox, ratioy, ymin, ymax, yrot, ycur; const Double_t angr = TMath::Pi()*(180.-angle)/180.; const Double_t epsil = 0.0001; - const Int_t maxnbi = 100; - Double_t xli[maxnbi], yli[maxnbi], xt1, xt2, yt1, yt2; - Double_t ll, x, y, x1, x2, y1, y2, a, b, xi, xip, xin, yi, yip; + + std::vector xli; + std::vector yli; + + Double_t xt1, xt2, yt1, yt2; + Double_t x, y, x1, x2, y1, y2, a, b, xi, xip, xin, yi, yip; Double_t rwxmin = gPad->GetX1(); Double_t rwxmax = gPad->GetX2(); @@ -4382,14 +4262,20 @@ void TPad::PaintHatches(Double_t dy, Double_t angle, while (dy * yindx >= ymin) { ycur = dy * yindx--; nbi = 0; + + xli.clear(); + yli.clear(); + for (i=2; i<=nn+1; i++) { i2 = i; i1 = i-1; if (i == nn+1) i2=1; + x1 = wndc*ratiox*(xx[i1-1]-rwxmin); y1 = hndc*ratioy*(yy[i1-1]-rwymin); x2 = wndc*ratiox*(xx[i2-1]-rwxmin); y2 = hndc*ratioy*(yy[i2-1]-rwymin); + xt1 = cosa*x1-sina*y1; yt1 = sina*x1+cosa*y1; xt2 = cosa*x2-sina*y2; @@ -4406,8 +4292,7 @@ void TPad::PaintHatches(Double_t dy, Double_t angle, } if ((yi <= ycur) && (ycur < yip)) { nbi++; - if (nbi >= maxnbi) return; - xli[nbi-1] = xt1; + xli.push_back(xt1); } continue; } @@ -4416,11 +4301,9 @@ void TPad::PaintHatches(Double_t dy, Double_t angle, if (yt1 == yt2) { if (yt1 == ycur) { nbi++; - if (nbi >= maxnbi) return; - xli[nbi-1] = xt1; + xli.push_back(xt1); nbi++; - if (nbi >= maxnbi) return; - xli[nbi-1] = xt2; + xli.push_back(xt2); } continue; } @@ -4428,6 +4311,7 @@ void TPad::PaintHatches(Double_t dy, Double_t angle, // Other line segment a = (yt1-yt2)/(xt1-xt2); b = (yt2*xt1-xt2*yt1)/(xt1-xt2); + if (xt1 < xt2) { xi = xt1; xip = xt2; @@ -4435,47 +4319,35 @@ void TPad::PaintHatches(Double_t dy, Double_t angle, xi = xt2; xip = xt1; } + xin = (ycur-b)/a; - if ((xi <= xin) && (xin < xip) && - (TMath::Min(yt1,yt2) <= ycur) && - (ycur < TMath::Max(yt1,yt2))) { + + if ((xi <= xin) && (xin < xip) && + (TMath::Min(yt1,yt2) <= ycur) && + (ycur < TMath::Max(yt1,yt2))) { nbi++; - if (nbi >= maxnbi) return; - xli[nbi-1] = xin; + xli.push_back(xin); } } // Sorting of the x coordinates intersections - inv = 0; - m = nbi-1; -L30: - for (i=1; i<=m; i++) { - if (xli[i] < xli[i-1]) { - inv++; - ll = xli[i-1]; - xli[i-1] = xli[i]; - xli[i] = ll; - } - } - m--; - if (inv == 0) goto L50; - inv = 0; - goto L30; + std::sort(xli.begin(), xli.end()); // Draw the hatches -L50: if ((nbi%2 != 0) || (nbi == 0)) continue; for (i=0; iPaintSegments(nbi/2, xli, yli); + + gPad->PaintSegments(nbi/2, xli.data(), yli.data()); } } @@ -5310,7 +5182,7 @@ void TPad::Print(const char *filename, Option_t *option) gPad->GetCanvas()->SetHighLightColor(-1); gPad->Modified(); gPad->Update(); - if (GetPainter()){ + if (GetPainter()) { GetPainter()->SelectDrawable(wid); GetPainter()->SaveImage(this, psname.Data(), gtype); } @@ -5325,9 +5197,9 @@ void TPad::Print(const char *filename, Option_t *option) gPad->GetCanvas()->SetHighLightColor(-1); gPad->Modified(); gPad->Update(); - gVirtualX->Update(1); - gSystem->Sleep(30); // synchronize - if (GetPainter()) GetPainter()->SaveImage(this, psname, gtype); + gPad->GetCanvasImp()->UpdateDisplay(1, kTRUE); + if (GetPainter()) + GetPainter()->SaveImage(this, psname, gtype); if (!gSystem->AccessPathName(psname)) { Info("Print", "file %s has been created", psname.Data()); } diff --git a/graf2d/gpad/src/TPadPainter.cxx b/graf2d/gpad/src/TPadPainter.cxx index 41dd510269633..5ede3a1071d51 100644 --- a/graf2d/gpad/src/TPadPainter.cxx +++ b/graf2d/gpad/src/TPadPainter.cxx @@ -47,13 +47,13 @@ void ConvertPointsAndMergePassX(TVirtualPad *pad, unsigned nPoints, const T *x, void ConvertPointsAndMergeInplacePassY(std::vector &dst); template -void DrawFillAreaAux(TVirtualPad *pad, Int_t nPoints, const T *xs, const T *ys); +void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T *xs, const T *ys, Bool_t close_path); template -void DrawPolyLineAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys); +void DrawPolyLineAux(TVirtualPad *pad, WinContext_t cont, unsigned nPoints, const T *xs, const T *ys); template -void DrawPolyMarkerAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys); +void DrawPolyMarkerAux(TVirtualPad *pad, WinContext_t cont, unsigned nPoints, const T *xs, const T *ys); } @@ -132,6 +132,7 @@ void TPadPainter::SetLineStyle(Style_t lstyle) void TPadPainter::SetLineWidth(Width_t lwidth) { + fSetLineWidth = lwidth; gVirtualX->SetLineWidth(lwidth); } @@ -178,6 +179,7 @@ void TPadPainter::SetFillColor(Color_t fcolor) void TPadPainter::SetFillStyle(Style_t fstyle) { + fSetFillStyle = fstyle; gVirtualX->SetFillStyle(fstyle); } @@ -378,16 +380,22 @@ Bool_t TPadPainter::IsCocoa() const return gVirtualX->InheritsFrom("TGCocoa"); } +//////////////////////////////////////////////////////////////////////////////// +/// Returns true if trasnparent colors are supported + +Bool_t TPadPainter::IsSupportAlpha() const +{ + return gVirtualX->InheritsFrom("TGQuartz"); +} //////////////////////////////////////////////////////////////////////////////// /// Clear the current gVirtualX window. void TPadPainter::ClearDrawable() { - gVirtualX->ClearWindow(); + gVirtualX->ClearWindowW(fWinContext); } - //////////////////////////////////////////////////////////////////////////////// /// Copy a gVirtualX pixmap. @@ -404,6 +412,7 @@ void TPadPainter::DestroyDrawable(Int_t device) { gVirtualX->SelectWindow(device); gVirtualX->ClosePixmap(); + fWinContext = (WinContext_t) 0; } @@ -413,6 +422,23 @@ void TPadPainter::DestroyDrawable(Int_t device) void TPadPainter::SelectDrawable(Int_t device) { gVirtualX->SelectWindow(device); + fWinContext = gVirtualX->GetWindowContext(device); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Call low-level update of selected drawable, redirect to gVirtualX. + +void TPadPainter::UpdateDrawable(Int_t mode) +{ + gVirtualX->UpdateWindowW(fWinContext, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set drawing mode for specified device + +void TPadPainter::SetDrawMode(Int_t device, Int_t mode) +{ + gVirtualX->SetDrawModeW(gVirtualX->GetWindowContext(device), (TVirtualX::EDrawMode) mode); } //////////////////////////////////////////////////////////////////////////////// @@ -423,19 +449,53 @@ void TPadPainter::DrawPixels(const unsigned char * /*pixelData*/, UInt_t /*width { } +//////////////////////////////////////////////////////////////////////////////// +/// Set fill attributes + +void TPadPainter::SetAttFill(const TAttFill &att) +{ + fSetFillStyle = att.GetFillStyle(); + gVirtualX->SetAttFill(fWinContext, att); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set line attributes + +void TPadPainter::SetAttLine(const TAttLine &att) +{ + fSetLineWidth = att.GetLineWidth(); + gVirtualX->SetAttLine(fWinContext, att); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set marker attributes + +void TPadPainter::SetAttMarker(const TAttMarker &att) +{ + gVirtualX->SetAttMarker(fWinContext, att); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set text attributes + +void TPadPainter::SetAttText(const TAttText &att) +{ + gVirtualX->SetAttText(fWinContext, att); +} //////////////////////////////////////////////////////////////////////////////// /// Paint a simple line. void TPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2) { - if (GetLineWidth()<=0) return; + if (fSetLineWidth <= 0) + return; const Int_t px1 = gPad->XtoPixel(x1); const Int_t px2 = gPad->XtoPixel(x2); const Int_t py1 = gPad->YtoPixel(y1); const Int_t py2 = gPad->YtoPixel(y2); - gVirtualX->DrawLine(px1, py1, px2, py2); + gVirtualX->DrawLineW(fWinContext, px1, py1, px2, py2); } @@ -444,13 +504,14 @@ void TPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2) void TPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t v2) { - if (GetLineWidth()<=0) return; + if (fSetLineWidth <= 0) + return; const Int_t px1 = gPad->UtoPixel(u1); const Int_t py1 = gPad->VtoPixel(v1); const Int_t px2 = gPad->UtoPixel(u2); const Int_t py2 = gPad->VtoPixel(v2); - gVirtualX->DrawLine(px1, py1, px2, py2); + gVirtualX->DrawLineW(fWinContext, px1, py1, px2, py2); } @@ -459,7 +520,8 @@ void TPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t v2 void TPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, EBoxMode mode) { - if (GetLineWidth()<=0 && mode == TVirtualPadPainter::kHollow) return; + if (fSetLineWidth <= 0 && mode == TVirtualPadPainter::kHollow) + return; Int_t px1 = gPad->XtoPixel(x1); Int_t px2 = gPad->XtoPixel(x2); @@ -472,7 +534,7 @@ void TPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, EB if (TMath::Abs(py1 - py2) < 1) py1 = py2 + 1; - gVirtualX->DrawBox(px1, py1, px2, py2, (TVirtualX::EBoxMode)mode); + gVirtualX->DrawBoxW(fWinContext, px1, py1, px2, py2, (TVirtualX::EBoxMode)mode); } //////////////////////////////////////////////////////////////////////////////// @@ -485,7 +547,7 @@ void TPadPainter::DrawFillArea(Int_t nPoints, const Double_t *xs, const Double_t return; } - DrawFillAreaAux(gPad, nPoints, xs, ys); + DrawFillAreaAux(gPad, fWinContext, nPoints, xs, ys, fSetFillStyle == 0); } @@ -499,7 +561,7 @@ void TPadPainter::DrawFillArea(Int_t nPoints, const Float_t *xs, const Float_t * return; } - DrawFillAreaAux(gPad, nPoints, xs, ys); + DrawFillAreaAux(gPad, fWinContext, nPoints, xs, ys, fSetFillStyle == 0); } //////////////////////////////////////////////////////////////////////////////// @@ -507,14 +569,15 @@ void TPadPainter::DrawFillArea(Int_t nPoints, const Float_t *xs, const Float_t * void TPadPainter::DrawPolyLine(Int_t n, const Double_t *xs, const Double_t *ys) { - if (GetLineWidth()<=0) return; + if (fSetLineWidth <= 0) + return; if (n < 2) { ::Error("TPadPainter::DrawPolyLine", "invalid number of points"); return; } - DrawPolyLineAux(gPad, n, xs, ys); + DrawPolyLineAux(gPad, fWinContext, n, xs, ys); } @@ -523,14 +586,15 @@ void TPadPainter::DrawPolyLine(Int_t n, const Double_t *xs, const Double_t *ys) void TPadPainter::DrawPolyLine(Int_t n, const Float_t *xs, const Float_t *ys) { - if (GetLineWidth()<=0) return; + if (fSetLineWidth <= 0) + return; if (n < 2) { ::Error("TPadPainter::DrawPolyLine", "invalid number of points"); return; } - DrawPolyLineAux(gPad, n, xs, ys); + DrawPolyLineAux(gPad, fWinContext, n, xs, ys); } @@ -539,7 +603,8 @@ void TPadPainter::DrawPolyLine(Int_t n, const Float_t *xs, const Float_t *ys) void TPadPainter::DrawPolyLineNDC(Int_t n, const Double_t *u, const Double_t *v) { - if (GetLineWidth()<=0) return; + if (fSetLineWidth <= 0) + return; if (n < 2) { ::Error("TPadPainter::DrawPolyLineNDC", "invalid number of points %d", n); @@ -553,7 +618,7 @@ void TPadPainter::DrawPolyLineNDC(Int_t n, const Double_t *u, const Double_t *v) xy[i].fY = (SCoord_t)gPad->VtoPixel(v[i]); } - gVirtualX->DrawPolyLine(n, &xy[0]); + gVirtualX->DrawPolyLineW(fWinContext, n, &xy[0]); } //////////////////////////////////////////////////////////////////////////////// @@ -561,7 +626,7 @@ void TPadPainter::DrawPolyLineNDC(Int_t n, const Double_t *u, const Double_t *v) void TPadPainter::DrawSegments(Int_t n, Double_t *x, Double_t *y) { - if (GetLineWidth() <= 0) + if (fSetLineWidth <= 0) return; if (n < 1) { @@ -584,7 +649,7 @@ void TPadPainter::DrawSegments(Int_t n, Double_t *x, Double_t *y) } if (cnt > 1) - gVirtualX->DrawLinesSegments(cnt/2, &xy[0]); + gVirtualX->DrawLinesSegmentsW(fWinContext, cnt/2, &xy[0]); } //////////////////////////////////////////////////////////////////////////////// @@ -592,7 +657,7 @@ void TPadPainter::DrawSegments(Int_t n, Double_t *x, Double_t *y) void TPadPainter::DrawSegmentsNDC(Int_t n, Double_t *u, Double_t *v) { - if (GetLineWidth() <= 0) + if (fSetLineWidth <= 0) return; if (n < 1) { @@ -615,7 +680,7 @@ void TPadPainter::DrawSegmentsNDC(Int_t n, Double_t *u, Double_t *v) } if (cnt > 1) - gVirtualX->DrawLinesSegments(cnt/2, &xy[0]); + gVirtualX->DrawLinesSegmentsW(fWinContext, cnt/2, &xy[0]); } @@ -630,7 +695,7 @@ void TPadPainter::DrawPolyMarker(Int_t n, const Double_t *x, const Double_t *y) return; } - DrawPolyMarkerAux(gPad, n, x, y); + DrawPolyMarkerAux(gPad, fWinContext, n, x, y); } @@ -644,7 +709,7 @@ void TPadPainter::DrawPolyMarker(Int_t n, const Float_t *x, const Float_t *y) return; } - DrawPolyMarkerAux(gPad, n, x, y); + DrawPolyMarkerAux(gPad, fWinContext, n, x, y); } @@ -657,7 +722,7 @@ void TPadPainter::DrawText(Double_t x, Double_t y, const char *text, ETextMode m const Int_t py = gPad->YtoPixel(y); const Double_t angle = GetTextAngle(); const Double_t mgn = GetTextMagnitude(); - gVirtualX->DrawText(px, py, angle, mgn, text, (TVirtualX::ETextMode)mode); + gVirtualX->DrawTextW(fWinContext, px, py, angle, mgn, text, (TVirtualX::ETextMode)mode); } @@ -670,7 +735,7 @@ void TPadPainter::DrawText(Double_t x, Double_t y, const wchar_t *text, ETextMod const Int_t py = gPad->YtoPixel(y); const Double_t angle = GetTextAngle(); const Double_t mgn = GetTextMagnitude(); - gVirtualX->DrawText(px, py, angle, mgn, text, (TVirtualX::ETextMode)mode); + gVirtualX->DrawTextW(fWinContext, px, py, angle, mgn, text, (TVirtualX::ETextMode)mode); } @@ -683,7 +748,7 @@ void TPadPainter::DrawTextNDC(Double_t u, Double_t v, const char *text, ETextMod const Int_t py = gPad->VtoPixel(v); const Double_t angle = GetTextAngle(); const Double_t mgn = GetTextMagnitude(); - gVirtualX->DrawText(px, py, angle, mgn, text, (TVirtualX::ETextMode)mode); + gVirtualX->DrawTextW(fWinContext, px, py, angle, mgn, text, (TVirtualX::ETextMode)mode); } @@ -759,7 +824,7 @@ void TPadPainter::DrawTextNDC(Double_t u, Double_t v, const wchar_t *text, EText const Int_t py = gPad->VtoPixel(v); const Double_t angle = GetTextAngle(); const Double_t mgn = GetTextMagnitude(); - gVirtualX->DrawText(px, py, angle, mgn, text, (TVirtualX::ETextMode)mode); + gVirtualX->DrawTextW(fWinContext, px, py, angle, mgn, text, (TVirtualX::ETextMode)mode); } //Aux. private functions. @@ -962,7 +1027,7 @@ void ConvertPointsAndMerge(TVirtualPad *pad, unsigned threshold, unsigned nPoint //////////////////////////////////////////////////////////////////////////////// template -void DrawFillAreaAux(TVirtualPad *pad, Int_t nPoints, const T *xs, const T *ys) +void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T *xs, const T *ys, Bool_t close_path) { std::vector xy; @@ -981,19 +1046,19 @@ void DrawFillAreaAux(TVirtualPad *pad, Int_t nPoints, const T *xs, const T *ys) ConvertPointsAndMerge(pad, threshold, nPoints, xs, ys, xy); //We close the 'polygon' and it'll be rendered as a polyline by gVirtualX. - if (!gVirtualX->GetFillStyle()) + if (close_path) xy.push_back(xy.front()); if (xy.size() > 2) - gVirtualX->DrawFillArea(xy.size(), &xy[0]); + gVirtualX->DrawFillAreaW(cont, xy.size(), &xy[0]); } //////////////////////////////////////////////////////////////////////////////// template -void DrawPolyLineAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys) +void DrawPolyLineAux(TVirtualPad *pad, WinContext_t cont, unsigned nPoints, const T *xs, const T *ys) { - std::vector xy; + std::vector xy; const Int_t threshold = Int_t(TMath::Min(pad->GetWw() * pad->GetAbsWNDC(), pad->GetWh() * pad->GetAbsHNDC())) * 2; @@ -1009,14 +1074,14 @@ void DrawPolyLineAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *y ConvertPointsAndMerge(pad, threshold, nPoints, xs, ys, xy); if (xy.size() > 1) - gVirtualX->DrawPolyLine(xy.size(), &xy[0]); + gVirtualX->DrawPolyLineW(cont, xy.size(), &xy[0]); } //////////////////////////////////////////////////////////////////////////////// template -void DrawPolyMarkerAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T *ys) +void DrawPolyMarkerAux(TVirtualPad *pad, WinContext_t cont, unsigned nPoints, const T *xs, const T *ys) { std::vector xy(nPoints); @@ -1025,7 +1090,7 @@ void DrawPolyMarkerAux(TVirtualPad *pad, unsigned nPoints, const T *xs, const T xy[i].fY = (SCoord_t)pad->YtoPixel(ys[i]); } - gVirtualX->DrawPolyMarker(nPoints, &xy[0]); + gVirtualX->DrawPolyMarkerW(cont, nPoints, &xy[0]); } } diff --git a/graf2d/graf/inc/TWbox.h b/graf2d/graf/inc/TWbox.h index 1890643c8178f..c194f3eb36e93 100644 --- a/graf2d/graf/inc/TWbox.h +++ b/graf2d/graf/inc/TWbox.h @@ -42,6 +42,9 @@ class TWbox : public TBox { Int_t GetDarkColor() const {return TColor::GetColorDark(GetFillColor());} Int_t GetLightColor() const {return TColor::GetColorBright(GetFillColor());} void Paint(Option_t *option="") override; + void PaintBorderOn(TVirtualPad *pad, + Double_t x1, Double_t y1,Double_t x2 ,Double_t y2, + Short_t bordersize, Short_t bordermode, Bool_t with_selection = kFALSE); virtual void PaintFrame(Double_t x1, Double_t y1,Double_t x2 ,Double_t y2, Color_t color, Short_t bordersize, Short_t bordermode, Bool_t tops); diff --git a/graf2d/graf/src/TArrow.cxx b/graf2d/graf/src/TArrow.cxx index 1138b1d35d5b7..b9f2ff414943f 100644 --- a/graf2d/graf/src/TArrow.cxx +++ b/graf2d/graf/src/TArrow.cxx @@ -13,8 +13,7 @@ #include "TMath.h" #include "TArrow.h" #include "TVirtualPad.h" -#include "TVirtualPS.h" -#include "TVirtualX.h" +#include "TVirtualPadPainter.h" Float_t TArrow::fgDefaultAngle = 60; Float_t TArrow::fgDefaultArrowSize = 0.05; @@ -296,8 +295,7 @@ void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, y2ar[i] = (1/ry)*(y2ar[i]-y1ndc)+ry1; } if (opt.Contains("|>")) { - if (gVirtualX) gVirtualX->SetLineStyle(1); - if (gVirtualPS) gVirtualPS->SetLineStyle(1); + gPad->GetPainter()->SetLineStyle(1); if (GetFillColor()) { gPad->PaintFillArea(3,x2ar,y2ar); gPad->PaintPolyLine(4,x2ar,y2ar); @@ -326,8 +324,7 @@ void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, y1ar[i] = (1/ry)*(y1ar[i]-y1ndc)+ry1; } if (opt.Contains("<|")) { - if (gVirtualX) gVirtualX->SetLineStyle(1); - if (gVirtualPS) gVirtualPS->SetLineStyle(1); + gPad->GetPainter()->SetLineStyle(1); if (GetFillColor()) { gPad->PaintFillArea(3,x1ar,y1ar); gPad->PaintPolyLine(4,x1ar,y1ar); diff --git a/graf2d/graf/src/TBox.cxx b/graf2d/graf/src/TBox.cxx index ce4ea64d2efe3..7858b73dfa47a 100644 --- a/graf2d/graf/src/TBox.cxx +++ b/graf2d/graf/src/TBox.cxx @@ -16,7 +16,8 @@ #include "TBuffer.h" #include "TBox.h" #include "TVirtualPad.h" -#include "TVirtualX.h" +#include "TVirtualPadPainter.h" +#include "TCanvasImp.h" #include "TClass.h" #include "TMath.h" #include "TPoint.h" @@ -245,16 +246,43 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) static Double_t oldX1, oldY1, oldX2, oldY2; static Bool_t pA, pB, pC, pD, pTop, pL, pR, pBot, pINSIDE; Int_t wx, wy; - TVirtualPad *parent = gPad; + auto parent = gPad; + auto pp = parent->GetPainter(); Bool_t opaque = gPad->OpaqueMoving(); Bool_t ropaque = gPad->OpaqueResizing(); + // convert to user coordinates and either paint ot set back + auto action = [parent,pp,isBox,this](Bool_t paint, Int_t _x1, Int_t _y1, Int_t _x2, Int_t _y2) { + auto x1 = parent->AbsPixeltoX(_x1); + auto y1 = parent->AbsPixeltoY(_y1); + auto x2 = parent->AbsPixeltoX(_x2); + auto y2 = parent->AbsPixeltoY(_y2); + if (isBox) { + if (parent->GetLogx()) { + x1 = TMath::Power(10, x1); + x2 = TMath::Power(10, x2); + } + if (parent->GetLogy()) { + y1 = TMath::Power(10, y1); + y2 = TMath::Power(10, y2); + } + } + if (paint) { + pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); + } else { + SetX1(x1); + SetY1(y1); + SetX2(x2); + SetY2(y2); + } + }; + HideToolTip(event); switch (event) { case kMouseEnter: - if (fTip) gPad->ResetToolTip(fTip); + if (fTip) parent->ResetToolTip(fTip); break; case kButton1Double: @@ -268,13 +296,7 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) oldY1 = fY1; oldX2 = fX2; oldY2 = fY2; - gVirtualX->SetLineColor(-1); - TAttLine::Modify(); //Change line attributes only if necessary - if (GetFillColor()) - gVirtualX->SetLineColor(GetFillColor()); - else - gVirtualX->SetLineColor(1); - gVirtualX->SetLineWidth(2); + pp->SetAttLine({GetFillColor() > 0 ? GetFillColor() : (Color_t) 1, GetLineStyle(), 2}); // No break !!! @@ -287,12 +309,12 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (isBox) { if (gPad->GetLogx()) { - if (fX1>0) px1 = gPad->XtoAbsPixel(TMath::Log10(fX1)); - if (fX2>0) px2 = gPad->XtoAbsPixel(TMath::Log10(fX2)); + if (GetX1() > 0) px1 = gPad->XtoAbsPixel(TMath::Log10(GetX1())); + if (GetX2() > 0) px2 = gPad->XtoAbsPixel(TMath::Log10(GetX2())); } if (gPad->GetLogy()) { - if (fY1>0) py1 = gPad->YtoAbsPixel(TMath::Log10(fY1)); - if (fY2>0) py2 = gPad->YtoAbsPixel(TMath::Log10(fY2)); + if (GetY1() > 0) py1 = gPad->YtoAbsPixel(TMath::Log10(GetY1())); + if (GetY2() > 0) py2 = gPad->YtoAbsPixel(TMath::Log10(GetY2())); } } @@ -387,11 +409,9 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) gPad->SetCursor(kCross); } - fResizing = kFALSE; - if (pA || pB || pC || pD || pTop || pL || pR || pBot) - fResizing = kTRUE; + fResizing = pA || pB || pC || pD || pTop || pL || pR || pBot; - if (!pA && !pB && !pC && !pD && !pTop && !pL && !pR && !pBot && !pINSIDE) + if (!fResizing && !pINSIDE) gPad->SetCursor(kCross); break; @@ -402,67 +422,67 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) wx = wy = 0; if (pA) { - if (!ropaque) gVirtualX->DrawBox(pxold, pyt, pxt, pyold, TVirtualX::kHollow); // draw the old box + if (!ropaque) action(kTRUE, pxold, pyt, pxt, pyold); // draw the old box if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; } if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; } if (px < pxlp) { px = pxlp; wx = px; } if (py < pylp) { py = pylp; wy = py; } - if (!ropaque) gVirtualX->DrawBox(px , pyt, pxt, py, TVirtualX::kHollow); // draw the new box + if (!ropaque) action(kTRUE, px, pyt, pxt, py); // draw the new box } if (pB) { - if (!ropaque) gVirtualX->DrawBox(pxl , pyt, pxold, pyold, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxl, pyt, pxold, pyold); if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; } if (py > pyt-kMinSize) { py = pyt-kMinSize; wy = py; } if (px > pxtp) { px = pxtp; wx = px; } if (py < pylp) { py = pylp; wy = py; } - if (!ropaque) gVirtualX->DrawBox(pxl , pyt, px , py, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxl, pyt, px, py); } if (pC) { - if (!ropaque) gVirtualX->DrawBox(pxl , pyl, pxold, pyold, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxl, pyl, pxold, pyold); if (px < pxl+kMinSize) { px = pxl+kMinSize; wx = px; } if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; } if (px > pxtp) { px = pxtp; wx = px; } if (py > pytp) { py = pytp; wy = py; } - if (!ropaque) gVirtualX->DrawBox(pxl , pyl, px , py, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxl, pyl, px, py); } if (pD) { - if (!ropaque) gVirtualX->DrawBox(pxold, pyold, pxt, pyl, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, pxold, pyold, pxt, pyl); if (px > pxt-kMinSize) { px = pxt-kMinSize; wx = px; } if (py < pyl+kMinSize) { py = pyl+kMinSize; wy = py; } if (px < pxlp) { px = pxlp; wx = px; } if (py > pytp) { py = pytp; wy = py; } - if (!ropaque) gVirtualX->DrawBox(px , py , pxt, pyl, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px, py, pxt, pyl); } if (pTop) { - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); py2 += py - pyold; if (py2 > py1-kMinSize) { py2 = py1-kMinSize; wy = py2; } if (py2 < py2p) { py2 = py2p; wy = py2; } - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); } if (pBot) { - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); py1 += py - pyold; if (py1 < py2+kMinSize) { py1 = py2+kMinSize; wy = py1; } if (py1 > py1p) { py1 = py1p; wy = py1; } - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); } if (pL) { - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); px1 += px - pxold; if (px1 > px2-kMinSize) { px1 = px2-kMinSize; wx = px1; } if (px1 < px1p) { px1 = px1p; wx = px1; } - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); } if (pR) { - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); px2 += px - pxold; if (px2 < px1+kMinSize) { px2 = px1+kMinSize; wx = px2; } if (px2 > px2p) { px2 = px2p; wx = px2; } - if (!ropaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); + if (!ropaque) action(kTRUE, px1, py1, px2, py2); } if (pINSIDE) { - if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); // draw the old box + if (!opaque) action(kTRUE, px1, py1, px2, py2); // draw the old box Int_t dx = px - pxold; Int_t dy = py - pyold; px1 += dx; py1 += dy; px2 += dx; py2 += dy; @@ -470,72 +490,40 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (px2 > px2p) { dx = px2 - px2p; px1 -= dx; px2 -= dx; wx = px-dx; } if (py1 > py1p) { dy = py1 - py1p; py1 -= dy; py2 -= dy; wy = py-dy; } if (py2 < py2p) { dy = py2p - py2; py1 += dy; py2 += dy; wy = py+dy; } - if (!opaque) gVirtualX->DrawBox(px1, py1, px2, py2, TVirtualX::kHollow); // draw the new box + if (!opaque) action(kTRUE, px1, py1, px2, py2); // draw the new box } if (wx || wy) { if (wx) px = wx; if (wy) py = wy; - gVirtualX->Warp(px, py); + parent->GetCanvasImp()->Warp(px, py); } pxold = px; pyold = py; - if ((pINSIDE && opaque) || (fResizing && ropaque)) { - if (pA) { - fX1 = gPad->AbsPixeltoX(pxold); - fY1 = gPad->AbsPixeltoY(pyt); - fX2 = gPad->AbsPixeltoX(pxt); - fY2 = gPad->AbsPixeltoY(pyold); - } - if (pB) { - fX1 = gPad->AbsPixeltoX(pxl); - fY1 = gPad->AbsPixeltoY(pyt); - fX2 = gPad->AbsPixeltoX(pxold); - fY2 = gPad->AbsPixeltoY(pyold); - } - if (pC) { - fX1 = gPad->AbsPixeltoX(pxl); - fY1 = gPad->AbsPixeltoY(pyold); - fX2 = gPad->AbsPixeltoX(pxold); - fY2 = gPad->AbsPixeltoY(pyl); - } - if (pD) { - fX1 = gPad->AbsPixeltoX(pxold); - fY1 = gPad->AbsPixeltoY(pyold); - fX2 = gPad->AbsPixeltoX(pxt); - fY2 = gPad->AbsPixeltoY(pyl); - } - if (pTop || pBot || pL || pR || pINSIDE) { - fX1 = gPad->AbsPixeltoX(px1); - fY1 = gPad->AbsPixeltoY(py1); - fX2 = gPad->AbsPixeltoX(px2); - fY2 = gPad->AbsPixeltoY(py2); - } - - if (isBox) { - if (gPad->GetLogx()) { - fX1 = TMath::Power(10,fX1); - fX2 = TMath::Power(10,fX2); - } - if (gPad->GetLogy()) { - fY1 = TMath::Power(10,fY1); - fY2 = TMath::Power(10,fY2); - } - } - - if (pINSIDE) gPad->ShowGuidelines(this, event, 'i', true); - if (pTop) gPad->ShowGuidelines(this, event, 't', true); - if (pBot) gPad->ShowGuidelines(this, event, 'b', true); - if (pL) gPad->ShowGuidelines(this, event, 'l', true); - if (pR) gPad->ShowGuidelines(this, event, 'r', true); - if (pA) gPad->ShowGuidelines(this, event, '1', true); - if (pB) gPad->ShowGuidelines(this, event, '2', true); - if (pC) gPad->ShowGuidelines(this, event, '3', true); - if (pD) gPad->ShowGuidelines(this, event, '4', true); - gPad->Modified(kTRUE); + if (pA) + action(kFALSE, pxold, pyt, pxt, pyold); + if (pB) + action(kFALSE, pxl, pyt, pxold, pyold); + if (pC) + action(kFALSE, pxl, pyold, pxold, pyl); + if (pD) + action(kFALSE, pxold, pyold, pxt, pyl); + if (pTop || pBot || pL || pR || pINSIDE) + action(kFALSE, px1, py1, px2, py2); + + if (pINSIDE) parent->ShowGuidelines(this, event, 'i', true); + if (pTop) parent->ShowGuidelines(this, event, 't', true); + if (pBot) parent->ShowGuidelines(this, event, 'b', true); + if (pL) parent->ShowGuidelines(this, event, 'l', true); + if (pR) parent->ShowGuidelines(this, event, 'r', true); + if (pA) parent->ShowGuidelines(this, event, '1', true); + if (pB) parent->ShowGuidelines(this, event, '2', true); + if (pC) parent->ShowGuidelines(this, event, '3', true); + if (pD) parent->ShowGuidelines(this, event, '4', true); + parent->Modified(kTRUE); } break; @@ -544,84 +532,54 @@ void TBox::ExecuteEvent(Int_t event, Int_t px, Int_t py) if (gROOT->IsEscaped()) { gROOT->SetEscape(kFALSE); if (opaque) { - this->SetX1(oldX1); - this->SetY1(oldY1); - this->SetX2(oldX2); - this->SetY2(oldY2); - gPad->Modified(kTRUE); - gPad->Update(); + SetX1(oldX1); + SetY1(oldY1); + SetX2(oldX2); + SetY2(oldY2); + parent->Modified(kTRUE); + parent->Update(); } break; } if (opaque || ropaque) { - gPad->ShowGuidelines(this, event); + parent->ShowGuidelines(this, event); } else { - if (px1 < 0 ) break; - if (pA) { - fX1 = gPad->AbsPixeltoX(pxold); - fY1 = gPad->AbsPixeltoY(pyt); - fX2 = gPad->AbsPixeltoX(pxt); - fY2 = gPad->AbsPixeltoY(pyold); - } - if (pB) { - fX1 = gPad->AbsPixeltoX(pxl); - fY1 = gPad->AbsPixeltoY(pyt); - fX2 = gPad->AbsPixeltoX(pxold); - fY2 = gPad->AbsPixeltoY(pyold); - } - if (pC) { - fX1 = gPad->AbsPixeltoX(pxl); - fY1 = gPad->AbsPixeltoY(pyold); - fX2 = gPad->AbsPixeltoX(pxold); - fY2 = gPad->AbsPixeltoY(pyl); - } - if (pD) { - fX1 = gPad->AbsPixeltoX(pxold); - fY1 = gPad->AbsPixeltoY(pyold); - fX2 = gPad->AbsPixeltoX(pxt); - fY2 = gPad->AbsPixeltoY(pyl); - } - if (pTop || pBot || pL || pR || pINSIDE) { - fX1 = gPad->AbsPixeltoX(px1); - fY1 = gPad->AbsPixeltoY(py1); - fX2 = gPad->AbsPixeltoX(px2); - fY2 = gPad->AbsPixeltoY(py2); - } - - if (isBox) { - if (gPad->GetLogx()) { - fX1 = TMath::Power(10,fX1); - fX2 = TMath::Power(10,fX2); - } - if (gPad->GetLogy()) { - fY1 = TMath::Power(10,fY1); - fY2 = TMath::Power(10,fY2); - } - } + if (px1 < 0) + break; + if (pA) + action(kFALSE, pxold, pyt, pxt, pyold); + if (pB) + action(kFALSE, pxl, pyt, pxold, pyold); + if (pC) + action(kFALSE, pxl, pyold, pxold, pyl); + if (pD) + action(kFALSE, pxold, pyold, pxt, pyl); + if (pTop || pBot || pL || pR || pINSIDE) + action(kFALSE, px1, py1, px2, py2); if (pINSIDE) { // if it was not a pad that was moved then it must have been // a box or something like that so we have to redraw the pad - if (parent == gPad) gPad->Modified(kTRUE); + if (parent == gPad) parent->Modified(kTRUE); } } - if (pA || pB || pC || pD || pTop || pL || pR || pBot) gPad->Modified(kTRUE); + if (pA || pB || pC || pD || pTop || pL || pR || pBot) + parent->Modified(kTRUE); - if (!opaque) { - gVirtualX->SetLineColor(-1); - gVirtualX->SetLineWidth(-1); - } + if (!opaque) + pp->SetAttLine({-1, 1, -1}); break; case kButton1Locate: + // Sergey: code is never used, has to be removed in ROOT7 ExecuteEvent(kButton1Down, px, py); while (true) { px = py = 0; - event = gVirtualX->RequestLocator(1, 1, px, py); + event = parent->GetCanvasImp()->RequestLocator(px, py); ExecuteEvent(kButton1Motion, px, py); diff --git a/graf2d/graf/src/TLatex.cxx b/graf2d/graf/src/TLatex.cxx index 5156438c6ae61..a7c9a691adb5c 100644 --- a/graf2d/graf/src/TLatex.cxx +++ b/graf2d/graf/src/TLatex.cxx @@ -15,8 +15,8 @@ #include "TMathText.h" #include "TMath.h" #include "TVirtualPad.h" +#include "TVirtualPadPainter.h" #include "TVirtualPS.h" -#include "TVirtualX.h" #include "snprintf.h" const Double_t kPI = TMath::Pi(); @@ -1357,13 +1357,13 @@ TLatex::TLatexFormSize TLatex::Analyse(Double_t x, Double_t y, const TextSpec_t result = fs1+fs2; } - else if (opSpec>-1) { + else if (opSpec > -1) { TextSpec_t newSpec = spec; newSpec.fFont = fItalic ? 152 : 122; char letter = '\243' + opSpec; if(opSpec == 75 || opSpec == 76) { newSpec.fFont = GetTextFont(); - if (gVirtualX->InheritsFrom("TGCocoa")) { + if (gPad->GetPainter()->IsCocoa()) { if (opSpec == 75) letter = '\201'; // AA Angstroem if (opSpec == 76) letter = '\214'; // aa Angstroem } else { @@ -1491,25 +1491,26 @@ TLatex::TLatexFormSize TLatex::Analyse(Double_t x, Double_t y, const TextSpec_t Double_t x2 = x+fs1.Width()/2, y2 = y -fs1.Over(); // tilde must be drawn separately on screen and on PostScript // because an adjustment is required along Y for PostScript. - TVirtualPS *saveps = gVirtualPS; - if (gVirtualPS) gVirtualPS = nullptr; - Double_t y22 = y2; - if (gVirtualX->InheritsFrom("TGCocoa")) y2 -= 4.7*sub; + Double_t xx, yy; - Rotate(gPad, spec.fAngle, x2, y2, xx, yy); - TText tilde; - tilde.SetTextFont(fTextFont); - tilde.SetTextColor(spec.fColor); - tilde.SetTextSize(0.9*spec.fSize); - tilde.SetTextAlign(22); - tilde.SetTextAngle(fTextAngle); - tilde.PaintText(xx,yy,"~"); - if (saveps) { - gVirtualPS = saveps; - if (!strstr(gVirtualPS->GetTitle(),"IMG")) y22 -= 4*sub; - Rotate(gPad, spec.fAngle, x2, y22, xx, yy); - gVirtualPS->SetTextAlign(22); - gVirtualPS->Text(xx, yy, "~"); + if (auto ps = gPad->GetPainter()->GetPS()) { + if (!strstr(ps->GetTitle(), "IMG")) + y2 -= 4*sub; + Rotate(gPad, spec.fAngle, x2, y2, xx, yy); + ps->SetTextAlign(22); + ps->Text(xx, yy, "~"); + } else { + if (gPad->GetPainter()->IsCocoa()) + y2 -= 4.7*sub; + Rotate(gPad, spec.fAngle, x2, y2, xx, yy); + // TODO: use pad painter SetAttText and DrawText directly + TText tilde; + tilde.SetTextFont(fTextFont); + tilde.SetTextColor(spec.fColor); + tilde.SetTextSize(0.9*spec.fSize); + tilde.SetTextAlign(22); + tilde.SetTextAngle(fTextAngle); + tilde.PaintText(xx,yy,"~"); } break; } @@ -2150,88 +2151,76 @@ void TLatex::PaintLatex(Double_t x, Double_t y, Double_t angle, Double_t size, c TAttText::Modify(); // Change text attributes only if necessary. - TVirtualPS *saveps = gVirtualPS; - - if (gVirtualPS) { - if (gVirtualPS->InheritsFrom("TTeXDump")) { - gVirtualPS->SetTextAngle(angle); - TString t(text1); - if (t.Index("#")>=0 || t.Index("^")>=0 || t.Index("\\")>=0) { - t.ReplaceAll("#LT","\\langle"); - t.ReplaceAll("#GT","\\rangle"); - t.ReplaceAll("#club","\\clubsuit"); - t.ReplaceAll("#spade","\\spadesuit"); - t.ReplaceAll("#heart","\\heartsuit"); - t.ReplaceAll("#diamond","\\diamondsuit"); - t.ReplaceAll("#voidn","\\wp"); - t.ReplaceAll("#voidb","f"); - t.ReplaceAll("#ocopyright","\\copyright"); - t.ReplaceAll("#trademark","TM"); - t.ReplaceAll("#void3","TM"); - t.ReplaceAll("#oright","R"); - t.ReplaceAll("#void1","R"); - t.ReplaceAll("#3dots","\\ldots"); - t.ReplaceAll("#lbar","\\mid"); - t.ReplaceAll("#bar","\\wwbar"); - t.ReplaceAll("#void8","\\mid"); - t.ReplaceAll("#divide","\\div"); - t.ReplaceAll("#Jgothic","\\Im"); - t.ReplaceAll("#Rgothic","\\Re"); - t.ReplaceAll("#doublequote","\""); - t.ReplaceAll("#plus","+"); - t.ReplaceAll("#minus","-"); - t.ReplaceAll("#/","/"); - t.ReplaceAll("#upoint","."); - t.ReplaceAll("#aa","\\mbox{\\aa}"); - t.ReplaceAll("#AA","\\mbox{\\AA}"); - - t.ReplaceAll("#omicron","o"); - t.ReplaceAll("#Alpha","A"); - t.ReplaceAll("#Beta","B"); - t.ReplaceAll("#Epsilon","E"); - t.ReplaceAll("#Zeta","Z"); - t.ReplaceAll("#Eta","H"); - t.ReplaceAll("#Iota","I"); - t.ReplaceAll("#Kappa","K"); - t.ReplaceAll("#Mu","M"); - t.ReplaceAll("#Nu","N"); - t.ReplaceAll("#Omicron","O"); - t.ReplaceAll("#Rho","P"); - t.ReplaceAll("#Tau","T"); - t.ReplaceAll("#Chi","X"); - t.ReplaceAll("#varomega","\\varpi"); - - t.ReplaceAll("#varUpsilon","?"); - t.ReplaceAll("#corner","?"); - t.ReplaceAll("#ltbar","?"); - t.ReplaceAll("#bottombar","?"); - t.ReplaceAll("#notsubset","?"); - t.ReplaceAll("#arcbottom","?"); - t.ReplaceAll("#cbar","?"); - t.ReplaceAll("#arctop","?"); - t.ReplaceAll("#topbar","?"); - t.ReplaceAll("#arcbar","?"); - t.ReplaceAll("#downleftarrow","?"); - t.ReplaceAll("#splitline","\\genfrac{}{}{0pt}{}"); - - t.ReplaceAll("#","\\"); - t.ReplaceAll("%","\\%"); - } - gVirtualPS->Text(x,y,t.Data()); - } else { - Bool_t saveb = gPad->IsBatch(); - gPad->SetBatch(kTRUE); - if (!PaintLatex1( x, y, angle, size, text1)) { - if (saveps) gVirtualPS = saveps; - return; - } - gPad->SetBatch(saveb); - } - gVirtualPS = nullptr; + auto ps = gPad->GetPainter()->GetPS(); + + if (ps && ps->InheritsFrom("TTeXDump")) { + TString t(text1); + if (t.Index("#")>=0 || t.Index("^")>=0 || t.Index("\\")>=0) { + t.ReplaceAll("#LT","\\langle"); + t.ReplaceAll("#GT","\\rangle"); + t.ReplaceAll("#club","\\clubsuit"); + t.ReplaceAll("#spade","\\spadesuit"); + t.ReplaceAll("#heart","\\heartsuit"); + t.ReplaceAll("#diamond","\\diamondsuit"); + t.ReplaceAll("#voidn","\\wp"); + t.ReplaceAll("#voidb","f"); + t.ReplaceAll("#ocopyright","\\copyright"); + t.ReplaceAll("#trademark","TM"); + t.ReplaceAll("#void3","TM"); + t.ReplaceAll("#oright","R"); + t.ReplaceAll("#void1","R"); + t.ReplaceAll("#3dots","\\ldots"); + t.ReplaceAll("#lbar","\\mid"); + t.ReplaceAll("#bar","\\wwbar"); + t.ReplaceAll("#void8","\\mid"); + t.ReplaceAll("#divide","\\div"); + t.ReplaceAll("#Jgothic","\\Im"); + t.ReplaceAll("#Rgothic","\\Re"); + t.ReplaceAll("#doublequote","\""); + t.ReplaceAll("#plus","+"); + t.ReplaceAll("#minus","-"); + t.ReplaceAll("#/","/"); + t.ReplaceAll("#upoint","."); + t.ReplaceAll("#aa","\\mbox{\\aa}"); + t.ReplaceAll("#AA","\\mbox{\\AA}"); + + t.ReplaceAll("#omicron","o"); + t.ReplaceAll("#Alpha","A"); + t.ReplaceAll("#Beta","B"); + t.ReplaceAll("#Epsilon","E"); + t.ReplaceAll("#Zeta","Z"); + t.ReplaceAll("#Eta","H"); + t.ReplaceAll("#Iota","I"); + t.ReplaceAll("#Kappa","K"); + t.ReplaceAll("#Mu","M"); + t.ReplaceAll("#Nu","N"); + t.ReplaceAll("#Omicron","O"); + t.ReplaceAll("#Rho","P"); + t.ReplaceAll("#Tau","T"); + t.ReplaceAll("#Chi","X"); + t.ReplaceAll("#varomega","\\varpi"); + + t.ReplaceAll("#varUpsilon","?"); + t.ReplaceAll("#corner","?"); + t.ReplaceAll("#ltbar","?"); + t.ReplaceAll("#bottombar","?"); + t.ReplaceAll("#notsubset","?"); + t.ReplaceAll("#arcbottom","?"); + t.ReplaceAll("#cbar","?"); + t.ReplaceAll("#arctop","?"); + t.ReplaceAll("#topbar","?"); + t.ReplaceAll("#arcbar","?"); + t.ReplaceAll("#downleftarrow","?"); + t.ReplaceAll("#splitline","\\genfrac{}{}{0pt}{}"); + + t.ReplaceAll("#","\\"); + t.ReplaceAll("%","\\%"); + } + ps->SetTextAngle(angle); + ps->Text(x, y, t.Data()); + } else { + PaintLatex1(x, y, angle, size, text1); } - - if (!gPad->IsBatch()) PaintLatex1( x, y, angle, size, text1); - if (saveps) gVirtualPS = saveps; } //////////////////////////////////////////////////////////////////////////////// @@ -2255,30 +2244,23 @@ Int_t TLatex::PaintLatex1(Double_t x, Double_t y, Double_t angle, Double_t size, // Do not use Latex if font is low precision. if (fTextFont % 10 < 2) { - if (gVirtualX) gVirtualX->SetTextAngle(angle); - if (gVirtualPS) gVirtualPS->SetTextAngle(angle); - gPad->PaintText(x,y,text1); + gPad->GetPainter()->SetTextAngle(angle); + gPad->PaintText(x, y, text1); return 1; } - Bool_t saveb = gPad->IsBatch(); // Paint the text using TMathText if contains a "\" if (strstr(text1,"\\")) { - TMathText tm; - tm.SetTextAlign(GetTextAlign()); - tm.SetTextFont(GetTextFont()); - tm.SetTextColor(GetTextColor()); - tm.PaintMathText(x, y, angle, size, text1); - // If PDF, paint using TLatex - if (gVirtualPS) { - if (gVirtualPS->InheritsFrom("TPDF") || - gVirtualPS->InheritsFrom("TSVG")) { - newText.ReplaceAll("\\","#"); - gPad->SetBatch(kTRUE); - } else { - return 1; - } + auto ps = gPad->GetPainter()->GetPS(); + // If PDF or SVG, paint using TLatex + if (ps && (ps->InheritsFrom("TPDF") || ps->InheritsFrom("TSVG"))) { + newText.ReplaceAll("\\","#"); } else { + TMathText tm; + tm.SetTextAlign(GetTextAlign()); + tm.SetTextFont(GetTextFont()); + tm.SetTextColor(GetTextColor()); + tm.PaintMathText(x, y, angle, size, text1); return 1; } } @@ -2334,7 +2316,6 @@ Int_t TLatex::PaintLatex1(Double_t x, Double_t y, Double_t angle, Double_t size, Analyse(x,y,newSpec,text,length); } - gPad->SetBatch(saveb); SetTextSize(saveSize); SetTextAngle(angle); SetTextFont(saveFont); diff --git a/graf2d/graf/src/TMathText.cxx b/graf2d/graf/src/TMathText.cxx index e907f7eb511ff..db37252c02cbc 100644 --- a/graf2d/graf/src/TMathText.cxx +++ b/graf2d/graf/src/TMathText.cxx @@ -18,8 +18,8 @@ #include "TMathText.h" #include "TMath.h" #include "TVirtualPad.h" +#include "TVirtualPadPainter.h" #include "TVirtualPS.h" -#include "TVirtualX.h" #include "TText.h" #include "../../../graf2d/mathtext/inc/mathtext.h" @@ -583,20 +583,13 @@ void TMathText::PaintMathText(Double_t x, Double_t y, Double_t angle, Short_t saveAlign = fTextAlign; TAttText::Modify(); - if (gVirtualPS) { // Initialise TMathTextRenderer - if (gPad->IsBatch()) { - if (gVirtualPS->InheritsFrom("TImageDump")) gPad->PaintText(0, 0, ""); - } + if (auto ps = gPad->GetPainter()->GetPS()) { // Initialise TMathTextRenderer + if (ps->InheritsFrom("TImageDump")) gPad->PaintText(0, 0, ""); } // Do not use Latex if font is low precision. if (fTextFont % 10 < 2) { - if (gVirtualX) { - gVirtualX->SetTextAngle(angle); - } - if (gVirtualPS) { - gVirtualPS->SetTextAngle(angle); - } + gPad->GetPainter()->SetTextAngle(angle); gPad->PaintText(x, y, text1); return; } diff --git a/graf2d/graf/src/TWbox.cxx b/graf2d/graf/src/TWbox.cxx index a4be334f69f5f..a2fb8b9bd8619 100644 --- a/graf2d/graf/src/TWbox.cxx +++ b/graf2d/graf/src/TWbox.cxx @@ -14,9 +14,9 @@ #include "Strlen.h" #include "TWbox.h" #include "TColor.h" +#include "TStyle.h" #include "TVirtualPad.h" -#include "TVirtualX.h" -#include "TPoint.h" +#include "TVirtualPadPainter.h" /** \class TWbox @@ -135,69 +135,115 @@ void TWbox::PaintWbox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, void TWbox::PaintFrame(Double_t x1, Double_t y1,Double_t x2, Double_t y2, Color_t color, Short_t bordersize, Short_t bordermode, - Bool_t tops) + Bool_t /* tops */) { - if (!gPad) return; - if (bordermode == 0) return; - if (bordersize <= 0) bordersize = 2; + if (bordermode == 0) + return; - Short_t pxl,pyl,pxt,pyt,px1,py1,px2,py2; - Double_t xl, xt, yl, yt; + auto oldcolor = GetFillColor(); + SetFillColor(color); - // Compute real left bottom & top right of the box in pixels - px1 = gPad->XtoPixel(x1); py1 = gPad->YtoPixel(y1); - px2 = gPad->XtoPixel(x2); py2 = gPad->YtoPixel(y2); - if (px1 < px2) {pxl = px1; pxt = px2; xl = x1; xt = x2; } - else {pxl = px2; pxt = px1; xl = x2; xt = x1;} - if (py1 > py2) {pyl = py1; pyt = py2; yl = y1; yt = y2;} - else {pyl = py2; pyt = py1; yl = y2; yt = y1;} - - if (!gPad->IsBatch()) { - TPoint frame[7]; - - // GetDarkColor() and GetLightColor() use GetFillColor() - Color_t oldcolor = GetFillColor(); - SetFillColor(color); - TAttFill::Modify(); - - // Draw top&left part of the box - frame[0].fX = pxl; frame[0].fY = pyl; - frame[1].fX = pxl + bordersize; frame[1].fY = pyl - bordersize; - frame[2].fX = frame[1].fX; frame[2].fY = pyt + bordersize; - frame[3].fX = pxt - bordersize; frame[3].fY = frame[2].fY; - frame[4].fX = pxt; frame[4].fY = pyt; - frame[5].fX = pxl; frame[5].fY = pyt; - frame[6].fX = pxl; frame[6].fY = pyl; - - if (bordermode == -1) gVirtualX->SetFillColor(GetDarkColor()); - else gVirtualX->SetFillColor(GetLightColor()); - gVirtualX->DrawFillArea(7, frame); - - // Draw bottom&right part of the box - frame[0].fX = pxl; frame[0].fY = pyl; - frame[1].fX = pxl + bordersize; frame[1].fY = pyl - bordersize; - frame[2].fX = pxt - bordersize; frame[2].fY = frame[1].fY; - frame[3].fX = frame[2].fX; frame[3].fY = pyt + bordersize; - frame[4].fX = pxt; frame[4].fY = pyt; - frame[5].fX = pxt; frame[5].fY = pyl; - frame[6].fX = pxl; frame[6].fY = pyl; - - if (bordermode == -1) gVirtualX->SetFillColor(TColor::GetColorBright(GetFillColor())); - else gVirtualX->SetFillColor(TColor::GetColorDark(GetFillColor())); - gVirtualX->DrawFillArea(7, frame); - - gVirtualX->SetFillColor(-1); - SetFillColor(oldcolor); + PaintBorderOn(gPad, x1, y1, x2, y2, bordersize, bordermode); + + SetFillColor(oldcolor); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Paint a 3D border around a box. +/// Used also by the pad painter + +void TWbox::PaintBorderOn(TVirtualPad *pad, + Double_t x1, Double_t y1,Double_t x2 ,Double_t y2, + Short_t bordersize, Short_t bordermode, Bool_t with_selection) +{ + if (bordermode == 0) + return; + if (bordersize <= 0) + bordersize = 2; + + auto pp = pad->GetPainter(); + if (!pp) + return; + + Double_t ww = pad->GetWw(), wh = pad->GetWh(); + + if (pp->GetPS()) { + // SL: need to calculate page size to get real coordiantes for border + // TODO: Code can be removed if border not need to be exact pixel size + Float_t xsize = 20, ysize = 26; + gStyle->GetPaperSize(xsize, ysize); + Double_t ratio = wh/ww; + if (xsize * ratio > ysize) + xsize = ysize/ratio; + else + ysize = xsize*ratio; + ww = 72 / 2.54 * xsize; + wh = 72 / 2.54 * ysize; } - if (!tops) return; + const Double_t realBsX = bordersize / (pad->GetAbsWNDC() * ww) * (pad->GetX2() - pad->GetX1()); + const Double_t realBsY = bordersize / (pad->GetAbsHNDC() * wh) * (pad->GetY2() - pad->GetY1()); + + // GetColorDark() and GetColorBright() use GetFillColor() + Color_t fillcolor = GetFillColor(); + Color_t light = !fillcolor ? 0 : GetLightColor(); + Color_t dark = !fillcolor ? 0 : GetDarkColor(); + + Double_t xl, xt, yl, yt; - // same for PostScript - // Double_t dx = (xt - xl) *Double_t(bordersize)/Double_t(pxt - pxl); - // Int_t border = gVirtualPS->XtoPS(xt) - gVirtualPS->XtoPS(xt-dx); + // Compute real left bottom & top right of the box in pixels + if (pad->XtoPixel(x1) < pad->XtoPixel(x2)) { + xl = x1; + xt = x2; + } else { + xl = x2; + xt = x1; + } + if (pad->YtoPixel(y1) > pad->YtoPixel(y2)) { + yl = y1; + yt = y2; + } else { + yl = y2; + yt = y1; + } - gPad->PaintBorderPS(xl, yl, xt, yt, bordermode, bordersize, - GetDarkColor(), GetLightColor()); + Double_t frameXs[7] = {}, frameYs[7] = {}; + + // Draw top&left part of the box + frameXs[0] = xl; frameYs[0] = yl; + frameXs[1] = xl + realBsX; frameYs[1] = yl + realBsY; + frameXs[2] = frameXs[1]; frameYs[2] = yt - realBsY; + frameXs[3] = xt - realBsX; frameYs[3] = frameYs[2]; + frameXs[4] = xt; frameYs[4] = yt; + frameXs[5] = xl; frameYs[5] = yt; + frameXs[6] = xl; frameYs[6] = yl; + + SetFillColor(bordermode == -1 ? dark : light); + pp->SetAttFill(*this); + pp->DrawFillArea(7, frameXs, frameYs); + + // Draw bottom&right part of the box + frameXs[0] = xl; frameYs[0] = yl; + frameXs[1] = xl + realBsX; frameYs[1] = yl + realBsY; + frameXs[2] = xt - realBsX; frameYs[2] = frameYs[1]; + frameXs[3] = frameXs[2]; frameYs[3] = yt - realBsY; + frameXs[4] = xt; frameYs[4] = yt; + frameXs[5] = xt; frameYs[5] = yl; + frameXs[6] = xl; frameYs[6] = yl; + + SetFillColor(bordermode == -1 ? light : dark); + pp->SetAttFill(*this); + pp->DrawFillArea(7, frameXs, frameYs); + + SetFillColor(fillcolor); + + if (with_selection) { + Color_t oldlinecolor = GetLineColor(); + SetLineColor(GetFillColor() != 2 ? 2 : 4); + pp->SetAttLine(*this); + pp->DrawBox(xl + realBsX, yl + realBsY, xt - realBsX, yt - realBsY, TVirtualPadPainter::kHollow); + SetLineColor(oldlinecolor); + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/graf2d/quartz/inc/QuartzFillArea.h b/graf2d/quartz/inc/QuartzFillArea.h index 73f88b6bedbe9..ab3b0caee21a1 100644 --- a/graf2d/quartz/inc/QuartzFillArea.h +++ b/graf2d/quartz/inc/QuartzFillArea.h @@ -22,21 +22,21 @@ #include -#include "TAttFill.h" #include "Rtypes.h" #include "TPoint.h" class TColorGradient; +class TAttFill; namespace ROOT { namespace Quartz { Bool_t SetFillColor(CGContextRef ctx, Color_t colorIndex); -Bool_t SetFillAreaParameters(CGContextRef ctx, unsigned *patternIndex); +Bool_t SetFillAreaParameters(CGContextRef ctx, unsigned *patternIndex, const TAttFill &attfill); void DrawBox(CGContextRef ctx, Int_t x1, Int_t y1, Int_t x2, Int_t y2, bool hollow); -void DrawFillArea(CGContextRef ctx, Int_t n, TPoint *xy, Bool_t drawShadow); +void DrawFillArea(CGContextRef ctx, Int_t n, TPoint *xy, Bool_t drawShadow, const TAttFill &attfill); void DrawPolygonWithGradientFill(CGContextRef ctx, const TColorGradient *extendedColor, const CGSize &sizeOfDrawable, Int_t nPoints, const TPoint *xy, Bool_t drawShadow); diff --git a/graf2d/quartz/inc/QuartzText.h b/graf2d/quartz/inc/QuartzText.h index e0c3014ab3f4b..b6ff70bfb9072 100644 --- a/graf2d/quartz/inc/QuartzText.h +++ b/graf2d/quartz/inc/QuartzText.h @@ -20,6 +20,8 @@ #include "GuiTypes.h" +class TAttText; + ///////////////////////////////////////////////// // // // TextLine - wrapper class for a CoreText's // @@ -48,8 +50,8 @@ class TextLine { void GetBounds(UInt_t &w, UInt_t &h)const; void GetAscentDescent(Int_t &asc, Int_t &desc)const; - void DrawLine(CGContextRef ctx)const; - void DrawLine(CGContextRef ctx, Double_t x, Double_t y)const; + void DrawLine(CGContextRef ctx) const; + void DrawLine(CGContextRef ctx, Double_t x, Double_t y, const TAttText &att) const; private: CTLineRef fCTLine; //Core Text line, created from Attributed string. CTFontRef fCTFont; //A font used for this CTLine. diff --git a/graf2d/quartz/src/QuartzFillArea.mm b/graf2d/quartz/src/QuartzFillArea.mm index 3114e2d7132be..0818d416a30d1 100644 --- a/graf2d/quartz/src/QuartzFillArea.mm +++ b/graf2d/quartz/src/QuartzFillArea.mm @@ -15,9 +15,9 @@ #include "QuartzFillArea.h" #include "TColorGradient.h" +#include "TAttFill.h" #include "QuartzLine.h" #include "CocoaUtils.h" -#include "TVirtualX.h" #include "RStipples.h" #include "TError.h" #include "TROOT.h" @@ -347,12 +347,12 @@ void DrawPattern(void *data, CGContextRef ctx) } //______________________________________________________________________________ -bool SetFillPattern(CGContextRef ctx, const unsigned *patternIndex) +bool SetFillPattern(CGContextRef ctx, const unsigned *patternIndex, Color_t attrFillColor) { assert(ctx != nullptr && "SetFillPattern, ctx parameter is null"); assert(patternIndex != nullptr && "SetFillPattern, patternIndex parameter is null"); - const TColor *fillColor = gROOT->GetColor(gVirtualX->GetFillColor()); + const TColor *fillColor = gROOT->GetColor(attrFillColor); if (!fillColor) fillColor = gROOT->GetColor(kWhite); @@ -388,33 +388,36 @@ bool SetFillPattern(CGContextRef ctx, const unsigned *patternIndex) } //______________________________________________________________________________ -bool SetFillAreaParameters(CGContextRef ctx, unsigned *patternIndex) +bool SetFillAreaParameters(CGContextRef ctx, unsigned *patternIndex, const TAttFill &attfill) { assert(ctx != nullptr && "SetFillAreaParameters, ctx parameter is null"); - const unsigned fillStyle = gVirtualX->GetFillStyle() / 1000; + Style_t attFillStyle = attfill.GetFillStyle(); + Color_t attFillColor = attfill.GetFillColor(); + + const unsigned fillStyle = attFillStyle / 1000; //2 is hollow, 1 is solid and 3 is a hatch, !solid and !hatch - this is from O.C.'s code. if (fillStyle == 2 || (fillStyle != 1 && fillStyle != 3)) { - if (!SetLineColor(ctx, gVirtualX->GetFillColor())) { - ::Error("SetFillAreaParameters", "Line color for index %d was not found", int(gVirtualX->GetLineColor())); + if (!SetLineColor(ctx, attFillColor)) { + ::Error("SetFillAreaParameters", "Line color for index %d was not found", int(attFillColor)); return false; } } else if (fillStyle == 1) { //Solid fill. - if (!SetFillColor(ctx, gVirtualX->GetFillColor())) { - ::Error("SetFillAreaParameters", "Fill color for index %d was not found", int(gVirtualX->GetFillColor())); + if (!SetFillColor(ctx, attFillColor)) { + ::Error("SetFillAreaParameters", "Fill color for index %d was not found", int(attFillColor)); return false; } } else { assert(patternIndex != nullptr && "SetFillAreaParameters, pattern index in null"); - *patternIndex = gVirtualX->GetFillStyle() % 1000; + *patternIndex = attFillStyle % 1000; //ROOT has 26 fixed patterns. if (*patternIndex > 25) *patternIndex = 2; - if (!SetFillPattern(ctx, patternIndex)) { + if (!SetFillPattern(ctx, patternIndex, attFillColor)) { ::Error("SetFillAreaParameters", "SetFillPattern failed"); return false; } @@ -440,7 +443,7 @@ void DrawBox(CGContextRef ctx, Int_t x1, Int_t y1, Int_t x2, Int_t y2, bool holl } //______________________________________________________________________________ -void DrawFillArea(CGContextRef ctx, Int_t n, TPoint *xy, Bool_t shadow) +void DrawFillArea(CGContextRef ctx, Int_t n, TPoint *xy, Bool_t shadow, const TAttFill &attfill) { // Draw a filled area through all points. // n : number of points @@ -457,7 +460,7 @@ void DrawFillArea(CGContextRef ctx, Int_t n, TPoint *xy, Bool_t shadow) CGContextClosePath(ctx); - const unsigned fillStyle = gVirtualX->GetFillStyle() / 1000; + const unsigned fillStyle = attfill.GetFillStyle() / 1000; //2 is hollow, 1 is solid and 3 is a hatch, !solid and !hatch - this is from O.C.'s code. if (fillStyle == 2 || (fillStyle != 1 && fillStyle != 3)) { diff --git a/graf2d/quartz/src/QuartzText.mm b/graf2d/quartz/src/QuartzText.mm index 1da812a09aeaa..a38beede91343 100644 --- a/graf2d/quartz/src/QuartzText.mm +++ b/graf2d/quartz/src/QuartzText.mm @@ -18,7 +18,7 @@ #include "QuartzText.h" #include "CocoaUtils.h" -#include "TVirtualX.h" +#include "TAttText.h" #include "TColor.h" #include "TError.h" #include "TROOT.h" @@ -264,7 +264,7 @@ CGRect BBoxForCTRun(CTFontRef font, CTRunRef run) } //_________________________________________________________________ -void TextLine::DrawLine(CGContextRef ctx)const +void TextLine::DrawLine(CGContextRef ctx) const { assert(ctx != 0 && "DrawLine, ctx parameter is null"); CTLineDraw(fCTLine, ctx); @@ -272,7 +272,7 @@ CGRect BBoxForCTRun(CTFontRef font, CTRunRef run) //______________________________________________________________________________ -void TextLine::DrawLine(CGContextRef ctx, Double_t x, Double_t y)const +void TextLine::DrawLine(CGContextRef ctx, Double_t x, Double_t y, const TAttText &att) const { assert(ctx != 0 && "DrawLine, ctx parameter is null"); @@ -282,7 +282,7 @@ CGRect BBoxForCTRun(CTFontRef font, CTRunRef run) GetBounds(w, h); Double_t xc = 0., yc = 0.; - const UInt_t hAlign = UInt_t(gVirtualX->GetTextAlign() / 10); + Int_t hAlign = att.GetTextAlign() / 10; switch (hAlign) { case 1: xc = 0.5 * w; @@ -294,7 +294,7 @@ CGRect BBoxForCTRun(CTFontRef font, CTRunRef run) break; } - const UInt_t vAlign = UInt_t(gVirtualX->GetTextAlign() % 10); + Int_t vAlign = att.GetTextAlign() % 10; switch (vAlign) { case 1: yc = 0.5 * h; @@ -308,7 +308,7 @@ CGRect BBoxForCTRun(CTFontRef font, CTRunRef run) CGContextSetTextPosition(ctx, 0., 0.); CGContextTranslateCTM(ctx, x, y); - CGContextRotateCTM(ctx, gVirtualX->GetTextAngle() * TMath::DegToRad()); + CGContextRotateCTM(ctx, att.GetTextAngle() * TMath::DegToRad()); CGContextTranslateCTM(ctx, xc, yc); CGContextTranslateCTM(ctx, -0.5 * w, -0.5 * h); diff --git a/graf2d/win32gdk/inc/TGWin32.h b/graf2d/win32gdk/inc/TGWin32.h index ef6e849267bb9..43c0a92aebac7 100644 --- a/graf2d/win32gdk/inc/TGWin32.h +++ b/graf2d/win32gdk/inc/TGWin32.h @@ -17,6 +17,10 @@ #include "TTF.h" +#include +#include + + #ifndef __CLING__ @@ -64,31 +68,24 @@ class TExMap; class TGWin32 : public TVirtualX { private: - enum EAlign { kNone, kTLeft, kTCenter, kTRight, kMLeft, kMCenter, kMRight, - kBLeft, kBCenter, kBRight }; - - FT_Vector fAlign; ///< alignment vector - - void Align(void); + void Align(WinContext_t wctxt); void DrawImage(FT_Bitmap *source, ULong_t fore, ULong_t back, GdkImage *xim, Int_t bx, Int_t by); - Bool_t IsVisible(Int_t x, Int_t y, UInt_t w, UInt_t h); - GdkImage *GetBackground(Int_t x, Int_t y, UInt_t w, UInt_t h); - void RenderString(Int_t x, Int_t y, ETextMode mode); + Bool_t IsVisible(WinContext_t wctxt, Int_t x, Int_t y, UInt_t w, UInt_t h); + GdkImage *GetBackground(WinContext_t wctxt, Int_t x, Int_t y, UInt_t w, UInt_t h); + void RenderString(WinContext_t wctxt, Int_t x, Int_t y, ETextMode mode); - Int_t fMaxNumberOfWindows; ///< Maximum number of windows - XWindow_t *fWindows; ///< List of windows + std::unordered_map> fWindows; // map of windows TExMap *fColors; ///< Hash list of colors GdkCursor *fCursors[kNumCursors]; ///< List of cursors - void CloseWindow1(); + Int_t AddWindowHandle(); void PutImage(Int_t offset, Int_t itran, Int_t x0, Int_t y0, Int_t nx, Int_t ny, Int_t xmin, Int_t ymin, Int_t xmax, Int_t ymax, UChar_t *image, Drawable_t id); void RemovePixmap(GdkDrawable *pix); - void SetColor(GdkGC *gc, Int_t ci); + void SetColor(XWindow_t *ctxt, GdkGC *gc, Int_t ci); void SetInput(Int_t inp); - void SetMarkerType(Int_t type, Int_t n, GdkPoint *xy); void MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors); Int_t FindColor(ULong_t pixel, ULong_t *orgcolors, Int_t ncolors); void ImgPickPalette(GdkImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B); @@ -110,9 +107,6 @@ class TGWin32 : public TVirtualX { Int_t fScreenNumber; ///< Screen number Bool_t fHasTTFonts; ///< True when TrueType fonts are used Bool_t fUseSysPointers; ///< True when using system mouse pointers - Int_t fTextAlignH; ///< Text Alignment Horizontal - Int_t fTextAlignV; ///< Text Alignment Vertical - Int_t fTextAlign; ///< Text alignment (set in SetTextAlign) Float_t fCharacterUpX; ///< Character Up vector along X Float_t fCharacterUpY; ///< Character Up vector along Y Float_t fTextMagnitude; ///< Text Magnitude @@ -126,20 +120,6 @@ class TGWin32 : public TVirtualX { Handle_t fXEvent; ///< Current native (GDK) event TObject* fRefreshTimer; ///< TGWin32RefreshTimer for GUI thread message handler - Bool_t fFillColorModified; - Bool_t fFillStyleModified; - Bool_t fLineColorModified; - Bool_t fPenModified; ///< line syle || width modified - Bool_t fMarkerStyleModified; - Bool_t fMarkerColorModified; - - void UpdateFillColor(); - void UpdateFillStyle(); - void UpdateLineColor(); - void UpdateMarkerStyle(); - void UpdateMarkerColor(); - void UpdateLineStyle(); - // needed by TGWin32TTF Bool_t AllocColor(GdkColormap *cmap, GdkColor *color); void QueryColors(GdkColormap *cmap, GdkColor *colors, Int_t ncolors); @@ -205,10 +185,13 @@ class TGWin32 : public TVirtualX { void SetDrawMode(EDrawMode mode) override; void SetFillColor(Color_t cindex) override; void SetFillStyle(Style_t style) override; + Style_t GetFillStyle() const override; void SetLineColor(Color_t cindex) override; void SetLineType(Int_t n, Int_t *dash) override; void SetLineStyle(Style_t linestyle) override; + Style_t GetLineStyle() const override; void SetLineWidth(Width_t width) override; + Width_t GetLineWidth() const override; void SetMarkerColor(Color_t cindex) override; void SetMarkerSize(Float_t markersize) override; void SetMarkerStyle(Style_t markerstyle) override; @@ -224,6 +207,28 @@ class TGWin32 : public TVirtualX { void WritePixmap(Int_t wid, UInt_t w, UInt_t h, char *pxname) override; Window_t GetCurrentWindow() const override; + //---- Methods used for new graphics ----- + WinContext_t GetWindowContext(Int_t wid) override; + void SetAttFill(WinContext_t wctxt, const TAttFill &att) override; + void SetAttLine(WinContext_t wctxt, const TAttLine &att) override; + void SetAttMarker(WinContext_t wctxt, const TAttMarker &att) override; + void SetAttText(WinContext_t wctxt, const TAttText &att) override; + void SetDrawModeW(WinContext_t wctxt, EDrawMode mode) override; + EDrawMode GetDrawModeW(WinContext_t wctxt) override; + void ClearWindowW(WinContext_t wctxt) override; + void UpdateWindowW(WinContext_t wctxt, Int_t mode) override; + + void DrawBoxW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) override; + void DrawFillAreaW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawLineW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2) override; + void DrawPolyLineW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawLinesSegmentsW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawPolyMarkerW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, + const char *text, ETextMode mode) override; + void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, + const wchar_t *text, ETextMode mode) override; + //---- Methods used for GUI ----- void GetWindowAttributes(Window_t id, WindowAttributes_t &attr) override; void MapWindow(Window_t id) override; diff --git a/graf2d/win32gdk/src/TGWin32.cxx b/graf2d/win32gdk/src/TGWin32.cxx index 4ae85c959c7a6..22632eb44b9ea 100644 --- a/graf2d/win32gdk/src/TGWin32.cxx +++ b/graf2d/win32gdk/src/TGWin32.cxx @@ -116,24 +116,53 @@ void gdk_win32_draw_lines (GdkDrawable *drawable, }; +enum EAlign { kAlignNone, kTLeft, kTCenter, kTRight, kMLeft, kMCenter, kMRight, + kBLeft, kBCenter, kBRight }; + +const int kMAXGC = 7, + kGCline = 0, kGCmark = 1, kGCfill = 2, + kGCtext = 3, kGCinvt = 4, kGCdash = 5, kGCpxmp = 6; + + //////////// internal classes & structures (very private) //////////////// struct XWindow_t { - Int_t open; // 1 if the window is open, 0 if not - Int_t double_buffer; // 1 if the double buffer is on, 0 if not - Int_t ispixmap; // 1 if pixmap, 0 if not - GdkDrawable *drawing; // drawing area, equal to window or buffer - GdkDrawable *window; // win32 window - GdkDrawable *buffer; // pixmap used for double buffer - UInt_t width; // width of the window - UInt_t height; // height of the window - Int_t clip; // 1 if the clipping is on - Int_t xclip; // x coordinate of the clipping rectangle - Int_t yclip; // y coordinate of the clipping rectangle - UInt_t wclip; // width of the clipping rectangle - UInt_t hclip; // height of the clipping rectangle - ULong_t *new_colors; // new image colors (after processing) - Int_t ncolors; // number of different colors + Int_t open = 0; ///< 1 if the window is open, 0 if not + Int_t shared = 0; ///< 1 if Qt window + Int_t double_buffer = 0; ///< 1 if the double buffer is on, 0 if not + Int_t ispixmap = 0; ///< 1 if pixmap, 0 if not + GdkDrawable *drawing = nullptr;///< drawing area, equal to window or buffer + GdkDrawable *window = nullptr; ///< win32 window + GdkDrawable *buffer = nullptr; ///< pixmap used for double buffer + UInt_t width = 0; ///< width of the window + UInt_t height = 0; ///< height of the window + Int_t clip = 0; ///< 1 if the clipping is on + Int_t xclip = 0; ///< x coordinate of the clipping rectangle + Int_t yclip = 0; ///< y coordinate of the clipping rectangle + UInt_t wclip = 0; ///< width of the clipping rectangle + UInt_t hclip = 0; ///< height of the clipping rectangle + ULong_t *new_colors = nullptr; ///< new image colors (after processing) + Int_t ncolors = 0; ///< number of different colors + GdkGC *fGClist[kMAXGC]; ///< array of GC objects for concrete window + TVirtualX::EDrawMode drawMode = TVirtualX::kCopy; ///< current draw mode + TAttLine fAttLine = {-1, -1, -1}; ///< current line attributes + GdkLineStyle lineStyle = GDK_LINE_SOLID; ///< current line style + Int_t lineWidth = 0; ///< current line width + std::vector dashList; ///< Gtk array for dashes + Int_t dashLength = 0; ///< total length of dashes + Int_t dashOffset = 0; ///< current dash offset + TAttFill fAttFill = {-1, -1}; ///< current fill attributes + Int_t fillHollow = 0; ///< Flag if fill style is hollow + Int_t fillFasi = -1; ///< fasi parameter for fill pattern + GdkPixmap *fillPattern = nullptr; ///< current fill pattern + TAttMarker fAttMarker = { -1, -1, -1 }; ///< current marker attribute + Int_t markerType = 0; ///< 4 differen kinds of marker + Int_t markerSize = 0; ///< size of simple markers + std::vector markerShape; ///< marker shape points + Int_t markerLineWidth = 0; ///< line width used for marker + TAttText fAttText; ///< current text attributes + EAlign textAlign = kAlignNone; ///< selected text align + FT_Vector alignVector; ///< alignment vector }; @@ -147,35 +176,9 @@ GdkAtom gClipboardAtom = GDK_NONE; static XWindow_t *gCws; // gCws: pointer to the current window static XWindow_t *gTws; // gTws: temporary pointer -// -// gColors[0] : background also used for b/w screen -// gColors[1] : foreground also used for b/w screen -// gColors[2..kMAXCOL-1]: colors which can be set by SetColor -// const Int_t kBIGGEST_RGB_VALUE = 65535; -//const Int_t kMAXCOL = 1000; -//static struct { -// Int_t defined; -// GdkColor color; -//} gColors[kMAXCOL]; - -// -// Primitives Graphic Contexts global for all windows -// -const int kMAXGC = 7; -static GdkGC *gGClist[kMAXGC]; -static GdkGC *gGCline; // = gGClist[0]; // PolyLines -static GdkGC *gGCmark; // = gGClist[1]; // PolyMarker -static GdkGC *gGCfill; // = gGClist[2]; // Fill areas -static GdkGC *gGCtext; // = gGClist[3]; // Text -static GdkGC *gGCinvt; // = gGClist[4]; // Inverse text -static GdkGC *gGCdash; // = gGClist[5]; // Dashed lines -static GdkGC *gGCpxmp; // = gGClist[6]; // Pixmap management - -static GdkGC *gGCecho; // Input echo -static Int_t gFillHollow; // Flag if fill style is hollow -static GdkPixmap *gFillPattern; // Fill pattern +static GdkGC *gGCecho; // Input echo - global // // Text management @@ -185,13 +188,6 @@ static const char *gTextFont = "arial.ttf"; // Current font // // Markers // -const Int_t kMAXMK = 100; -static struct { - int type; - int n; - GdkPoint xy[kMAXMK]; -} gMarker; // Point list to draw marker -static int gMarkerLineWidth = 0; static int gMarkerLineStyle = GDK_LINE_SOLID; static int gMarkerCapStyle = GDK_CAP_ROUND; static int gMarkerJoinStyle = GDK_JOIN_ROUND; @@ -199,14 +195,8 @@ static int gMarkerJoinStyle = GDK_JOIN_ROUND; // // Keep style values for line GdkGC // -static int gLineWidth = 0; -static int gLineStyle = GDK_LINE_SOLID; static int gCapStyle = GDK_CAP_BUTT; static int gJoinStyle = GDK_JOIN_MITER; -static char gDashList[10]; -static int gDashLength = 0; -static int gDashOffset = 0; -static int gDashSize = 0; // // Event masks @@ -810,7 +800,6 @@ TGWin32MainThread::TGWin32MainThread() TGWin32::TGWin32(): fRefreshTimer(0) { fScreenNumber = 0; - fWindows = 0; fColors = 0; } @@ -822,25 +811,11 @@ TGWin32::TGWin32(const char *name, const char *title) : TVirtualX(name,title), f fScreenNumber = 0; fHasTTFonts = kFALSE; fUseSysPointers = kFALSE; - fTextAlignH = 1; - fTextAlignV = 1; - fTextAlign = 7; fTextMagnitude = 1; fCharacterUpX = 1; fCharacterUpY = 1; fDrawMode = kCopy; - fWindows = 0; - fMaxNumberOfWindows = 10; fXEvent = 0; - fFillColorModified = kFALSE; - fFillStyleModified = kFALSE; - fLineColorModified = kFALSE; - fPenModified = kFALSE; - fMarkerStyleModified = kFALSE; - fMarkerColorModified = kFALSE; - - fWindows = (XWindow_t*) TStorage::Alloc(fMaxNumberOfWindows*sizeof(XWindow_t)); - for (int i = 0; i < fMaxNumberOfWindows; i++) fWindows[i].open = 0; fColors = new TExMap; @@ -921,9 +896,6 @@ void TGWin32::CloseDisplay() delete delSplash; } - if (fWindows) TStorage::Dealloc(fWindows); - fWindows = 0; - if (fXEvent) gdk_event_free((GdkEvent*)fXEvent); TGWin32ProxyBase::GlobalUnlock(); @@ -972,7 +944,6 @@ Int_t TGWin32::OpenDisplay(const char *dpyName) GdkPixmap *pixmp1, *pixmp2; GdkColor fore, back; GdkColor color; - GdkGCValues gcvals; int i; HWND hDesktop = ::GetDesktopWindow(); @@ -1004,25 +975,6 @@ Int_t TGWin32::OpenDisplay(const char *dpyName) GetColor(0).fDefined = kTRUE; // default background gdk_color_white((GdkColormap *)fColormap, &GetColor(0).color); - // Create primitives graphic contexts - for (i = 0; i < kMAXGC; i++) { - gGClist[i] = gdk_gc_new(GDK_ROOT_PARENT()); - gdk_gc_set_foreground(gGClist[i], &GetColor(1).color); - gdk_gc_set_background(gGClist[i], &GetColor(0).color); - } - - gGCline = gGClist[0]; // PolyLines - gGCmark = gGClist[1]; // PolyMarker - gGCfill = gGClist[2]; // Fill areas - gGCtext = gGClist[3]; // Text - gGCinvt = gGClist[4]; // Inverse text - gGCdash = gGClist[5]; // Dashed lines - gGCpxmp = gGClist[6]; // Pixmap management - - gdk_gc_get_values(gGCtext, &gcvals); - gdk_gc_set_foreground(gGCinvt, &gcvals.background); - gdk_gc_set_background(gGCinvt, &gcvals.foreground); - // Create input echo graphic context GdkGCValues echov; gdk_color_black(fColormap, &echov.foreground); // = BlackPixel(fDisplay, fScreenNumber); @@ -1199,30 +1151,32 @@ void TGWin32::QueryColors(GdkColormap *cmap, GdkColor *color, Int_t ncolors) /// then the rotation is applied on the alignment variables. /// SetRotation and LayoutGlyphs should have been called before. -void TGWin32::Align(void) +void TGWin32::Align(WinContext_t wctxt) { - EAlign align = (EAlign) fTextAlign; + auto ctxt = (XWindow_t *) wctxt; + + auto align = ctxt->textAlign; // vertical alignment if (align == kTLeft || align == kTCenter || align == kTRight) { - fAlign.y = TTF::GetAscent(); + ctxt->alignVector.y = TTF::GetAscent(); } else if (align == kMLeft || align == kMCenter || align == kMRight) { - fAlign.y = TTF::GetAscent()/2; + ctxt->alignVector.y = TTF::GetAscent()/2; } else { - fAlign.y = 0; + ctxt->alignVector.y = 0; } // horizontal alignment if (align == kTRight || align == kMRight || align == kBRight) { - fAlign.x = TTF::GetWidth(); + ctxt->alignVector.x = TTF::GetWidth(); } else if (align == kTCenter || align == kMCenter || align == kBCenter) { - fAlign.x = TTF::GetWidth()/2; + ctxt->alignVector.x = TTF::GetWidth()/2; } else { - fAlign.x = 0; + ctxt->alignVector.x = 0; } - FT_Vector_Transform(&fAlign, TTF::GetRotMatrix()); - fAlign.x = fAlign.x >> 6; - fAlign.y = fAlign.y >> 6; + FT_Vector_Transform(&ctxt->alignVector, TTF::GetRotMatrix()); + ctxt->alignVector.x = ctxt->alignVector.x >> 6; + ctxt->alignVector.y = ctxt->alignVector.y >> 6; } //////////////////////////////////////////////////////////////////////////////// @@ -1352,12 +1306,25 @@ void TGWin32::DrawImage(FT_Bitmap *source, ULong_t fore, ULong_t back, void TGWin32::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode) { + DrawTextW((WinContext_t) gCws, x, y, angle, mgn, text, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw text using TrueType fonts on specified window. +/// If TrueType fonts are not available the +/// text is drawn with TGWin32::DrawText. + +void TGWin32::DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, + const char *text, ETextMode mode) +{ + if (!wctxt) return; + if (!TTF::IsInitialized()) TTF::Init(); TTF::SetRotationMatrix(angle); TTF::PrepareString(text); TTF::LayoutGlyphs(); - Align(); - RenderString(x, y, mode); + Align(wctxt); + RenderString(wctxt, x, y, mode); } //////////////////////////////////////////////////////////////////////////////// @@ -1367,24 +1334,38 @@ void TGWin32::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, void TGWin32::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, const wchar_t *text, ETextMode mode) { + DrawTextW((WinContext_t) gCws, x, y, angle, mgn, text, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw text using TrueType fonts on specified window. +/// If TrueType fonts are not available the +/// text is drawn with TGWin32::DrawText. + +void TGWin32::DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, + const wchar_t *text, ETextMode mode) +{ + if (!wctxt) return; + if (!TTF::IsInitialized()) TTF::Init(); TTF::SetRotationMatrix(angle); TTF::PrepareString(text); TTF::LayoutGlyphs(); - Align(); - RenderString(x, y, mode); + Align(wctxt); + RenderString(wctxt, x, y, mode); } //////////////////////////////////////////////////////////////////////////////// /// Get the background of the current window in an XImage. -GdkImage *TGWin32::GetBackground(Int_t x, Int_t y, UInt_t w, UInt_t h) +GdkImage *TGWin32::GetBackground(WinContext_t wctxt, Int_t x, Int_t y, UInt_t w, UInt_t h) { - Window_t cws = GetCurrentWindow(); + auto ctxt = (XWindow_t *) wctxt; + UInt_t width; UInt_t height; Int_t xy; - gVirtualX->GetWindowSize(cws, xy, xy, width, height); + GetWindowSize((Drawable_t) ctxt->drawing, xy, xy, width, height); if (x < 0) { w += x; @@ -1398,19 +1379,20 @@ GdkImage *TGWin32::GetBackground(Int_t x, Int_t y, UInt_t w, UInt_t h) if (x+w > width) w = width - x; if (y+h > height) h = height - y; - return gdk_image_get((GdkDrawable*)cws, x, y, w, h); + return gdk_image_get(ctxt->drawing, x, y, w, h); } //////////////////////////////////////////////////////////////////////////////// /// Test if there is really something to render -Bool_t TGWin32::IsVisible(Int_t x, Int_t y, UInt_t w, UInt_t h) +Bool_t TGWin32::IsVisible(WinContext_t wctxt, Int_t x, Int_t y, UInt_t w, UInt_t h) { - Window_t cws = GetCurrentWindow(); + auto ctxt = (XWindow_t *) wctxt; + UInt_t width; UInt_t height; Int_t xy; - gVirtualX->GetWindowSize(cws, xy, xy, width, height); + GetWindowSize((Drawable_t) ctxt->drawing, xy, xy, width, height); // If w or h is 0, very likely the string is only blank characters if ((int)w == 0 || (int)h == 0) return kFALSE; @@ -1426,8 +1408,10 @@ Bool_t TGWin32::IsVisible(Int_t x, Int_t y, UInt_t w, UInt_t h) /// Perform the string rendering in the pad. /// LayoutGlyphs should have been called before. -void TGWin32::RenderString(Int_t x, Int_t y, ETextMode mode) +void TGWin32::RenderString(WinContext_t wctxt, Int_t x, Int_t y, ETextMode mode) { + auto ctxt = (XWindow_t *) wctxt; + TTF::TTGlyph* glyph = TTF::GetGlyphs(); GdkGCValues gcvals; @@ -1436,10 +1420,10 @@ void TGWin32::RenderString(Int_t x, Int_t y, ETextMode mode) Int_t Yoff = 0; if (TTF::GetBox().yMin < 0) Yoff = -TTF::GetBox().yMin; Int_t w = TTF::GetBox().xMax + Xoff; Int_t h = TTF::GetBox().yMax + Yoff; - Int_t x1 = x-Xoff-fAlign.x; - Int_t y1 = y+Yoff+fAlign.y-h; + Int_t x1 = x - Xoff - ctxt->alignVector.x; + Int_t y1 = y + Yoff + ctxt->alignVector.y - h; - if (!IsVisible(x1, y1, w, h)) { + if (!IsVisible(wctxt, x1, y1, w, h)) { return; } @@ -1454,12 +1438,12 @@ void TGWin32::RenderString(Int_t x, Int_t y, ETextMode mode) ULong_t pixel; ULong_t bg; - gdk_gc_get_values((GdkGC*)GetGC(3), &gcvals); + gdk_gc_get_values(ctxt->fGClist[kGCtext], &gcvals); // get the background if (mode == kClear) { // if mode == kClear we need to get an image of the background - GdkImage *bim = GetBackground(x1, y1, w, h); + GdkImage *bim = GetBackground(wctxt, x1, y1, w, h); if (!bim) { Error("DrawText", "error getting background image"); return; @@ -1483,7 +1467,7 @@ void TGWin32::RenderString(Int_t x, Int_t y, ETextMode mode) } else { // if mode == kOpaque its simple, we just draw the background - GdkImage *bim = GetBackground(x1, y1, w, h); + GdkImage *bim = GetBackground(wctxt, x1, y1, w, h); if (!bim) { pixel = gcvals.background.pixel; } else { @@ -1523,8 +1507,7 @@ void TGWin32::RenderString(Int_t x, Int_t y, ETextMode mode) } // put the Ximage on the screen - Window_t cws = GetCurrentWindow(); - gdk_draw_image((GdkDrawable *)cws, GetGC(6), xim, 0, 0, x1, y1, w, h); + gdk_draw_image(ctxt->drawing, ctxt->fGClist[kGCpxmp], xim, 0, 0, x1, y1, w, h); gdk_image_unref(xim); } @@ -1534,8 +1517,12 @@ void TGWin32::RenderString(Int_t x, Int_t y, ETextMode mode) void TGWin32::SetTextFont(Font_t fontnumber) { - fTextFont = fontnumber; - TTF::SetTextFont(fontnumber); + TAttText::SetTextFont(fontnumber); + + TAttText arg(gCws->fAttText); + arg.SetTextFont(fontnumber); + + SetAttText((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -1548,7 +1535,7 @@ void TGWin32::SetTextFont(Font_t fontnumber) /// Set text font to specified name. This function returns 0 if /// the specified font is found, 1 if not. -Int_t TGWin32::SetTextFont(char *fontname, ETextSetMode mode) +Int_t TGWin32::SetTextFont(char *fontname, ETextSetMode /* mode */) { return TTF::SetTextFont(fontname); } @@ -1558,8 +1545,12 @@ Int_t TGWin32::SetTextFont(char *fontname, ETextSetMode mode) void TGWin32::SetTextSize(Float_t textsize) { - fTextSize = textsize; - TTF::SetTextSize(textsize); + TAttText::SetTextSize(textsize); + + TAttText arg(gCws->fAttText); + arg.SetTextSize(textsize); + + SetAttText((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -1567,17 +1558,27 @@ void TGWin32::SetTextSize(Float_t textsize) void TGWin32::ClearWindow() { - if (!fWindows) return; + ClearWindowW((WinContext_t) gCws); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Clear specified window. - if (!gCws->ispixmap && !gCws->double_buffer) { - gdk_window_set_background(gCws->drawing, (GdkColor *) & GetColor(0).color); - gdk_window_clear(gCws->drawing); +void TGWin32::ClearWindowW(WinContext_t wctxt) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; + + if (!ctxt->ispixmap && !ctxt->double_buffer) { + gdk_window_set_background(ctxt->drawing, (GdkColor *) & GetColor(0).color); + gdk_window_clear(ctxt->drawing); GdiFlush(); } else { - SetColor(gGCpxmp, 0); - gdk_win32_draw_rectangle(gCws->drawing, gGCpxmp, 1, - 0, 0, gCws->width, gCws->height); - SetColor(gGCpxmp, 1); + SetColor(ctxt, ctxt->fGClist[kGCpxmp], 0); + gdk_win32_draw_rectangle(ctxt->drawing, ctxt->fGClist[kGCpxmp], 1, + 0, 0, ctxt->width, ctxt->height); + SetColor(ctxt, ctxt->fGClist[kGCpxmp], 1); } } @@ -1586,7 +1587,7 @@ void TGWin32::ClearWindow() void TGWin32::ClosePixmap() { - CloseWindow1(); + CloseWindow(); } //////////////////////////////////////////////////////////////////////////////// @@ -1594,20 +1595,17 @@ void TGWin32::ClosePixmap() void TGWin32::CloseWindow() { - CloseWindow1(); -} - -//////////////////////////////////////////////////////////////////////////////// -/// Delete current window. - -void TGWin32::CloseWindow1() -{ - int wid; + if (!gCws->shared) { + if (gCws->ispixmap) { + gdk_pixmap_unref(gCws->window); + } else { + gdk_window_destroy(gCws->window, kTRUE); + } + } - if (gCws->ispixmap) { - gdk_pixmap_unref(gCws->window); - } else { - gdk_window_destroy(gCws->window, kTRUE); + if (gCws->fillPattern) { + gdk_pixmap_unref(gCws->fillPattern); + gCws->fillPattern = nullptr; } if (gCws->buffer) { @@ -1618,22 +1616,31 @@ void TGWin32::CloseWindow1() (GdkColor *)gCws->new_colors, gCws->ncolors); delete [] gCws->new_colors; - gCws->new_colors = 0; + gCws->new_colors = nullptr; } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_unref(gCws->fGClist[i]); // gdk_gc_unref is equivalent to gdk_gc_destroy + GdiFlush(); gCws->open = 0; - if (!fWindows) return; + for (auto iter = fWindows.begin(); iter != fWindows.end(); ++iter) + if (iter->second.get() == gCws) { + fWindows.erase(iter); + gCws = nullptr; + break; + } + + if (gCws) + Fatal("CloseWindow", "Not found gCws in list of windows"); // make first window in list the current window - for (wid = 0; wid < fMaxNumberOfWindows; wid++) { - if (fWindows[wid].open) { - gCws = &fWindows[wid]; + for (auto iter = fWindows.begin(); iter != fWindows.end(); ++iter) + if (iter->second && iter->second->open) { + gCws = iter->second.get(); return; } - } - gCws = 0; } //////////////////////////////////////////////////////////////////////////////// @@ -1641,10 +1648,11 @@ void TGWin32::CloseWindow1() void TGWin32::CopyPixmap(int wid, int xpos, int ypos) { - if (!fWindows) return; + if (fWindows.count(wid) == 0) + return; - gTws = &fWindows[wid]; - gdk_window_copy_area(gCws->drawing, gGCpxmp, xpos, ypos, gTws->drawing, + gTws = fWindows[wid].get(); + gdk_window_copy_area(gCws->drawing, gTws->fGClist[kGCpxmp], xpos, ypos, gTws->drawing, 0, 0, gTws->width, gTws->height); GdiFlush(); } @@ -1656,7 +1664,19 @@ void TGWin32::CopyPixmap(int wid, int xpos, int ypos) void TGWin32::DrawBox(int x1, int y1, int x2, int y2, EBoxMode mode) { - if (!fWindows) return; + DrawBoxW((WinContext_t) gCws, x1, y1, x2, y2, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw a box on specified window. +/// mode=0 hollow (kHollow) +/// mode=1 solid (kSolid) + +void TGWin32::DrawBoxW(WinContext_t wctxt, int x1, int y1, int x2, int y2, EBoxMode mode) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; Int_t x = TMath::Min(x1, x2); Int_t y = TMath::Min(y1, y2); @@ -1664,21 +1684,16 @@ void TGWin32::DrawBox(int x1, int y1, int x2, int y2, EBoxMode mode) Int_t h = TMath::Abs(y2 - y1); switch (mode) { + case kHollow: + gdk_win32_draw_rectangle(ctxt->drawing, ctxt->fGClist[kGCline], 0, x, y, w, h); + break; - case kHollow: - if (fLineColorModified) UpdateLineColor(); - if (fPenModified) UpdateLineStyle(); - gdk_win32_draw_rectangle(gCws->drawing, gGCline, 0, x, y, w, h); - break; - - case kFilled: - if (fFillStyleModified) UpdateFillStyle(); - if (fFillColorModified) UpdateFillColor(); - gdk_win32_draw_rectangle(gCws->drawing, gGCfill, 1, x, y, w, h); - break; + case kFilled: + gdk_win32_draw_rectangle(ctxt->drawing, ctxt->fGClist[kGCfill], 1, x, y, w, h); + break; - default: - break; + default: + break; } } @@ -1696,28 +1711,23 @@ void TGWin32::DrawBox(int x1, int y1, int x2, int y2, EBoxMode mode) void TGWin32::DrawCellArray(Int_t x1, Int_t y1, Int_t x2, Int_t y2, Int_t nx, Int_t ny, Int_t *ic) { - int i, j, icol, ix, iy, w, h, current_icol; - - if (!fWindows) return; + if (!gCws) return; - current_icol = -1; - w = TMath::Max((x2 - x1) / (nx), 1); - h = TMath::Max((y1 - y2) / (ny), 1); - ix = x1; + int current_icol = -1; + int w = TMath::Max((x2 - x1) / (nx), 1); + int h = TMath::Max((y1 - y2) / (ny), 1); + int ix = x1; - if (fFillStyleModified) UpdateFillStyle(); - if (fFillColorModified) UpdateFillColor(); - - for (i = 0; i < nx; i++) { - iy = y1 - h; - for (j = 0; j < ny; j++) { - icol = ic[i + (nx * j)]; + for (int i = 0; i < nx; i++) { + int iy = y1 - h; + for (int j = 0; j < ny; j++) { + int icol = ic[i + (nx * j)]; if (icol != current_icol) { - gdk_gc_set_foreground(gGCfill, (GdkColor *) & GetColor(icol).color); + gdk_gc_set_foreground(gCws->fGClist[kGCfill], (GdkColor *) & GetColor(icol).color); current_icol = icol; } - gdk_win32_draw_rectangle(gCws->drawing, gGCfill, kTRUE, ix, iy, w, h); + gdk_win32_draw_rectangle(gCws->drawing, gCws->fGClist[kGCfill], kTRUE, ix, iy, w, h); iy = iy - h; } ix = ix + w; @@ -1731,29 +1741,30 @@ void TGWin32::DrawCellArray(Int_t x1, Int_t y1, Int_t x2, Int_t y2, void TGWin32::DrawFillArea(int n, TPoint *xyt) { - int i; - static int lastn = 0; - static GdkPoint *xy = 0; + DrawFillAreaW((WinContext_t) gCws, n, xyt); +} - if (!fWindows) return; +//////////////////////////////////////////////////////////////////////////////// +/// Fill area described by polygon in a specified window. +/// n : number of points +/// xy(2,n) : list of points - if (fFillStyleModified) UpdateFillStyle(); - if (fFillColorModified) UpdateFillColor(); +void TGWin32::DrawFillAreaW(WinContext_t wctxt, int n, TPoint *xyt) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; - if (lastn!=n) { - delete [] (GdkPoint *)xy; - xy = new GdkPoint[n]; - lastn = n; - } - for (i = 0; i < n; i++) { + std::vector xy(n); + for (int i = 0; i < n; i++) { xy[i].x = xyt[i].fX; xy[i].y = xyt[i].fY; } - if (gFillHollow) { - gdk_win32_draw_lines(gCws->drawing, gGCfill, xy, n); + if (ctxt->fillHollow) { + gdk_win32_draw_lines(ctxt->drawing, ctxt->fGClist[kGCfill], xy.data(), n); } else { - gdk_win32_draw_polygon(gCws->drawing, gGCfill, 1, xy, n); + gdk_win32_draw_polygon(ctxt->drawing, ctxt->fGClist[kGCfill], 1, xy.data(), n); } } @@ -1764,24 +1775,25 @@ void TGWin32::DrawFillArea(int n, TPoint *xyt) void TGWin32::DrawLine(Int_t x1, Int_t y1, Int_t x2, Int_t y2) { - if (!fWindows) return; + DrawLineW((WinContext_t) gCws, x1, y1, x2, y2); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw a line on specified window. +/// x1,y1 : begin of line +/// x2,y2 : end of line - if (fLineColorModified) UpdateLineColor(); - if (fPenModified) UpdateLineStyle(); +void TGWin32::DrawLineW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; - if (gLineStyle == GDK_LINE_SOLID) { - gdk_draw_line(gCws->drawing, gGCline, x1, y1, x2, y2); + if (ctxt->lineStyle == GDK_LINE_SOLID) { + gdk_draw_line(ctxt->drawing, ctxt->fGClist[kGCline], x1, y1, x2, y2); } else { - int i; - gint8 dashes[32]; - for (i = 0; i < gDashSize; i++) { - dashes[i] = (gint8) gDashList[i]; - } - for (i = gDashSize; i < 32; i++) { - dashes[i] = (gint8) 0; - } - gdk_gc_set_dashes(gGCdash, gDashOffset, dashes, gDashSize); - gdk_draw_line(gCws->drawing, gGCdash, x1, y1, x2, y2); + gdk_gc_set_dashes(ctxt->fGClist[kGCdash], ctxt->dashOffset, ctxt->dashList.data(), ctxt->dashList.size()); + gdk_draw_line(ctxt->drawing, ctxt->fGClist[kGCdash], x1, y1, x2, y2); } } @@ -1792,55 +1804,63 @@ void TGWin32::DrawLine(Int_t x1, Int_t y1, Int_t x2, Int_t y2) void TGWin32::DrawPolyLine(int n, TPoint * xyt) { - int i; + DrawPolyLineW((WinContext_t) gCws, n, xyt); +} - if (!fWindows) return; +//////////////////////////////////////////////////////////////////////////////// +/// Draw a line through all points in specified window. +/// n : number of points +/// xy : list of points - Point_t *xy = new Point_t[n]; +void TGWin32::DrawPolyLineW(WinContext_t wctxt, int n, TPoint * xyt) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt || (n < 1)) + return; - for (i = 0; i < n; i++) { - xy[i].fX = xyt[i].fX; - xy[i].fY = xyt[i].fY; + std::vector xy(n); + for (int i = 0; i < n; i++) { + xy[i].x = xyt[i].fX; + xy[i].y = xyt[i].fY; } - if (fLineColorModified) UpdateLineColor(); - if (fPenModified) UpdateLineStyle(); - if (n > 1) { - if (gLineStyle == GDK_LINE_SOLID) { - gdk_win32_draw_lines(gCws->drawing, gGCline, (GdkPoint *)xy, n); + if (ctxt->lineStyle == GDK_LINE_SOLID) { + gdk_win32_draw_lines(ctxt->drawing, ctxt->fGClist[kGCline], xy.data(), n); } else { - int i; - gint8 dashes[32]; - - for (i = 0; i < gDashSize; i++) { - dashes[i] = (gint8) gDashList[i]; - } - for (i = gDashSize; i < 32; i++) { - dashes[i] = (gint8) 0; - } - - gdk_gc_set_dashes(gGCdash, gDashOffset, dashes, gDashSize); - gdk_win32_draw_lines(gCws->drawing, (GdkGC*)gGCdash, (GdkPoint *)xy, n); + gdk_gc_set_dashes(ctxt->fGClist[kGCdash], ctxt->dashOffset, ctxt->dashList.data(), ctxt->dashList.size()); + gdk_win32_draw_lines(ctxt->drawing, ctxt->fGClist[kGCdash], xy.data(), n); // calculate length of line to update dash offset - for (i = 1; i < n; i++) { - int dx = xy[i].fX - xy[i - 1].fX; - int dy = xy[i].fY - xy[i - 1].fY; + for (int i = 1; i < n; i++) { + int dx = xy[i].x - xy[i - 1].x; + int dy = xy[i].y - xy[i - 1].y; if (dx < 0) dx = -dx; if (dy < 0) dy = -dy; - gDashOffset += dx > dy ? dx : dy; + ctxt->dashOffset += dx > dy ? dx : dy; } - gDashOffset %= gDashLength; + ctxt->dashOffset %= ctxt->dashLength; } } else { - gdk_win32_draw_points( gCws->drawing, gLineStyle == GDK_LINE_SOLID ? - gGCline : gGCdash, (GdkPoint *)xy,1); + gdk_win32_draw_points(ctxt->drawing, + ctxt->fGClist[ctxt->lineStyle == GDK_LINE_SOLID ? kGCline : kGCdash], + xy.data(), 1); } - delete [] xy; } +//////////////////////////////////////////////////////////////////////////////// +/// Draw N segments on specified window +/// n : number of segments +/// xy : list of points, 2*N size + +void TGWin32::DrawLinesSegmentsW(WinContext_t wctxt, Int_t n, TPoint *xyt) +{ + for(Int_t i = 0; i < 2*n; i += 2) + DrawPolyLineW(wctxt, 2, &xyt[i]); +} + + //////////////////////////////////////////////////////////////////////////////// /// Draw n markers with the current attributes at position x, y. /// n : number of markers to draw @@ -1848,72 +1868,69 @@ void TGWin32::DrawPolyLine(int n, TPoint * xyt) void TGWin32::DrawPolyMarker(int n, TPoint *xyt) { - int i; - static int lastn = 0; - static GdkPoint *xy = 0; - - if (!fWindows) return; + DrawPolyMarkerW((WinContext_t) gCws, n, xyt); +} - if (fMarkerStyleModified) UpdateMarkerStyle(); - if (fMarkerColorModified) UpdateMarkerColor(); +//////////////////////////////////////////////////////////////////////////////// +/// Draw n markers with the current attributes at position x, y in specified window. +/// n : number of markers to draw +/// xy : x,y coordinates of markers - if (lastn!=n) { - delete [] (GdkPoint *)xy; - xy = new GdkPoint[n]; - lastn = n; - } +void TGWin32::DrawPolyMarkerW(WinContext_t wctxt, Int_t n, TPoint *xyt) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) return; - for (i = 0; i < n; i++) { - xy[i].x = xyt[i].fX; - xy[i].y = xyt[i].fY; - } + if ((ctxt->markerShape.size() == 0) && (ctxt->markerSize <= 0)) { + std::vector xy(n); + for (int i = 0; i < n; i++) { + xy[i].x = xyt[i].fX; + xy[i].y = xyt[i].fY; + } - if (gMarker.n <= 0) { - gdk_win32_draw_points(gCws->drawing, gGCmark, xy, n); + gdk_win32_draw_points(ctxt->drawing, ctxt->fGClist[kGCmark], xy.data(), n); } else { - int r = gMarker.n / 2; - int m; + int r = ctxt->markerSize / 2; + auto &shape = ctxt->markerShape; - for (m = 0; m < n; m++) { + for (int m = 0; m < n; m++) { int hollow = 0; - switch (gMarker.type) { - int i; - + switch (ctxt->markerType) { case 0: // hollow circle - gdk_win32_draw_arc(gCws->drawing, gGCmark, kFALSE, xy[m].x-r, xy[m].y-r, - gMarker.n, gMarker.n, 0, 23040); + gdk_win32_draw_arc(ctxt->drawing, ctxt->fGClist[kGCmark], kFALSE, xyt[m].fX - r, xyt[m].fY - r, + ctxt->markerSize, ctxt->markerSize, 0, 23040); break; case 1: // filled circle - gdk_win32_draw_arc(gCws->drawing, gGCmark, kTRUE, xy[m].x-r, xy[m].y-r, - gMarker.n, gMarker.n, 0, 23040); + gdk_win32_draw_arc(ctxt->drawing, ctxt->fGClist[kGCmark], kTRUE, xyt[m].fX - r, xyt[m].fY - r, + ctxt->markerSize, ctxt->markerSize, 0, 23040); break; case 2: // hollow polygon hollow = 1; case 3: // filled polygon - for (i = 0; i < gMarker.n; i++) { - gMarker.xy[i].x += xy[m].x; - gMarker.xy[i].y += xy[m].y; + for (size_t i = 0; i < shape.size(); i++) { + shape[i].x += xyt[m].fX; + shape[i].y += xyt[m].fY; } if (hollow) { - gdk_win32_draw_lines(gCws->drawing, gGCmark, (GdkPoint *)gMarker.xy, gMarker.n); + gdk_win32_draw_lines(ctxt->drawing, ctxt->fGClist[kGCmark], shape.data(), shape.size()); } else { - gdk_win32_draw_polygon(gCws->drawing, gGCmark, 1, (GdkPoint *)gMarker.xy, gMarker.n); + gdk_win32_draw_polygon(ctxt->drawing, ctxt->fGClist[kGCmark], 1, shape.data(), shape.size()); } - for (i = 0; i < gMarker.n; i++) { - gMarker.xy[i].x -= xy[m].x; - gMarker.xy[i].y -= xy[m].y; + for (size_t i = 0; i < shape.size(); i++) { + shape[i].x -= xyt[m].fX; + shape[i].y -= xyt[m].fY; } break; - case 4: // segmented line - for (i = 0; i < gMarker.n; i += 2) { - gdk_draw_line(gCws->drawing, gGCmark, - xy[m].x + gMarker.xy[i].x, - xy[m].y + gMarker.xy[i].y, - xy[m].x + gMarker.xy[i + 1].x, - xy[m].y + gMarker.xy[i + 1].y); + case 4: // segmented line + for (size_t i = 0; i < shape.size(); i += 2) { + gdk_draw_line(ctxt->drawing, ctxt->fGClist[kGCmark], + xyt[m].fX + shape[i].x, + xyt[m].fY + shape[i].y, + xyt[m].fX + shape[i+1].x, + xyt[m].fY + shape[i+1].y); } break; } @@ -1963,7 +1980,11 @@ GdkGC *TGWin32::GetGC(Int_t which) const return 0; } - return gGClist[which]; + if (!gCws) { + Error("GetGC", "No current window selected"); + return nullptr; + } + return gCws->fGClist[which]; } //////////////////////////////////////////////////////////////////////////////// @@ -1971,9 +1992,10 @@ GdkGC *TGWin32::GetGC(Int_t which) const Int_t TGWin32::GetDoubleBuffer(int wid) { - if (!fWindows) return 0; + if (fWindows.count(wid) == 0) + return 0; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); if (!gTws->open) { return -1; @@ -1992,8 +2014,6 @@ Int_t TGWin32::GetDoubleBuffer(int wid) void TGWin32::GetGeometry(int wid, int &x, int &y, unsigned int &w, unsigned int &h) { - if (!fWindows) return; - if (wid < 0) { x = 0; y = 0; @@ -2004,7 +2024,9 @@ void TGWin32::GetGeometry(int wid, int &x, int &y, unsigned int &w, int depth; int width, height; - gTws = &fWindows[wid]; + if (fWindows.count(wid) == 0) return; + + gTws = fWindows[wid].get(); gdk_window_get_geometry((GdkDrawable *) gTws->window, &x, &y, &width, &height, &depth); @@ -2071,8 +2093,8 @@ void TGWin32::GetTextExtent(UInt_t &w, UInt_t &h, char *mess) Window_t TGWin32::GetWindowID(int wid) { - if (!fWindows) return 0; - return (Window_t) fWindows[wid].window; + if (fWindows.count(wid) == 0) return 0; + return (Window_t) fWindows[wid]->window; } //////////////////////////////////////////////////////////////////////////////// @@ -2083,14 +2105,61 @@ Window_t TGWin32::GetWindowID(int wid) void TGWin32::MoveWindow(Int_t wid, Int_t x, Int_t y) { - if (!fWindows) return; + if (fWindows.count(wid) == 0) return; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); if (!gTws->open) return; gdk_window_move((GdkDrawable *) gTws->window, x, y); } +//////////////////////////////////////////////////////////////////////////////// +/// Add new window handle +/// Only for private usage + +Int_t TGWin32::AddWindowHandle() +{ + Int_t maxid = 0; + for (auto & pair : fWindows) { + if (pair.first > maxid) + maxid = pair.first; + } + + if (fWindows.size() == (size_t) maxid) { + // all ids are in use - just add maximal+1 + maxid++; + } else + for (int id = 1; id < maxid; id++) { + if (fWindows.count(id) == 0) { + maxid = id; + break; + } + } + + fWindows.emplace(maxid, std::make_unique()); + + auto ctxt = fWindows[maxid].get(); + ctxt->open = 1; + ctxt->shared = 0; + ctxt->drawing = nullptr; + ctxt->window = nullptr; + ctxt->buffer = nullptr; + ctxt->new_colors = nullptr; + ctxt->ncolors = 0; + + ctxt->drawMode = TVirtualX::kCopy; + + // Create primitives graphic contexts + for (int i = 0; i < kMAXGC; i++) { + ctxt->fGClist[i] = gdk_gc_new(GDK_ROOT_PARENT()); + gdk_gc_set_foreground(ctxt->fGClist[i], &GetColor(1).color); + gdk_gc_set_background(ctxt->fGClist[i], &GetColor(0).color); + } + + return maxid; +} + + //////////////////////////////////////////////////////////////////////////////// /// Open a new pixmap. /// w,h : Width and height of the pixmap. @@ -2098,54 +2167,33 @@ void TGWin32::MoveWindow(Int_t wid, Int_t x, Int_t y) Int_t TGWin32::OpenPixmap(unsigned int w, unsigned int h) { int wval, hval; - int i, wid; int ww, hh, depth; wval = w; hval = h; - // Select next free window number - again: - for (wid = 0; wid < fMaxNumberOfWindows; wid++) { - if (!fWindows[wid].open) { - fWindows[wid].open = 1; - gCws = &fWindows[wid]; - break; - } - } - if (wid == fMaxNumberOfWindows) { - int newsize = fMaxNumberOfWindows + 10; - fWindows = (XWindow_t *) TStorage::ReAlloc(fWindows, - newsize * sizeof(XWindow_t), - fMaxNumberOfWindows * - sizeof(XWindow_t)); + int wid = AddWindowHandle(); - for (i = fMaxNumberOfWindows; i < newsize; i++) fWindows[i].open = 0; - fMaxNumberOfWindows = newsize; - goto again; - } + gCws = fWindows[wid].get(); + gCws->ispixmap = 1; - depth =gdk_visual_get_best_depth(); + depth = gdk_visual_get_best_depth(); gCws->window = (GdkPixmap *) gdk_pixmap_new(GDK_ROOT_PARENT(),wval,hval,depth); gdk_drawable_get_size((GdkDrawable *) gCws->window, &ww, &hh); - for (i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_mask((GdkGC *) gGClist[i], (GdkDrawable *)None); - } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_mask(gCws->fGClist[i], (GdkDrawable *)None); - SetColor(gGCpxmp, 0); - gdk_win32_draw_rectangle(gCws->window,(GdkGC *)gGCpxmp, kTRUE, + SetColor(gCws, gCws->fGClist[kGCpxmp], 0); + gdk_win32_draw_rectangle(gCws->window, gCws->fGClist[kGCpxmp], kTRUE, 0, 0, ww, hh); - SetColor(gGCpxmp, 1); + SetColor(gCws, gCws->fGClist[kGCpxmp], 1); // Initialise the window structure gCws->drawing = gCws->window; - gCws->buffer = 0; gCws->double_buffer = 0; - gCws->ispixmap = 1; gCws->clip = 0; gCws->width = wval; gCws->height = hval; - gCws->new_colors = 0; return wid; } @@ -2158,41 +2206,18 @@ Int_t TGWin32::InitWindow(ULongptr_t win) { GdkWindowAttr attributes; unsigned long attr_mask = 0; - int wid; int xval, yval; int wval, hval, depth; - GdkWindow *wind = (GdkWindow *) win; - - gdk_window_get_geometry(wind, &xval, &yval, &wval, &hval, &depth); + int wid = AddWindowHandle(); - // Select next free window number - - again: - for (wid = 0; wid < fMaxNumberOfWindows; wid++) { - if (!fWindows[wid].open) { - fWindows[wid].open = 1; - fWindows[wid].double_buffer = 0; - gCws = &fWindows[wid]; - break; - } - } + gCws = fWindows[wid].get(); + gCws->ispixmap = 0; - if (wid == fMaxNumberOfWindows) { - int newsize = fMaxNumberOfWindows + 10; - fWindows = - (XWindow_t *) TStorage::ReAlloc(fWindows, - newsize * sizeof(XWindow_t), - fMaxNumberOfWindows * - sizeof(XWindow_t)); + GdkWindow *wind = (GdkWindow *) win; - for (int i = fMaxNumberOfWindows; i < newsize; i++) { - fWindows[i].open = 0; - } + gdk_window_get_geometry(wind, &xval, &yval, &wval, &hval, &depth); - fMaxNumberOfWindows = newsize; - goto again; - } // Create window attributes.wclass = GDK_INPUT_OUTPUT; attributes.event_mask = 0L; //GDK_ALL_EVENTS_MASK; @@ -2241,13 +2266,10 @@ Int_t TGWin32::InitWindow(ULongptr_t win) // Initialise the window structure gCws->drawing = gCws->window; - gCws->buffer = 0; gCws->double_buffer = 0; - gCws->ispixmap = 0; gCws->clip = 0; gCws->width = wval; gCws->height = hval; - gCws->new_colors = 0; return wid; } @@ -2696,11 +2718,9 @@ Int_t TGWin32::RequestString(int x, int y, char *text) void TGWin32::RescaleWindow(int wid, unsigned int w, unsigned int h) { - int i; + if (fWindows.count(wid) == 0) return; - if (!fWindows) return; - - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); if (!gTws->open) return; @@ -2717,14 +2737,14 @@ void TGWin32::RescaleWindow(int wid, unsigned int w, unsigned int h) gTws->buffer = gdk_pixmap_new(GDK_ROOT_PARENT(), // NULL, w, h, gdk_visual_get_best_depth()); } - for (i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_mask(gGClist[i], (GdkBitmap *)None); - } - SetColor(gGCpxmp, 0); - gdk_win32_draw_rectangle(gTws->buffer, gGCpxmp, 1, 0, 0, w, h); - SetColor(gGCpxmp, 1); + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_mask(gTws->fGClist[i], (GdkBitmap *)None); + SetColor(gTws, gTws->fGClist[kGCpxmp], 0); + gdk_win32_draw_rectangle(gTws->buffer, gTws->fGClist[kGCpxmp], 1, 0, 0, w, h); + SetColor(gTws, gTws->fGClist[kGCpxmp], 1); - if (gTws->double_buffer) gTws->drawing = gTws->buffer; + if (gTws->double_buffer) + gTws->drawing = gTws->buffer; } gTws->width = w; gTws->height = h; @@ -2738,14 +2758,13 @@ void TGWin32::RescaleWindow(int wid, unsigned int w, unsigned int h) int TGWin32::ResizePixmap(int wid, unsigned int w, unsigned int h) { int wval, hval; - int i; int ww, hh, depth; wval = w; hval = h; - if (!fWindows) return 0; + if (fWindows.count(wid) == 0) return 0; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); // don't do anything when size did not change // if (gTws->width == wval && gTws->height == hval) return 0; @@ -2765,13 +2784,12 @@ int TGWin32::ResizePixmap(int wid, unsigned int w, unsigned int h) gdk_drawable_get_size(gTws->window, &ww, &hh); - for (i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_mask((GdkGC *) gGClist[i], (GdkDrawable *)None); - } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_mask(gTws->fGClist[i], (GdkDrawable *)None); - SetColor(gGCpxmp, 0); - gdk_win32_draw_rectangle(gTws->window,(GdkGC *)gGCpxmp, kTRUE, 0, 0, ww, hh); - SetColor(gGCpxmp, 1); + SetColor(gTws, gTws->fGClist[kGCpxmp], 0); + gdk_win32_draw_rectangle(gTws->window, gTws->fGClist[kGCpxmp], kTRUE, 0, 0, ww, hh); + SetColor(gTws, gTws->fGClist[kGCpxmp], 1); // Initialise the window structure gTws->drawing = gTws->window; @@ -2785,16 +2803,13 @@ int TGWin32::ResizePixmap(int wid, unsigned int w, unsigned int h) void TGWin32::ResizeWindow(Int_t wid) { - int i; - int xval = 0, yval = 0; - GdkWindow *win, *root = NULL; - int wval = 0, hval = 0, depth = 0; + if (fWindows.count(wid) == 0) return; - if (!fWindows) return; + int xval = 0, yval = 0, wval = 0, hval = 0, depth = 0; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); - win = (GdkWindow *) gTws->window; + auto win = (GdkWindow *) gTws->window; gdk_window_get_geometry(win, &xval, &yval, &wval, &hval, &depth); @@ -2813,16 +2828,16 @@ void TGWin32::ResizeWindow(Int_t wid) wval, hval, depth); } - for (i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_mask((GdkGC *) gGClist[i], (GdkDrawable *)None); - } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_mask(gTws->fGClist[i], (GdkDrawable *)None); - SetColor(gGCpxmp, 0); - gdk_win32_draw_rectangle(gTws->buffer,(GdkGC *)gGCpxmp, kTRUE, 0, 0, wval, hval); + SetColor(gTws, gTws->fGClist[kGCpxmp], 0); + gdk_win32_draw_rectangle(gTws->buffer, gTws->fGClist[kGCpxmp], kTRUE, 0, 0, wval, hval); - SetColor(gGCpxmp, 1); + SetColor(gTws, gTws->fGClist[kGCpxmp], 1); - if (gTws->double_buffer) gTws->drawing = gTws->buffer; + if (gTws->double_buffer) + gTws->drawing = gTws->buffer; } gTws->width = wval; @@ -2834,28 +2849,24 @@ void TGWin32::ResizeWindow(Int_t wid) void TGWin32::SelectWindow(int wid) { - int i; - GdkRectangle rect; + if (fWindows.count(wid) == 0) return; - if (!fWindows || wid < 0 || wid >= fMaxNumberOfWindows || !fWindows[wid].open) { - return; - } + if (!fWindows[wid]->open) return; - gCws = &fWindows[wid]; + gCws = fWindows[wid].get(); if (gCws->clip && !gCws->ispixmap && !gCws->double_buffer) { + GdkRectangle rect; rect.x = gCws->xclip; rect.y = gCws->yclip; rect.width = gCws->wclip; rect.height = gCws->hclip; - for (i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_rectangle((GdkGC *) gGClist[i], &rect); - } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_rectangle(gCws->fGClist[i], &rect); } else { - for (i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_mask((GdkGC *) gGClist[i], (GdkDrawable *)None); - } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_mask(gCws->fGClist[i], (GdkDrawable *)None); } } @@ -2893,14 +2904,13 @@ void TGWin32::SetCharacterUp(Float_t chupx, Float_t chupy) void TGWin32::SetClipOFF(int wid) { - if (!fWindows) return; + if (fWindows.count(wid) == 0) return; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); gTws->clip = 0; - for (int i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_mask((GdkGC *) gGClist[i], (GdkDrawable *)None); - } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_mask(gTws->fGClist[i], (GdkDrawable *)None); } //////////////////////////////////////////////////////////////////////////////// @@ -2912,25 +2922,24 @@ void TGWin32::SetClipOFF(int wid) void TGWin32::SetClipRegion(int wid, int x, int y, unsigned int w, unsigned int h) { - if (!fWindows) return; + if (fWindows.count(wid) == 0) return; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); gTws->xclip = x; gTws->yclip = y; gTws->wclip = w; gTws->hclip = h; gTws->clip = 1; - GdkRectangle rect; if (gTws->clip && !gTws->ispixmap && !gTws->double_buffer) { + GdkRectangle rect; rect.x = gTws->xclip; rect.y = gTws->yclip; rect.width = gTws->wclip; rect.height = gTws->hclip; - for (int i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_rectangle((GdkGC *)gGClist[i], &rect); - } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_rectangle(gTws->fGClist[i], &rect); } } @@ -2949,12 +2958,12 @@ ULong_t TGWin32::GetPixel(Color_t ci) //////////////////////////////////////////////////////////////////////////////// /// Set the foreground color in GdkGC. -void TGWin32::SetColor(GdkGC *gc, int ci) +void TGWin32::SetColor(XWindow_t *ctxt, GdkGC *gc, int ci) { GdkGCValues gcvals; GdkColor color; - if (ci<=0) ci = 10; //white + if (ci <= 0) ci = 10; //white TColor *clr = gROOT->GetColor(ci); if (clr) @@ -2967,7 +2976,7 @@ void TGWin32::SetColor(GdkGC *gc, int ci) col = GetColor(0); } - if (fDrawMode == kXor) { + if (ctxt && ctxt->drawMode == kXor) { gdk_gc_get_values(gc, &gcvals); color.pixel = col.color.pixel ^ gcvals.background.pixel; @@ -2993,9 +3002,9 @@ void TGWin32::SetColor(GdkGC *gc, int ci) void TGWin32::SetCursor(Int_t wid, ECursor cursor) { - if (!fWindows) return; + if (fWindows.count(wid) == 0) return; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); gdk_window_set_cursor((GdkWindow *)gTws->window, (GdkCursor *)fCursors[cursor]); } @@ -3025,11 +3034,9 @@ void TGWin32::SetCursor(Window_t id, Cursor_t curid) void TGWin32::SetDoubleBuffer(int wid, int mode) { - if (!fWindows) return; - if (wid == 999) { - for (int i = 0; i < fMaxNumberOfWindows; i++) { - gTws = &fWindows[i]; + for (auto & pair : fWindows) { + gTws = pair.second.get(); if (gTws->open) { switch (mode) { case 1: @@ -3042,7 +3049,9 @@ void TGWin32::SetDoubleBuffer(int wid, int mode) } } } else { - gTws = &fWindows[wid]; + if (fWindows.count(wid) == 0) return; + + gTws = fWindows[wid].get(); if (!gTws->open) return; switch (mode) { @@ -3071,20 +3080,19 @@ void TGWin32::SetDoubleBufferOFF() void TGWin32::SetDoubleBufferON() { - if (!fWindows || gTws->double_buffer || gTws->ispixmap) return; + if (fWindows.size() == 0 || !gTws || gTws->double_buffer || gTws->ispixmap) return; if (!gTws->buffer) { gTws->buffer = gdk_pixmap_new(GDK_ROOT_PARENT(), //NULL, gTws->width, gTws->height, gdk_visual_get_best_depth()); - SetColor(gGCpxmp, 0); - gdk_win32_draw_rectangle(gTws->buffer, gGCpxmp, 1, 0, 0, gTws->width, - gTws->height); - SetColor(gGCpxmp, 1); - } - for (int i = 0; i < kMAXGC; i++) { - gdk_gc_set_clip_mask(gGClist[i], (GdkBitmap *)None); + SetColor(gTws, gTws->fGClist[kGCpxmp], 0); + gdk_win32_draw_rectangle(gTws->buffer, gTws->fGClist[kGCpxmp], 1, 0, 0, + gTws->width, gTws->height); + SetColor(gTws, gTws->fGClist[kGCpxmp], 1); } + for (int i = 0; i < kMAXGC; i++) + gdk_gc_set_clip_mask(gTws->fGClist[i], (GdkBitmap *)None); gTws->double_buffer = 1; gTws->drawing = gTws->buffer; } @@ -3100,29 +3108,7 @@ void TGWin32::SetDoubleBufferON() void TGWin32::SetDrawMode(EDrawMode mode) { - int i; - - switch (mode) { - case kCopy: - for (i = 0; i < kMAXGC; i++) { - if (gGClist[i]) - gdk_gc_set_function(gGClist[i], GDK_COPY); - } - break; - case kXor: - for (i = 0; i < kMAXGC; i++) { - if (gGClist[i]) - gdk_gc_set_function(gGClist[i], GDK_XOR); - } - break; - case kInvert: - for (i = 0; i < kMAXGC; i++) { - if (gGClist[i]) - gdk_gc_set_function(gGClist[i], GDK_INVERT); - } - break; - } - fDrawMode = mode; + SetDrawModeW((WinContext_t) gCws, mode); } //////////////////////////////////////////////////////////////////////////////// @@ -3130,31 +3116,14 @@ void TGWin32::SetDrawMode(EDrawMode mode) void TGWin32::SetFillColor(Color_t cindex) { - Int_t indx = Int_t(cindex); + if (cindex < 0) return; - if (!gStyle->GetFillColor() && cindex > 1) { - indx = 0; - } - - fFillColor = indx; - fFillColorModified = kTRUE; -} + TAttFill::SetFillColor(cindex); -//////////////////////////////////////////////////////////////////////////////// -/// + TAttFill arg(gCws->fAttFill); + arg.SetFillColor(cindex); -void TGWin32::UpdateFillColor() -{ - if (fFillColor >= 0) { - SetColor(gGCfill, fFillColor); - } - - // invalidate fill pattern - if (gFillPattern != NULL) { - gdk_pixmap_unref(gFillPattern); - gFillPattern = NULL; - } - fFillColorModified = kFALSE; + SetAttFill((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -3164,58 +3133,77 @@ void TGWin32::UpdateFillColor() void TGWin32::SetFillStyle(Style_t fstyle) { - if (fFillStyle==fstyle) return; + TAttFill::SetFillStyle(fstyle); + + TAttFill arg(gCws->fAttFill); + arg.SetFillStyle(fstyle); - fFillStyle = fstyle; - fFillStyleModified = kTRUE; + SetAttFill((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// -/// Set fill area style index. +/// Return current fill style +/// FIXME: Only as temporary solution while some code analyze current fill style -void TGWin32::UpdateFillStyle() +Style_t TGWin32::GetFillStyle() const { - static int current_fasi = 0; + return gCws ? gCws->fAttFill.GetFillStyle() : TAttFill::GetFillStyle(); +} - Int_t style = fFillStyle / 1000; - Int_t fasi = fFillStyle % 1000; +//////////////////////////////////////////////////////////////////////////////// +/// Set fill attributes for specified window - switch (style) { +void TGWin32::SetAttFill(WinContext_t wctxt, const TAttFill &att) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; + + Int_t cindex = att.GetFillColor(); + if (!gStyle->GetFillColor() && cindex > 1) + cindex = 0; + if (cindex >= 0) + SetColor(ctxt, ctxt->fGClist[kGCfill], Int_t(cindex)); + ctxt->fAttFill.SetFillColor(cindex); - case 1: // solid - gFillHollow = 0; - gdk_gc_set_fill(gGCfill, GDK_SOLID); - break; + Int_t style = att.GetFillStyle() / 1000; + Int_t fasi = att.GetFillStyle() % 1000; + + switch (style) { + case 1: // solid + ctxt->fillHollow = 0; + gdk_gc_set_fill(ctxt->fGClist[kGCfill], GDK_SOLID); + break; - case 2: // pattern - gFillHollow = 1; - break; + case 2: // pattern + ctxt->fillHollow = 1; + break; - case 3: // hatch - gFillHollow = 0; - gdk_gc_set_fill(gGCfill, GDK_STIPPLED); + case 3: // hatch + ctxt->fillHollow = 0; + gdk_gc_set_fill(ctxt->fGClist[kGCfill], GDK_STIPPLED); - if (fasi != current_fasi) { - if (gFillPattern != NULL) { - gdk_pixmap_unref(gFillPattern); - gFillPattern = NULL; + if (fasi != ctxt->fillFasi) { + if (ctxt->fillPattern) { + gdk_pixmap_unref(ctxt->fillPattern); + ctxt->fillPattern = nullptr; + } + int stn = (fasi >= 1 && fasi <=25) ? fasi : 2; + char pattern[32]; + for (int i = 0; i < 32; ++i) + pattern[i] = ~gStipples[stn][i]; + ctxt->fillPattern = gdk_bitmap_create_from_data(GDK_ROOT_PARENT(), + (const char *)&pattern, 16, 16); + gdk_gc_set_stipple(ctxt->fGClist[kGCfill], ctxt->fillPattern); + ctxt->fillFasi = fasi; } - int stn = (fasi >= 1 && fasi <=25) ? fasi : 2; - char pattern[32]; - for (int i=0;i<32;++i) - pattern[i] = ~gStipples[stn][i]; - gFillPattern = gdk_bitmap_create_from_data(GDK_ROOT_PARENT(), - (const char *)&pattern, 16, 16); - gdk_gc_set_stipple(gGCfill, gFillPattern); - current_fasi = fasi; - } - break; + break; - default: - gFillHollow = 1; + default: + ctxt->fillHollow = 1; } - fFillStyleModified = kFALSE; + ctxt->fAttFill.SetFillStyle(att.GetFillStyle()); } //////////////////////////////////////////////////////////////////////////////// @@ -3231,20 +3219,14 @@ void TGWin32::SetInput(int inp) void TGWin32::SetLineColor(Color_t cindex) { - if ((cindex < 0) || (cindex==fLineColor)) return; + if (cindex < 0) return; - fLineColor = cindex; - fLineColorModified = kTRUE; -} + TAttLine::SetLineColor(cindex); -//////////////////////////////////////////////////////////////////////////////// -/// + TAttLine arg(gCws->fAttLine); + arg.SetLineColor(cindex); -void TGWin32::UpdateLineColor() -{ - SetColor(gGCline, Int_t(fLineColor)); - SetColor(gGCdash, Int_t(fLineColor)); - fLineColorModified = kFALSE; + SetAttLine((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -3257,31 +3239,9 @@ void TGWin32::UpdateLineColor() /// e.g. N=4,DASH=(6,3,1,3) gives a dashed-dotted line with dash length 6 /// and a gap of 7 between dashes -void TGWin32::SetLineType(int n, int *dash) +void TGWin32::SetLineType(int /* n */ , int * /* dash */) { - if (n <= 0) { - gLineStyle = GDK_LINE_SOLID; - gdk_gc_set_line_attributes(gGCline, gLineWidth, - (GdkLineStyle)gLineStyle, - (GdkCapStyle) gCapStyle, - (GdkJoinStyle) gJoinStyle); - } else { - int i; - gDashSize = TMath::Min((int)sizeof(gDashList),n); - gDashLength = 0; - for (i = 0; i < gDashSize; i++) { - gDashList[i] = dash[i]; - gDashLength += gDashList[i]; - } - gDashOffset = 0; - gLineStyle = GDK_LINE_ON_OFF_DASH; - if (gLineWidth == 0) gLineWidth =1; - gdk_gc_set_line_attributes(gGCdash, gLineWidth, - (GdkLineStyle) gLineStyle, - (GdkCapStyle) gCapStyle, - (GdkJoinStyle) gJoinStyle); - } - fPenModified = kFALSE; + Warning("SetLineType", "DEPRECATED, use SetAttLine() instead"); } //////////////////////////////////////////////////////////////////////////////// @@ -3289,45 +3249,21 @@ void TGWin32::SetLineType(int n, int *dash) void TGWin32::SetLineStyle(Style_t lstyle) { - if (fLineStyle == lstyle) return; + TAttLine::SetLineStyle(lstyle); + + TAttLine arg(gCws->fAttLine); + arg.SetLineStyle(lstyle); - fLineStyle = lstyle; - fPenModified = kTRUE; + SetAttLine((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// -/// Update line style +/// Return current line style +/// FIXME: Only as temporary solution while some code analyze current line style -void TGWin32::UpdateLineStyle() +Style_t TGWin32::GetLineStyle() const { - static Int_t dashed[2] = { 3, 3 }; - static Int_t dotted[2] = { 1, 2 }; - static Int_t dasheddotted[4] = { 3, 4, 1, 4 }; - - if (fLineStyle <= 1) { - SetLineType(0, 0); - } else if (fLineStyle == 2) { - SetLineType(2, dashed); - } else if (fLineStyle == 3) { - SetLineType(2, dotted); - } else if (fLineStyle == 4) { - SetLineType(4, dasheddotted); - } else { - TString st = (TString)gStyle->GetLineStyleString(fLineStyle); - TObjArray *tokens = st.Tokenize(" "); - Int_t nt; - nt = tokens->GetEntries(); - Int_t *linestyle = new Int_t[nt]; - for (Int_t j = 0; jAt(j))->GetName(), "%d", &it); - linestyle[j] = (Int_t)(it/4); - } - SetLineType(nt,linestyle); - delete [] linestyle; - delete tokens; - } - fPenModified = kFALSE; + return gCws ? gCws->fAttLine.GetLineStyle() : TAttLine::GetLineStyle(); } //////////////////////////////////////////////////////////////////////////////// @@ -3336,32 +3272,96 @@ void TGWin32::UpdateLineStyle() void TGWin32::SetLineWidth(Width_t width) { - if (fLineWidth == width) return; - fLineWidth = width; + TAttLine::SetLineWidth(width); - if (width == 1 && gLineStyle == GDK_LINE_SOLID) gLineWidth = 0; - else gLineWidth = width; + TAttLine arg(gCws->fAttLine); + arg.SetLineWidth(width); - fPenModified = kTRUE; + SetAttLine((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// -/// Set color index for markers. +/// Return current line width +/// FIXME: Only as temporary solution while some code analyze current line wide -void TGWin32::SetMarkerColor(Color_t cindex) +Width_t TGWin32::GetLineWidth() const { - if ((cindex<0) || (cindex==fMarkerColor)) return; - fMarkerColor = cindex; - fMarkerColorModified = kTRUE; + return gCws ? gCws->fAttLine.GetLineWidth() : TAttLine::GetLineWidth(); } //////////////////////////////////////////////////////////////////////////////// -/// +/// Set line attributes for specified window. + +void TGWin32::SetAttLine(WinContext_t wctxt, const TAttLine &att) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; + + SetColor(ctxt, ctxt->fGClist[kGCline], att.GetLineColor()); + SetColor(ctxt, ctxt->fGClist[kGCdash], att.GetLineColor()); + + if (ctxt->fAttLine.GetLineStyle() != att.GetLineStyle()) { //set style index only if different + if (att.GetLineStyle() <= 1) + ctxt->dashList.clear(); + else if (att.GetLineStyle() == 2) + ctxt->dashList = { 3, 3 }; + else if (att.GetLineStyle() == 3) + ctxt->dashList = { 1, 2 }; + else if (att.GetLineStyle() == 4) { + ctxt->dashList = { 3, 4, 1, 4} ; + } else { + TString st = (TString)gStyle->GetLineStyleString(att.GetLineStyle()); + auto tokens = st.Tokenize(" "); + Int_t nt = tokens->GetEntries(); + ctxt->dashList.resize(nt); + for (Int_t j = 0; j < nt; ++j) { + Int_t it; + sscanf(tokens->At(j)->GetName(), "%d", &it); + ctxt->dashList[j] = (Int_t) (it/4); + } + delete tokens; + } + ctxt->dashLength = 0; + for (auto elem : ctxt->dashList) + ctxt->dashLength += elem; + ctxt->dashOffset = 0; + ctxt->lineStyle = ctxt->dashList.size() == 0 ? GDK_LINE_SOLID : GDK_LINE_ON_OFF_DASH; + } + + ctxt->lineWidth = att.GetLineWidth(); + if (ctxt->lineWidth == 1 && ctxt->lineStyle == GDK_LINE_SOLID) + ctxt->lineWidth = 0; + + if (ctxt->lineStyle == GDK_LINE_SOLID) { + gdk_gc_set_line_attributes(ctxt->fGClist[kGCline], ctxt->lineWidth, + ctxt->lineStyle, + (GdkCapStyle) gCapStyle, + (GdkJoinStyle) gJoinStyle); + } else { + gdk_gc_set_line_attributes(ctxt->fGClist[kGCdash], ctxt->lineWidth, + ctxt->lineStyle, + (GdkCapStyle) gCapStyle, + (GdkJoinStyle) gJoinStyle); + } + + ctxt->fAttLine = att; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// Set color index for markers. -void TGWin32::UpdateMarkerColor() +void TGWin32::SetMarkerColor(Color_t cindex) { - SetColor(gGCmark, Int_t(fMarkerColor)); - fMarkerColorModified = kFALSE; + if (cindex < 0) return; + + TAttMarker::SetMarkerColor(cindex); + + TAttMarker arg(gCws->fAttMarker); + arg.SetMarkerColor(cindex); + + SetAttMarker((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -3370,35 +3370,15 @@ void TGWin32::UpdateMarkerColor() void TGWin32::SetMarkerSize(Float_t msize) { - if ((msize==fMarkerSize) || (msize<0)) return; + if ((msize == TAttMarker::GetMarkerSize()) || (msize < 0)) + return; - fMarkerSize = msize; - SetMarkerStyle(-fMarkerStyle); -} + TAttMarker::SetMarkerSize(msize); -//////////////////////////////////////////////////////////////////////////////// -/// Set marker type. -/// type : marker type -/// n : length of marker description -/// xy : list of points describing marker shape -/// -/// if n == 0 marker is a single point -/// if TYPE == 0 marker is hollow circle of diameter N -/// if TYPE == 1 marker is filled circle of diameter N -/// if TYPE == 2 marker is a hollow polygon describe by line XY -/// if TYPE == 3 marker is a filled polygon describe by line XY -/// if TYPE == 4 marker is described by segmented line XY -/// e.g. TYPE=4,N=4,XY=(-3,0,3,0,0,-3,0,3) sets a plus shape of 7x7 pixels + TAttMarker arg(gCws->fAttMarker); + arg.SetMarkerSize(msize); -void TGWin32::SetMarkerType(int type, int n, GdkPoint * xy) -{ - gMarker.type = type; - gMarker.n = n < kMAXMK ? n : kMAXMK; - if (gMarker.type >= 2) { - for (int i = 0; i < gMarker.n; i++) { - gMarker.xy[i] = xy[i]; - } - } + SetAttMarker((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -3406,36 +3386,59 @@ void TGWin32::SetMarkerType(int type, int n, GdkPoint * xy) void TGWin32::SetMarkerStyle(Style_t markerstyle) { - if (fMarkerStyle == markerstyle) return; - fMarkerStyle = TMath::Abs(markerstyle); - fMarkerStyleModified = kTRUE; + if (TAttMarker::GetMarkerStyle() == markerstyle) + return; + + TAttMarker::SetMarkerStyle(markerstyle); + + TAttMarker arg(gCws->fAttMarker); + arg.SetMarkerStyle(markerstyle); + + SetAttMarker((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// -/// +/// Set marker attributes for speicfied window -void TGWin32::UpdateMarkerStyle() +void TGWin32::SetAttMarker(WinContext_t wctxt, const TAttMarker &att) { - Style_t markerstyle = TAttMarker::GetMarkerStyleBase(fMarkerStyle); - gMarkerLineWidth = TAttMarker::GetMarkerLineWidth(fMarkerStyle); + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; + + SetColor(ctxt, ctxt->fGClist[kGCmark], att.GetMarkerColor()); + + Bool_t changed = (att.GetMarkerSize() != ctxt->fAttMarker.GetMarkerSize()) || + (att.GetMarkerStyle() != ctxt->fAttMarker.GetMarkerStyle()); + + ctxt->fAttMarker = att; + + if (!changed) + return; + + auto markerstyle = TAttMarker::GetMarkerStyleBase(att.GetMarkerStyle()); + ctxt->markerLineWidth = TAttMarker::GetMarkerLineWidth(att.GetMarkerStyle()); // The fast pixel markers need to be treated separately if (markerstyle == 1 || markerstyle == 6 || markerstyle == 7) { - gdk_gc_set_line_attributes(gGCmark, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); + gdk_gc_set_line_attributes(ctxt->fGClist[kGCmark], 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); } else { - gdk_gc_set_line_attributes(gGCmark, gMarkerLineWidth, - (GdkLineStyle) gMarkerLineStyle, - (GdkCapStyle) gMarkerCapStyle, - (GdkJoinStyle) gMarkerJoinStyle); + gdk_gc_set_line_attributes(ctxt->fGClist[kGCmark], ctxt->markerLineWidth, + (GdkLineStyle) gMarkerLineStyle, + (GdkCapStyle) gMarkerCapStyle, + (GdkJoinStyle) gMarkerJoinStyle); } - static GdkPoint shape[30]; + Float_t markerSizeReduced = att.GetMarkerSize() - TMath::Floor(ctxt->markerLineWidth/2.)/4.; + Int_t im = Int_t(4 * markerSizeReduced + 0.5); - Float_t MarkerSizeReduced = fMarkerSize - TMath::Floor(gMarkerLineWidth/2.)/4.; - Int_t im = Int_t(4 * MarkerSizeReduced + 0.5); + auto& shape = ctxt->markerShape; + ctxt->markerSize = 0; + ctxt->markerType = 0; if (markerstyle == 2) { // + shaped marker + shape.resize(4); shape[0].x = -im; shape[0].y = 0; shape[1].x = im; @@ -3444,9 +3447,10 @@ void TGWin32::UpdateMarkerStyle() shape[2].y = -im; shape[3].x = 0; shape[3].y = im; - SetMarkerType(4, 4, shape); + ctxt->markerType = 4; } else if (markerstyle == 3 || markerstyle == 31) { // * shaped marker + shape.resize(8); shape[0].x = -im; shape[0].y = 0; shape[1].x = im; @@ -3464,13 +3468,16 @@ void TGWin32::UpdateMarkerStyle() shape[6].y = im; shape[7].x = im; shape[7].y = -im; - SetMarkerType(4, 8, shape); + ctxt->markerType = 4; } else if (markerstyle == 4 || markerstyle == 24) { // O shaped marker - SetMarkerType(0, im * 2, shape); + shape.resize(0); + ctxt->markerType = 0; + ctxt->markerSize = im * 2; } else if (markerstyle == 5) { // X shaped marker im = Int_t(0.707 * Float_t(im) + 0.5); + shape.resize(4); shape[0].x = -im; shape[0].y = -im; shape[1].x = im; @@ -3479,9 +3486,10 @@ void TGWin32::UpdateMarkerStyle() shape[2].y = im; shape[3].x = im; shape[3].y = -im; - SetMarkerType(4, 4, shape); + ctxt->markerType = 4; } else if (markerstyle == 6) { // + shaped marker (with 1 pixel) + shape.resize(4); shape[0].x = -1; shape[0].y = 0; shape[1].x = 1; @@ -3490,9 +3498,10 @@ void TGWin32::UpdateMarkerStyle() shape[2].y = -1; shape[3].x = 0; shape[3].y = 1; - SetMarkerType(4, 4, shape); + ctxt->markerType = 4; } else if (markerstyle == 7) { // . shaped marker (with 9 pixel) + shape.resize(6); shape[0].x = -1; shape[0].y = 1; shape[1].x = 1; @@ -3505,12 +3514,15 @@ void TGWin32::UpdateMarkerStyle() shape[4].y = -1; shape[5].x = 1; shape[5].y = -1; - SetMarkerType(4, 6, shape); + ctxt->markerType = 4; } else if (markerstyle == 8 || markerstyle == 20) { // O shaped marker (filled) - SetMarkerType(1, im * 2, shape); + shape.resize(0); + ctxt->markerType = 0; + ctxt->markerSize = im * 2; } else if (markerstyle == 21) { // full square + shape.resize(5); shape[0].x = -im; shape[0].y = -im; shape[1].x = im; @@ -3521,9 +3533,10 @@ void TGWin32::UpdateMarkerStyle() shape[3].y = im; shape[4].x = -im; shape[4].y = -im; - SetMarkerType(3, 5, shape); + ctxt->markerType = 3; } else if (markerstyle == 22) { // full triangle up + shape.resize(4); shape[0].x = -im; shape[0].y = im; shape[1].x = im; @@ -3532,9 +3545,10 @@ void TGWin32::UpdateMarkerStyle() shape[2].y = -im; shape[3].x = -im; shape[3].y = im; - SetMarkerType(3, 4, shape); + ctxt->markerType = 3; } else if (markerstyle == 23) { // full triangle down + shape.resize(4); shape[0].x = 0; shape[0].y = im; shape[1].x = im; @@ -3543,9 +3557,10 @@ void TGWin32::UpdateMarkerStyle() shape[2].y = -im; shape[3].x = 0; shape[3].y = im; - SetMarkerType(3, 4, shape); + ctxt->markerType = 3; } else if (markerstyle == 25) { // open square + shape.resize(5); shape[0].x = -im; shape[0].y = -im; shape[1].x = im; @@ -3556,9 +3571,10 @@ void TGWin32::UpdateMarkerStyle() shape[3].y = im; shape[4].x = -im; shape[4].y = -im; - SetMarkerType(2, 5, shape); + ctxt->markerType = 2; } else if (markerstyle == 26) { // open triangle up + shape.resize(4); shape[0].x = -im; shape[0].y = im; shape[1].x = im; @@ -3567,10 +3583,11 @@ void TGWin32::UpdateMarkerStyle() shape[2].y = -im; shape[3].x = -im; shape[3].y = im; - SetMarkerType(2, 4, shape); + ctxt->markerType = 2; } else if (markerstyle == 27) { // open losange - Int_t imx = Int_t(2.66 * MarkerSizeReduced + 0.5); + Int_t imx = Int_t(2.66 * markerSizeReduced + 0.5); + shape.resize(5); shape[0].x = -imx; shape[0].y = 0; shape[1].x = 0; @@ -3581,10 +3598,11 @@ void TGWin32::UpdateMarkerStyle() shape[3].y = im; shape[4].x = -imx; shape[4].y = 0; - SetMarkerType(2, 5, shape); + ctxt->markerType = 2; } else if (markerstyle == 28) { // open cross - Int_t imx = Int_t(1.33 * MarkerSizeReduced + 0.5); + Int_t imx = Int_t(1.33 * markerSizeReduced + 0.5); + shape.resize(13); shape[0].x = -im; shape[0].y = -imx; shape[1].x = -imx; @@ -3611,13 +3629,14 @@ void TGWin32::UpdateMarkerStyle() shape[11].y = imx; shape[12].x = -im; shape[12].y = -imx; - SetMarkerType(2, 13, shape); + ctxt->markerType = 2; } else if (markerstyle == 29) { // full star pentagone - Int_t im1 = Int_t(0.66 * MarkerSizeReduced + 0.5); - Int_t im2 = Int_t(2.00 * MarkerSizeReduced + 0.5); - Int_t im3 = Int_t(2.66 * MarkerSizeReduced + 0.5); - Int_t im4 = Int_t(1.33 * MarkerSizeReduced + 0.5); + Int_t im1 = Int_t(0.66 * markerSizeReduced + 0.5); + Int_t im2 = Int_t(2.00 * markerSizeReduced + 0.5); + Int_t im3 = Int_t(2.66 * markerSizeReduced + 0.5); + Int_t im4 = Int_t(1.33 * markerSizeReduced + 0.5); + shape.resize(11); shape[0].x = -im; shape[0].y = im4; shape[1].x = -im2; @@ -3640,13 +3659,14 @@ void TGWin32::UpdateMarkerStyle() shape[9].y = im4; shape[10].x = -im; shape[10].y = im4; - SetMarkerType(3, 11, shape); + ctxt->markerType = 3; } else if (markerstyle == 30) { // open star pentagone - Int_t im1 = Int_t(0.66 * MarkerSizeReduced + 0.5); - Int_t im2 = Int_t(2.00 * MarkerSizeReduced + 0.5); - Int_t im3 = Int_t(2.66 * MarkerSizeReduced + 0.5); - Int_t im4 = Int_t(1.33 * MarkerSizeReduced + 0.5); + Int_t im1 = Int_t(0.66 * markerSizeReduced + 0.5); + Int_t im2 = Int_t(2.00 * markerSizeReduced + 0.5); + Int_t im3 = Int_t(2.66 * markerSizeReduced + 0.5); + Int_t im4 = Int_t(1.33 * markerSizeReduced + 0.5); + shape.resize(11); shape[0].x = -im; shape[0].y = im4; shape[1].x = -im2; @@ -3669,26 +3689,29 @@ void TGWin32::UpdateMarkerStyle() shape[9].y = im4; shape[10].x = -im; shape[10].y = im4; - SetMarkerType(2, 11, shape); + ctxt->markerType = 2; } else if (markerstyle == 32) { // open triangle down + shape.resize(4); shape[0].x = 0; shape[0].y = im; shape[1].x = im; shape[1].y = -im; shape[2].x = -im; shape[2].y = -im; shape[3].x = 0; shape[3].y = im; - SetMarkerType(2,4,shape); + ctxt->markerType = 2; } else if (markerstyle == 33) { // full losange - Int_t imx = Int_t(2.66*MarkerSizeReduced + 0.5); + Int_t imx = Int_t(2.66*markerSizeReduced + 0.5); + shape.resize(5); shape[0].x =-imx; shape[0].y = 0; shape[1].x = 0; shape[1].y = -im; shape[2].x = imx; shape[2].y = 0; shape[3].x = 0; shape[3].y = im; shape[4].x =-imx; shape[4].y = 0; - SetMarkerType(3,5,shape); + ctxt->markerType = 3; } else if (markerstyle == 34) { // full cross - Int_t imx = Int_t(1.33*MarkerSizeReduced + 0.5); + Int_t imx = Int_t(1.33*markerSizeReduced + 0.5); + shape.resize(13); shape[0].x = -im; shape[0].y =-imx; shape[1].x =-imx; shape[1].y =-imx; shape[2].x =-imx; shape[2].y = -im; @@ -3702,9 +3725,10 @@ void TGWin32::UpdateMarkerStyle() shape[10].x=-imx; shape[10].y= imx; shape[11].x= -im; shape[11].y= imx; shape[12].x= -im; shape[12].y=-imx; - SetMarkerType(3,13,shape); + ctxt->markerType = 3; } else if (markerstyle == 35) { // square with diagonal cross + shape.resize(8); shape[0].x = -im; shape[0].y = -im; shape[1].x = im; shape[1].y = -im; shape[2].x = im; shape[2].y = im; @@ -3713,9 +3737,10 @@ void TGWin32::UpdateMarkerStyle() shape[5].x = im; shape[5].y = im; shape[6].x = -im; shape[6].y = im; shape[7].x = im; shape[7].y = -im; - SetMarkerType(2,8,shape); + ctxt->markerType = 2; } else if (markerstyle == 36) { // diamond with cross + shape.resize(8); shape[0].x =-im; shape[0].y = 0; shape[1].x = 0; shape[1].y = -im; shape[2].x = im; shape[2].y = 0; @@ -3724,10 +3749,11 @@ void TGWin32::UpdateMarkerStyle() shape[5].x = im; shape[5].y = 0; shape[6].x = 0; shape[6].y = im; shape[7].x = 0; shape[7].y =-im; - SetMarkerType(2,8,shape); + ctxt->markerType = 2; } else if (markerstyle == 37) { // open three triangles - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(10); shape[0].x = 0; shape[0].y = 0; shape[1].x =-im2; shape[1].y = im; shape[2].x = -im; shape[2].y = 0; @@ -3738,10 +3764,11 @@ void TGWin32::UpdateMarkerStyle() shape[7].x = im; shape[7].y = 0; shape[8].x = im2; shape[8].y = im; shape[9].x = 0; shape[9].y = 0; - SetMarkerType(2,10,shape); + ctxt->markerType = 2; } else if (markerstyle == 38) { // + shaped marker with octagon - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(15); shape[0].x = -im; shape[0].y = 0; shape[1].x = -im; shape[1].y =-im2; shape[2].x =-im2; shape[2].y =-im; @@ -3757,10 +3784,11 @@ void TGWin32::UpdateMarkerStyle() shape[12].x = 0; shape[12].y = -im; shape[13].x = 0; shape[13].y = im; shape[14].x = 0; shape[14].y = 0; - SetMarkerType(2,15,shape); + ctxt->markerType = 2; } else if (markerstyle == 39) { // filled three triangles - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(9); shape[0].x = 0; shape[0].y = 0; shape[1].x =-im2; shape[1].y = im; shape[2].x = -im; shape[2].y = 0; @@ -3770,10 +3798,11 @@ void TGWin32::UpdateMarkerStyle() shape[6].x = 0; shape[6].y = 0; shape[7].x = im; shape[7].y = 0; shape[8].x = im2; shape[8].y = im; - SetMarkerType(3,9,shape); + ctxt->markerType = 3; } else if (markerstyle == 40) { // four open triangles X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(13); shape[0].x = 0; shape[0].y = 0; shape[1].x = im2; shape[1].y = im; shape[2].x = im; shape[2].y = im2; @@ -3787,10 +3816,11 @@ void TGWin32::UpdateMarkerStyle() shape[10].x = -im; shape[10].y = im2; shape[11].x = -im2; shape[11].y = im; shape[12].x = 0; shape[12].y = 0; - SetMarkerType(2,13,shape); + ctxt->markerType = 2; } else if (markerstyle == 41) { // four filled triangles X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(13); shape[0].x = 0; shape[0].y = 0; shape[1].x = im2; shape[1].y = im; shape[2].x = im; shape[2].y = im2; @@ -3804,10 +3834,11 @@ void TGWin32::UpdateMarkerStyle() shape[10].x = -im; shape[10].y = im2; shape[11].x = -im2; shape[11].y = im; shape[12].x = 0; shape[12].y = 0; - SetMarkerType(3,13,shape); + ctxt->markerType = 3; } else if (markerstyle == 42) { // open double diamonds - Int_t imx = Int_t(MarkerSizeReduced + 0.5); + Int_t imx = Int_t(markerSizeReduced + 0.5); + shape.resize(9); shape[0].x= 0; shape[0].y= im; shape[1].x= -imx; shape[1].y= imx; shape[2].x = -im; shape[2].y = 0; @@ -3817,10 +3848,11 @@ void TGWin32::UpdateMarkerStyle() shape[6].x = im; shape[6].y = 0; shape[7].x= imx; shape[7].y= imx; shape[8].x= 0; shape[8].y= im; - SetMarkerType(2,9,shape); + ctxt->markerType = 2; } else if (markerstyle == 43) { // filled double diamonds - Int_t imx = Int_t(MarkerSizeReduced + 0.5); + Int_t imx = Int_t(markerSizeReduced + 0.5); + shape.resize(9); shape[0].x = 0; shape[0].y = im; shape[1].x = -imx; shape[1].y = imx; shape[2].x = -im; shape[2].y = 0; @@ -3830,10 +3862,11 @@ void TGWin32::UpdateMarkerStyle() shape[6].x = im; shape[6].y = 0; shape[7].x = imx; shape[7].y = imx; shape[8].x = 0; shape[8].y = im; - SetMarkerType(3,9,shape); + ctxt->markerType = 3; } else if (markerstyle == 44) { // open four triangles plus - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(11); shape[0].x = 0; shape[0].y = 0; shape[1].x = im2; shape[1].y = im; shape[2].x = -im2; shape[2].y = im; @@ -3845,11 +3878,12 @@ void TGWin32::UpdateMarkerStyle() shape[8].x = -im; shape[8].y = im2; shape[9].x = -im; shape[9].y = -im2; shape[10].x = 0; shape[10].y = 0; - SetMarkerType(2,11,shape); + ctxt->markerType = 2; } else if (markerstyle == 45) { // filled four triangles plus - Int_t im0 = Int_t(0.4*MarkerSizeReduced + 0.5); - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im0 = Int_t(0.4*markerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(13); shape[0].x = im0; shape[0].y = im0; shape[1].x = im2; shape[1].y = im; shape[2].x = -im2; shape[2].y = im; @@ -3863,10 +3897,11 @@ void TGWin32::UpdateMarkerStyle() shape[10].x = im; shape[10].y = -im2; shape[11].x = im; shape[11].y = im2; shape[12].x = im0; shape[12].y = im0; - SetMarkerType(3,13,shape); + ctxt->markerType = 3; } else if (markerstyle == 46) { // open four triangles X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(13); shape[0].x = 0; shape[0].y = im2; shape[1].x = -im2; shape[1].y = im; shape[2].x = -im; shape[2].y = im2; @@ -3880,10 +3915,11 @@ void TGWin32::UpdateMarkerStyle() shape[10].x = im; shape[10].y = im2; shape[11].x = im2; shape[11].y = im; shape[12].x = 0; shape[12].y = im2; - SetMarkerType(2,13,shape); + ctxt->markerType = 2; } else if (markerstyle == 47) { // filled four triangles X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(13); shape[0].x = 0; shape[0].y = im2; shape[1].x = -im2; shape[1].y = im; shape[2].x = -im; shape[2].y = im2; @@ -3897,10 +3933,11 @@ void TGWin32::UpdateMarkerStyle() shape[10].x = im; shape[10].y = im2; shape[11].x = im2; shape[11].y = im; shape[12].x = 0; shape[12].y = im2; - SetMarkerType(3,13,shape); + ctxt->markerType = 3; } else if (markerstyle == 48) { // four filled squares X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*markerSizeReduced + 0.5); + shape.resize(17); shape[0].x = 0; shape[0].y = im2*1.005; shape[1].x = -im2; shape[1].y = im; shape[2].x = -im; shape[2].y = im2; @@ -3918,10 +3955,11 @@ void TGWin32::UpdateMarkerStyle() shape[14].x = 0; shape[14].y = -im2*0.995; shape[15].x = -im2*0.995; shape[15].y = 0; shape[16].x = 0; shape[16].y = im2*0.995; - SetMarkerType(3,16,shape); + ctxt->markerType = 3; } else if (markerstyle == 49) { // four filled squares plus - Int_t imx = Int_t(1.33*MarkerSizeReduced + 0.5); + Int_t imx = Int_t(1.33*markerSizeReduced + 0.5); + shape.resize(17); shape[0].x =-imx; shape[0].y =-imx*1.005; shape[1].x =-imx; shape[1].y = -im; shape[2].x = imx; shape[2].y = -im; @@ -3939,14 +3977,87 @@ void TGWin32::UpdateMarkerStyle() shape[14].x = imx; shape[14].y = imx; shape[15].x = imx; shape[15].y =-imx; shape[16].x =-imx; shape[16].y =-imx*1.005; - SetMarkerType(3,17,shape); + ctxt->markerType = 3; } else { // single dot - SetMarkerType(0, 0, shape); + shape.resize(0); + ctxt->markerType = 0; + ctxt->markerSize = 0; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set text attributes for speicfied window + +void TGWin32::SetAttText(WinContext_t wctxt, const TAttText &att) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; + + Int_t txalh = att.GetTextAlign() / 10; + Int_t txalv = att.GetTextAlign() % 10; + + ctxt->textAlign = kAlignNone; + + switch (txalh) { + case 0 : + case 1 : + switch (txalv) { //left + case 1 : + ctxt->textAlign = kBLeft; //bottom + break; + case 2 : + ctxt->textAlign = kMLeft; //middle + break; + case 3 : + ctxt->textAlign = kTLeft; //top + break; + } + break; + case 2 : + switch (txalv) { //center + case 1 : + ctxt->textAlign = kBCenter; //bottom + break; + case 2 : + ctxt->textAlign = kMCenter; //middle + break; + case 3 : + ctxt->textAlign = kTCenter; //top + break; + } + break; + case 3 : + switch (txalv) { //right + case 1 : + ctxt->textAlign = kBRight; //bottom + break; + case 2 : + ctxt->textAlign = kMRight; //center + break; + case 3 : + ctxt->textAlign = kTRight; //top + break; + } + break; } - fMarkerStyleModified = kFALSE; + + SetColor(ctxt, ctxt->fGClist[kGCtext], att.GetTextColor()); + + GdkGCValues values; + gdk_gc_get_values(ctxt->fGClist[kGCtext], &values); + gdk_gc_set_foreground(ctxt->fGClist[kGCinvt], &values.background); + gdk_gc_set_background(ctxt->fGClist[kGCinvt], &values.foreground); + gdk_gc_set_background(ctxt->fGClist[kGCtext], (GdkColor *) & GetColor(0).color); + + TTF::SetTextFont(att.GetTextFont()); + TTF::SetTextSize(att.GetTextSize()); + + ctxt->fAttText = att; } + //////////////////////////////////////////////////////////////////////////////// /// Set opacity of a window. This image manipulation routine works /// by adding to a percent amount of neutral to each pixels RGB. @@ -4001,7 +4112,7 @@ void TGWin32::SetOpacity(Int_t percent) } // put image back in pixmap on server - gdk_draw_image(gCws->drawing, gGCpxmp, (GdkImage *)image, + gdk_draw_image(gCws->drawing, gCws->fGClist[kGCpxmp], (GdkImage *)image, 0, 0, 0, 0, gCws->width, gCws->height); GdiFlush(); @@ -4132,59 +4243,12 @@ void TGWin32::SetRGB(int cindex, float r, float g, float b) void TGWin32::SetTextAlign(Short_t talign) { - static Short_t current = 0; - if (talign==current) return; - current = talign; + TAttText::SetTextAlign(talign); - Int_t txalh = talign / 10; - Int_t txalv = talign % 10; - fTextAlignH = txalh; - fTextAlignV = txalv; + TAttText arg(gCws->fAttText); + arg.SetTextAlign(talign); - switch (txalh) { - - case 0: - case 1: - switch (txalv) { //left - case 1: - fTextAlign = 7; //bottom - break; - case 2: - fTextAlign = 4; //center - break; - case 3: - fTextAlign = 1; //top - break; - } - break; - case 2: - switch (txalv) { //center - case 1: - fTextAlign = 8; //bottom - break; - case 2: - fTextAlign = 5; //center - break; - case 3: - fTextAlign = 2; //top - break; - } - break; - case 3: - switch (txalv) { //right - case 1: - fTextAlign = 9; //bottom - break; - case 2: - fTextAlign = 6; //center - break; - case 3: - fTextAlign = 3; //top - break; - } - break; - } - TAttText::SetTextAlign(fTextAlign); + SetAttText((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -4192,23 +4256,19 @@ void TGWin32::SetTextAlign(Short_t talign) void TGWin32::SetTextColor(Color_t cindex) { - static Int_t current = 0; - GdkGCValues values; - if ((cindex < 0) || (Int_t(cindex)==current)) return; + if (cindex < 0) return; TAttText::SetTextColor(cindex); - SetColor(gGCtext, Int_t(cindex)); - gdk_gc_get_values(gGCtext, &values); - gdk_gc_set_foreground(gGCinvt, &values.background); - gdk_gc_set_background(gGCinvt, &values.foreground); - gdk_gc_set_background(gGCtext, (GdkColor *) & GetColor(0).color); - current = Int_t(cindex); + TAttText arg(gCws->fAttText); + arg.SetTextColor(cindex); + + SetAttText((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// -void TGWin32::Sync(int mode) +void TGWin32::Sync(int /* mode */) { } @@ -4223,9 +4283,25 @@ void TGWin32::Sync(int mode) void TGWin32::UpdateWindow(int mode) { - if (gCws && gCws->double_buffer) { - gdk_window_copy_area(gCws->window, gGCpxmp, 0, 0, - gCws->drawing, 0, 0, gCws->width, gCws->height); + UpdateWindowW((WinContext_t) gCws, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Update current window +/// mode : (1) update +/// (0) sync +/// +/// Synchronise client and server once (not permanent). +/// Copy the pixmap ctxt->drawing on the window ctxt->window +/// if the double buffer is on. + +void TGWin32::UpdateWindowW(WinContext_t wctxt, Int_t mode) +{ + auto ctxt = (XWindow_t *) wctxt; + + if (ctxt && ctxt->double_buffer) { + gdk_window_copy_area(ctxt->window, ctxt->fGClist[kGCpxmp], 0, 0, + ctxt->drawing, 0, 0, ctxt->width, ctxt->height); } Update(mode); } @@ -4268,8 +4344,8 @@ void TGWin32::WritePixmap(int wid, unsigned int w, unsigned int h, wval = w; hval = h; - if (!fWindows) return; - gTws = &fWindows[wid]; + if (fWindows.count(wid) == 0) return; + gTws = fWindows[wid].get(); // XWriteBitmapFile(fDisplay,pxname,(Pixmap)gTws->drawing,wval,hval,-1,-1); } @@ -4457,11 +4533,14 @@ void TGWin32::PutImage(Int_t offset, Int_t itran, Int_t x0, Int_t y0, Int_t nx, int nlines[256]; GdkSegment lines[256][MAX_SEGMENT]; GdkDrawable *id; + GdkGC *lineGC; if (wid) { - id = (GdkDrawable*)wid; + id = (GdkDrawable*) wid; + lineGC = gdk_gc_new(GDK_ROOT_PARENT()); } else { id = gCws->drawing; + lineGC = gCws->fGClist[kGCline]; } for (i = 0; i < 256; i++) nlines[i] = 0; @@ -4484,8 +4563,8 @@ void TGWin32::PutImage(Int_t offset, Int_t itran, Int_t x0, Int_t y0, Int_t nx, lines[icol][n].x2 = x - 1; lines[icol][n].y2 = y; if (nlines[icol] == MAX_SEGMENT) { - SetColor(gGCline, (int) icol + offset); - gdk_win32_draw_segments(id, (GdkGC *) gGCline, + SetColor(wid ? nullptr : gCws, lineGC, (int) icol + offset); + gdk_win32_draw_segments(id, lineGC, (GdkSegment *) &lines[icol][0], MAX_SEGMENT); nlines[icol] = 0; } @@ -4501,8 +4580,8 @@ void TGWin32::PutImage(Int_t offset, Int_t itran, Int_t x0, Int_t y0, Int_t nx, lines[icol][n].x2 = x - 1; lines[icol][n].y2 = y; if (nlines[icol] == MAX_SEGMENT) { - SetColor(gGCline, (int) icol + offset); - gdk_win32_draw_segments(id, (GdkGC *) gGCline, + SetColor(wid ? nullptr : gCws, lineGC, (int) icol + offset); + gdk_win32_draw_segments(id, lineGC, (GdkSegment *)&lines[icol][0], MAX_SEGMENT); nlines[icol] = 0; } @@ -4511,11 +4590,15 @@ void TGWin32::PutImage(Int_t offset, Int_t itran, Int_t x0, Int_t y0, Int_t nx, for (i = 0; i < 256; i++) { if (nlines[i] != 0) { - SetColor(gGCline, i + offset); - gdk_win32_draw_segments(id, (GdkGC *) gGCline, - (GdkSegment *)&lines[icol][0], nlines[i]); + SetColor(wid ? nullptr : gCws, lineGC, i + offset); + gdk_win32_draw_segments(id, lineGC, + (GdkSegment *)&lines[icol][0], nlines[i]); } } + + if (wid) + gdk_gc_unref(lineGC); + } //////////////////////////////////////////////////////////////////////////////// @@ -7440,34 +7523,16 @@ Int_t TGWin32::AddPixmap(Window_t pix, UInt_t w, UInt_t h) SetBitmapDimensionEx(hBmp, w, h, &sz); GdkPixmap *newPix = gdk_pixmap_foreign_new(reinterpret_cast(hBmp)); - Int_t wid = 0; - for(; wid < fMaxNumberOfWindows; ++wid) - if (!fWindows[wid].open) - break; - - if (wid == fMaxNumberOfWindows) { - Int_t newSize = fMaxNumberOfWindows + 10; - - fWindows = (XWindow_t *)TStorage::ReAlloc(fWindows, newSize * sizeof(XWindow_t), - fMaxNumberOfWindows * sizeof(XWindow_t)); + Int_t wid = AddWindowHandle(); - for (Int_t i = fMaxNumberOfWindows; i < newSize; ++i) - fWindows[i].open = 0; - - fMaxNumberOfWindows = newSize; - } - - fWindows[wid].open = 1; - gCws = fWindows + wid; + gCws = fWindows[wid].get(); + gCws->ispixmap = 1; gCws->window = newPix; gCws->drawing = gCws->window; - gCws->buffer = 0; gCws->double_buffer = 0; - gCws->ispixmap = 1; gCws->clip = 0; gCws->width = w; gCws->height = h; - gCws->new_colors = 0; return wid; } @@ -7477,82 +7542,41 @@ Int_t TGWin32::AddPixmap(Window_t pix, UInt_t w, UInt_t h) Int_t TGWin32::AddWindow(ULongptr_t qwid, UInt_t w, UInt_t h) { - Int_t wid; - // Select next free window number + Int_t wid = AddWindowHandle(); - again: - for (wid = 0; wid < fMaxNumberOfWindows; wid++) { - if (!fWindows[wid].open) { - fWindows[wid].open = 1; - fWindows[wid].double_buffer = 0; - gCws = &fWindows[wid]; - break; - } - } - - if (wid == fMaxNumberOfWindows) { - int newsize = fMaxNumberOfWindows + 10; - fWindows = - (XWindow_t *) TStorage::ReAlloc(fWindows, - newsize * sizeof(XWindow_t), - fMaxNumberOfWindows * - sizeof(XWindow_t)); - - for (int i = fMaxNumberOfWindows; i < newsize; i++) { - fWindows[i].open = 0; - } + gCws = fWindows[wid].get(); - fMaxNumberOfWindows = newsize; - goto again; - } + gCws->shared = true; + gCws->ispixmap = 0; gCws->window = gdk_window_foreign_new(qwid); gCws->drawing = gCws->window; - gCws->buffer = 0; gCws->double_buffer = 0; - gCws->ispixmap = 0; gCws->clip = 0; gCws->width = w; gCws->height = h; - gCws->new_colors = 0; return wid; } //////////////////////////////////////////////////////////////////////////////// -/// Remove a window created by Qt (like CloseWindow1()). +/// Remove a window created by Qt. void TGWin32::RemoveWindow(ULongptr_t qwid) { - int wid; - SelectWindow((int)qwid); + CloseWindow(); +} - if (gCws->buffer) { - gdk_pixmap_unref(gCws->buffer); - } - if (gCws->new_colors) { - gdk_colormap_free_colors((GdkColormap *) fColormap, - (GdkColor *)gCws->new_colors, gCws->ncolors); - - delete [] gCws->new_colors; - gCws->new_colors = 0; - } - - GdiFlush(); - gCws->open = 0; - - if (!fWindows) return; +//////////////////////////////////////////////////////////////////////////////// +/// Returns window context for specified win id - // make first window in list the current window - for (wid = 0; wid < fMaxNumberOfWindows; wid++) { - if (fWindows[wid].open) { - gCws = &fWindows[wid]; - return; - } - } - gCws = 0; +WinContext_t TGWin32::GetWindowContext(Int_t wid) +{ + if (fWindows.count(wid) == 0) + return (WinContext_t) nullptr; + return (WinContext_t) fWindows[wid].get(); } //////////////////////////////////////////////////////////////////////////////// @@ -7845,3 +7869,37 @@ void TGWin32::SetUserThreadId(ULong_t id) } } +//////////////////////////////////////////////////////////////////////////////// +/// Set window draw mode + +void TGWin32::SetDrawModeW(WinContext_t wctxt, EDrawMode mode) +{ + // set TVirtualX member to support old interface + fDrawMode = mode; + + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; + + GdkFunction func = GDK_COPY; + if (mode == kXor) + func = GDK_XOR; + else if (mode == kInvert) + func = GDK_INVERT; + + for (int i = 0; i < kMAXGC; i++) { + if (ctxt->fGClist[i]) + gdk_gc_set_function(ctxt->fGClist[i], func); + } + + ctxt->drawMode = mode; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns window draw mode + +TVirtualX::EDrawMode TGWin32::GetDrawModeW(WinContext_t wctxt) +{ + auto ctxt = (XWindow_t *) wctxt; + return ctxt ? ctxt->drawMode : kCopy; +} diff --git a/graf2d/x11/inc/TGX11.h b/graf2d/x11/inc/TGX11.h index 17c524175761d..6d016d8e3cdf6 100644 --- a/graf2d/x11/inc/TGX11.h +++ b/graf2d/x11/inc/TGX11.h @@ -13,6 +13,8 @@ #define ROOT_TGX11 #include "TVirtualX.h" +#include +#include #ifdef Status // Convert Status from a CPP macro to a typedef: @@ -44,26 +46,6 @@ struct RXSetWindowAttributes; struct RXVisualInfo; struct RVisual; -/// Description of a X11 window. -struct XWindow_t { - Int_t fOpen; ///< 1 if the window is open, 0 if not - Int_t fDoubleBuffer; ///< 1 if the double buffer is on, 0 if not - Int_t fIsPixmap; ///< 1 if pixmap, 0 if not - Drawable fDrawing; ///< drawing area, equal to window or buffer - Drawable fWindow; ///< X11 window - Drawable fBuffer; ///< pixmap used for double buffer - UInt_t fWidth; ///< width of the window - UInt_t fHeight; ///< height of the window - Int_t fClip; ///< 1 if the clipping is on - Int_t fXclip; ///< x coordinate of the clipping rectangle - Int_t fYclip; ///< y coordinate of the clipping rectangle - UInt_t fWclip; ///< width of the clipping rectangle - UInt_t fHclip; ///< height of the clipping rectangle - ULong_t *fNewColors; ///< new image colors (after processing) - Int_t fNcolors; ///< number of different colors - Bool_t fShared; ///< notify when window is shared -}; - /// Description of a X11 color. struct XColor_t { ULong_t fPixel; ///< color pixel value @@ -76,29 +58,27 @@ struct XColor_t { class TExMap; +struct XWindow_t; + class TGX11 : public TVirtualX { +friend struct XWindow_t; + private: - Int_t fMaxNumberOfWindows; ///< Maximum number of windows - XWindow_t *fWindows; ///< List of windows + std::unordered_map> fWindows; // map of windows TExMap *fColors; ///< Hash list of colors Cursor fCursors[kNumCursors]; ///< List of cursors void *fXEvent; ///< Current native (X11) event - void CloseWindow1(); - void ClearPixmap(Drawable *pix); - void CopyWindowtoPixmap(Drawable *pix, Int_t xpos, Int_t ypos); + Int_t AddWindowHandle(); void FindBestVisual(); void FindUsableVisual(RXVisualInfo *vlist, Int_t nitems); void PutImage(Int_t offset, Int_t itran, Int_t x0, Int_t y0, Int_t nx, Int_t ny, Int_t xmin, Int_t ymin, Int_t xmax, Int_t ymax, UChar_t *image, Drawable_t id); - void RemovePixmap(Drawable *pix); - void SetColor(void *gc, Int_t ci); - void SetFillStyleIndex(Int_t style, Int_t fasi); + void SetColor(XWindow_t *ctxt, void *gc, Int_t ci); void SetInput(Int_t inp); - void SetMarkerType(Int_t type, Int_t n, RXPoint *xy); void CollectImageColors(ULong_t pixel, ULong_t *&orgcolors, Int_t &ncolors, Int_t &maxcolors); void MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors); @@ -130,9 +110,6 @@ class TGX11 : public TVirtualX { ULong_t fBlackPixel; ///< Value of black pixel in colormap ULong_t fWhitePixel; ///< Value of white pixel in colormap Int_t fScreenNumber; ///< Screen number - Int_t fTextAlignH; ///< Text Alignment Horizontal - Int_t fTextAlignV; ///< Text Alignment Vertical - Int_t fTextAlign; ///< Text alignment (set in SetTextAlign) Float_t fCharacterUpX; ///< Character Up vector along X Float_t fCharacterUpY; ///< Character Up vector along Y Float_t fTextMagnitude; ///< Text Magnitude @@ -147,14 +124,25 @@ class TGX11 : public TVirtualX { Bool_t fHasXft; ///< True when XftFonts are used // needed by TGX11TTF + enum EAlign { + kAlignNone, + kTLeft, kTCenter, kTRight, kMLeft, kMCenter, kMRight, + kBLeft, kBCenter, kBRight }; + Bool_t AllocColor(Colormap cmap, RXColor *color); void QueryColors(Colormap cmap, RXColor *colors, Int_t ncolors); void *GetGC(Int_t which) const; + Window_t GetWindow(WinContext_t wctxt) const; + void *GetGCW(WinContext_t wctxt, Int_t which) const; + EAlign GetTextAlignW(WinContext_t wctxt) const; + XColor_t &GetColor(Int_t cid); + TGX11(TGX11 &&org); + TGX11(const TGX11 &org) = delete; + public: TGX11(); - TGX11(const TGX11 &org); TGX11(const char *name, const char *title); ~TGX11() override; @@ -163,15 +151,6 @@ class TGX11 : public TVirtualX { void ClosePixmap() override; void CloseWindow() override; void CopyPixmap(Int_t wid, Int_t xpos, Int_t ypos) override; - void DrawBox(Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) override; - void DrawCellArray(Int_t x1, Int_t y1, Int_t x2, Int_t y2, Int_t nx, Int_t ny, Int_t *ic) override; - void DrawFillArea(Int_t n, TPoint *xy) override; - void DrawLine(Int_t x1, Int_t y1, Int_t x2, Int_t y2) override; - void DrawPolyLine(Int_t n, TPoint *xy) override; - void DrawLinesSegments(Int_t n, TPoint *xy) override; - void DrawPolyMarker(Int_t n, TPoint *xy) override; - void DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode) override; - void DrawText(Int_t, Int_t, Float_t, Float_t, const wchar_t *, ETextMode) override {} void GetCharacterUp(Float_t &chupx, Float_t &chupy) override; Int_t GetDoubleBuffer(Int_t wid) override; void GetGeometry(Int_t wid, Int_t &x, Int_t &y, UInt_t &w, UInt_t &h) override; @@ -208,12 +187,24 @@ class TGX11 : public TVirtualX { void SetDoubleBufferOFF() override; void SetDoubleBufferON() override; void SetDrawMode(EDrawMode mode) override; + void Sync(Int_t mode) override; + void UpdateWindow(Int_t mode) override; + void Warp(Int_t ix, Int_t iy, Window_t id = 0) override; + Int_t WriteGIF(char *name) override; + void WritePixmap(Int_t wid, UInt_t w, UInt_t h, char *pxname) override; + Window_t GetCurrentWindow() const override; + Int_t SupportsExtension(const char *ext) const override; + + //---- Methods used for old graphics ----- void SetFillColor(Color_t cindex) override; void SetFillStyle(Style_t style) override; + Style_t GetFillStyle() const override; void SetLineColor(Color_t cindex) override; void SetLineType(Int_t n, Int_t *dash) override; void SetLineStyle(Style_t linestyle) override; + Style_t GetLineStyle() const override; void SetLineWidth(Width_t width) override; + Width_t GetLineWidth() const override; void SetMarkerColor(Color_t cindex) override; void SetMarkerSize(Float_t markersize) override; void SetMarkerStyle(Style_t markerstyle) override; @@ -225,13 +216,35 @@ class TGX11 : public TVirtualX { void SetTextFont(Font_t fontnumber) override; void SetTextMagnitude(Float_t mgn=1) override { fTextMagnitude = mgn;} void SetTextSize(Float_t textsize) override; - void Sync(Int_t mode) override; - void UpdateWindow(Int_t mode) override; - void Warp(Int_t ix, Int_t iy, Window_t id = 0) override; - Int_t WriteGIF(char *name) override; - void WritePixmap(Int_t wid, UInt_t w, UInt_t h, char *pxname) override; - Window_t GetCurrentWindow() const override; - Int_t SupportsExtension(const char *ext) const override; + void DrawBox(Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) override; + void DrawCellArray(Int_t x1, Int_t y1, Int_t x2, Int_t y2, Int_t nx, Int_t ny, Int_t *ic) override; + void DrawFillArea(Int_t n, TPoint *xy) override; + void DrawLine(Int_t x1, Int_t y1, Int_t x2, Int_t y2) override; + void DrawPolyLine(Int_t n, TPoint *xy) override; + void DrawLinesSegments(Int_t n, TPoint *xy) override; + void DrawPolyMarker(Int_t n, TPoint *xy) override; + void DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode) override; + void DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, const wchar_t *text, ETextMode mode) override; + + //---- Methods used for new graphics ----- + WinContext_t GetWindowContext(Int_t wid) override; + void SetAttFill(WinContext_t wctxt, const TAttFill &att) override; + void SetAttLine(WinContext_t wctxt, const TAttLine &att) override; + void SetAttMarker(WinContext_t wctxt, const TAttMarker &att) override; + void SetAttText(WinContext_t wctxt, const TAttText &att) override; + void SetDrawModeW(WinContext_t wctxt, EDrawMode mode) override; + EDrawMode GetDrawModeW(WinContext_t wctxt) override; + void ClearWindowW(WinContext_t wctxt) override; + void UpdateWindowW(WinContext_t wctxt, Int_t mode) override; + + void DrawBoxW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) override; + void DrawFillAreaW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawLineW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2) override; + void DrawPolyLineW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawLinesSegmentsW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawPolyMarkerW(WinContext_t wctxt, Int_t n, TPoint *xy) override; + void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode) override; + void DrawTextW(WinContext_t, Int_t, Int_t, Float_t, Float_t, const wchar_t *, ETextMode) override {} //---- Methods used for GUI ----- void GetWindowAttributes(Window_t id, WindowAttributes_t &attr) override; diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index 9ba0fc6849e8f..dac0cdc595ce9 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -76,30 +76,66 @@ extern int XRotDrawAlignedImageString(Display*, XFontStruct*, float, extern XPoint *XRotTextExtents(Display*, XFontStruct*, float, int, int, char*, int); -//---- globals - -static XWindow_t *gCws; // gCws: pointer to the current window -static XWindow_t *gTws; // gTws: temporary pointer -const Int_t kBIGGEST_RGB_VALUE = 65535; // // Primitives Graphic Contexts global for all windows // -const int kMAXGC = 7; -static GC gGClist[kMAXGC]; -static GC *gGCline = &gGClist[0]; // PolyLines -static GC *gGCmark = &gGClist[1]; // PolyMarker -static GC *gGCfill = &gGClist[2]; // Fill areas -static GC *gGCtext = &gGClist[3]; // Text -static GC *gGCinvt = &gGClist[4]; // Inverse text -static GC *gGCdash = &gGClist[5]; // Dashed lines -static GC *gGCpxmp = &gGClist[6]; // Pixmap management +const int kMAXGC = 7, + kGCline = 0, kGCmark = 1, kGCfill = 2, + kGCtext = 3, kGCinvt = 4, kGCdash = 5, kGCpxmp = 6; static GC gGCecho; // Input echo -static Int_t gFillHollow; // Flag if fill style is hollow -static Pixmap gFillPattern = 0; // Fill pattern + +/// Description of a X11 window. +struct XWindow_t { + Int_t fOpen = 0; ///< 1 if the window is open, 0 if not + Int_t fDoubleBuffer = 0; ///< 1 if the double buffer is on, 0 if not + Int_t fIsPixmap = 0; ///< 1 if pixmap, 0 if not + Drawable fDrawing = 0; ///< drawing area, equal to window or buffer + Drawable fWindow = 0; ///< X11 window + Drawable fBuffer = 0; ///< pixmap used for double buffer + UInt_t fWidth = 0; ///< width of the window + UInt_t fHeight = 0; ///< height of the window + Int_t fClip = 0; ///< 1 if the clipping is on + Int_t fXclip = 0; ///< x coordinate of the clipping rectangle + Int_t fYclip = 0; ///< y coordinate of the clipping rectangle + UInt_t fWclip = 0; ///< width of the clipping rectangle + UInt_t fHclip = 0; ///< height of the clipping rectangle + ULong_t *fNewColors = 0; ///< new image colors (after processing) + Int_t fNcolors = 0; ///< number of different colors + Bool_t fShared = 0; ///< notify when window is shared + GC fGClist[kMAXGC]; ///< list of GC object, individual for each window + TVirtualX::EDrawMode drawMode = TVirtualX::kCopy; ///< current draw mode + TAttLine fAttLine = {-1, -1, -1}; ///< current line attributes + Int_t lineWidth = 0; ///< X11 line width + Int_t lineStyle = LineSolid; ///< X11 line style + std::vector dashList; ///< X11 array for dashes + Int_t dashLength = 0; ///< total length of dashes + Int_t dashOffset = 0; ///< current dash offset + TAttFill fAttFill = {-1, -1}; ///< current fill attributes + Int_t fillHollow = 0; ///< X11 fill method + Int_t fillFasi = 0; ///< selected fasi pattern + Pixmap fillPattern = 0; ///< current initialized fill pattern + TAttMarker fAttMarker = { -1, -1, -1 }; ///< current marker attribute + Int_t markerType = 0; ///< 4 differen kinds of marker + Int_t markerSize = 0; ///< size of simple markers + std::vector markerShape; ///< marker shape points + Int_t markerLineWidth = 0; ///< line width used for marker + TAttText fAttText; ///< current text attribute + TGX11::EAlign textAlign = TGX11::kAlignNone; ///< selected text align + XFontStruct *textFont = nullptr; ///< selected text font +}; + + +//---- globals + +static XWindow_t *gCws; // gCws: pointer to the current window +static XWindow_t *gTws; // gTws: temporary pointer + +const Int_t kBIGGEST_RGB_VALUE = 65535; + // // Text management @@ -113,16 +149,6 @@ static struct { static XFontStruct *gTextFont; // Current font static Int_t gCurrentFontNumber = 0; // Current font number in gFont[] -// -// Markers -// -const Int_t kMAXMK = 100; -static struct { - int type; - int n; - XPoint xy[kMAXMK]; -} gMarker; // Point list to draw marker -static int gMarkerLineWidth = 0; static int gMarkerLineStyle = LineSolid; static int gMarkerCapStyle = CapRound; static int gMarkerJoinStyle = JoinRound; @@ -130,14 +156,8 @@ static int gMarkerJoinStyle = JoinRound; // // Keep style values for line GC // -static int gLineWidth = 0; -static int gLineStyle = LineSolid; static int gCapStyle = CapButt; static int gJoinStyle = JoinMiter; -static char gDashList[10]; -static int gDashLength = 0; -static int gDashOffset = 0; -static int gDashSize = 0; // // Event masks @@ -180,7 +200,6 @@ TGX11::TGX11() fColormap = 0; fBlackPixel = 0; fWhitePixel = 0; - fWindows = nullptr; fColors = nullptr; fXEvent = new XEvent; fRedDiv = -1; @@ -194,10 +213,6 @@ TGX11::TGX11() fDepth = 0; fHasTTFonts = kFALSE; fHasXft = kFALSE; - fMaxNumberOfWindows = 10; - fTextAlignH = 1; - fTextAlignV = 1; - fTextAlign = 7; fTextMagnitude = 1; for (i = 0; i < kNumCursors; i++) fCursors[i] = 0; } @@ -229,17 +244,9 @@ TGX11::TGX11(const char *name, const char *title) : TVirtualX(name, title) fDepth = 0; fHasTTFonts = kFALSE; fHasXft = kFALSE; - fMaxNumberOfWindows = 10; - fTextAlignH = 1; - fTextAlignV = 1; - fTextAlign = 7; fTextMagnitude = 1; - for (i = 0; i < kNumCursors; i++) fCursors[i] = 0; - - //fWindows = new XWindow_t[fMaxNumberOfWindows]; - fWindows = (XWindow_t*) TStorage::Alloc(fMaxNumberOfWindows*sizeof(XWindow_t)); - for (i = 0; i < fMaxNumberOfWindows; i++) - fWindows[i].fOpen = 0; + for (i = 0; i < kNumCursors; i++) + fCursors[i] = 0; fColors = new TExMap; } @@ -247,10 +254,8 @@ TGX11::TGX11(const char *name, const char *title) : TVirtualX(name, title) //////////////////////////////////////////////////////////////////////////////// /// Copy constructor. Currently only used by TGX11TTF. -TGX11::TGX11(const TGX11 &org) : TVirtualX(org) +TGX11::TGX11(TGX11 &&org) : TVirtualX(org) { - int i; - fDisplay = org.fDisplay; fScreenNumber = org.fScreenNumber; fVisual = org.fVisual; @@ -261,9 +266,6 @@ TGX11::TGX11(const TGX11 &org) : TVirtualX(org) fWhitePixel = org.fWhitePixel; fHasTTFonts = org.fHasTTFonts; fHasXft = org.fHasXft; - fTextAlignH = org.fTextAlignH; - fTextAlignV = org.fTextAlignV; - fTextAlign = org.fTextAlign; fTextMagnitude = org.fTextMagnitude; fCharacterUpX = org.fCharacterUpX; fCharacterUpY = org.fCharacterUpY; @@ -275,45 +277,14 @@ TGX11::TGX11(const TGX11 &org) : TVirtualX(org) fGreenShift = org.fGreenShift; fBlueShift = org.fBlueShift; fDrawMode = org.fDrawMode; - fXEvent = new XEvent; - - fMaxNumberOfWindows = org.fMaxNumberOfWindows; - //fWindows = new XWindow_t[fMaxNumberOfWindows]; - fWindows = (XWindow_t*) TStorage::Alloc(fMaxNumberOfWindows*sizeof(XWindow_t)); - for (i = 0; i < fMaxNumberOfWindows; i++) { - fWindows[i].fOpen = org.fWindows[i].fOpen; - fWindows[i].fDoubleBuffer = org.fWindows[i].fDoubleBuffer; - fWindows[i].fIsPixmap = org.fWindows[i].fIsPixmap; - fWindows[i].fDrawing = org.fWindows[i].fDrawing; - fWindows[i].fWindow = org.fWindows[i].fWindow; - fWindows[i].fBuffer = org.fWindows[i].fBuffer; - fWindows[i].fWidth = org.fWindows[i].fWidth; - fWindows[i].fHeight = org.fWindows[i].fHeight; - fWindows[i].fClip = org.fWindows[i].fClip; - fWindows[i].fXclip = org.fWindows[i].fXclip; - fWindows[i].fYclip = org.fWindows[i].fYclip; - fWindows[i].fWclip = org.fWindows[i].fWclip; - fWindows[i].fHclip = org.fWindows[i].fHclip; - fWindows[i].fNewColors = org.fWindows[i].fNewColors; - fWindows[i].fNcolors = org.fWindows[i].fNcolors; - fWindows[i].fShared = org.fWindows[i].fShared; - } + fXEvent = org.fXEvent; org.fXEvent = nullptr; + fColors = org.fColors; org.fColors = nullptr; - for (i = 0; i < kNumCursors; i++) - fCursors[i] = org.fCursors[i]; + fWindows = std::move(org.fWindows); - fColors = new TExMap; - Long64_t key, value; - TExMapIter it(org.fColors); - while (it.Next(key, value)) { - XColor_t *colo = (XColor_t *) (Long_t)value; - XColor_t *col = new XColor_t; - col->fPixel = colo->fPixel; - col->fRed = colo->fRed; - col->fGreen = colo->fGreen; - col->fBlue = colo->fBlue; - col->fDefined = colo->fDefined; - fColors->Add(key, (Long_t) col); + for (int i = 0; i < kNumCursors; i++) { + fCursors[i] = org.fCursors[i]; + org.fCursors[i] = 0; } } @@ -322,17 +293,22 @@ TGX11::TGX11(const TGX11 &org) : TVirtualX(org) TGX11::~TGX11() { - delete (XEvent*)fXEvent; - if (fWindows) TStorage::Dealloc(fWindows); + if (fXEvent) + delete (XEvent*)fXEvent; - if (!fColors) return; - Long64_t key, value; - TExMapIter it(fColors); - while (it.Next(key, value)) { - XColor_t *col = (XColor_t *) (Long_t)value; - delete col; + if (fColors) { + Long64_t key, value; + TExMapIter it(fColors); + while (it.Next(key, value)) { + XColor_t *col = (XColor_t *) (Long_t)value; + delete col; + } + delete fColors; } - delete fColors; + + for (int i = 0; i < kNumCursors; i++) + if (fCursors[i]) + XFreeCursor((Display*)fDisplay, fCursors[i]); } //////////////////////////////////////////////////////////////////////////////// @@ -340,7 +316,8 @@ TGX11::~TGX11() Bool_t TGX11::Init(void *display) { - if (OpenDisplay(display) == -1) return kFALSE; + if (OpenDisplay(display) == -1) + return kFALSE; return kTRUE; } @@ -400,37 +377,33 @@ void TGX11::QueryColors(Colormap cmap, RXColor *color, Int_t ncolors) } } + //////////////////////////////////////////////////////////////////////////////// -/// Clear the pixmap pix. +/// Clear current window. -void TGX11::ClearPixmap(Drawable *pix) +void TGX11::ClearWindow() { - Window root; - int xx, yy; - unsigned int ww, hh, border, depth; - XGetGeometry((Display*)fDisplay, *pix, &root, &xx, &yy, &ww, &hh, &border, &depth); - SetColor(gGCpxmp, 0); - XFillRectangle((Display*)fDisplay, *pix, *gGCpxmp, 0 ,0 ,ww ,hh); - SetColor(gGCpxmp, 1); - XFlush((Display*)fDisplay); + ClearWindowW((WinContext_t) gCws); } //////////////////////////////////////////////////////////////////////////////// -/// Clear current window. +/// Clear specified window. -void TGX11::ClearWindow() +void TGX11::ClearWindowW(WinContext_t wctxt) { - if (!gCws) return; + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; - if (!gCws->fIsPixmap && !gCws->fDoubleBuffer) { - XSetWindowBackground((Display*)fDisplay, gCws->fDrawing, GetColor(0).fPixel); - XClearWindow((Display*)fDisplay, gCws->fDrawing); + if (!ctxt->fIsPixmap && !ctxt->fDoubleBuffer) { + XSetWindowBackground((Display*)fDisplay, ctxt->fDrawing, GetColor(0).fPixel); + XClearWindow((Display*)fDisplay, ctxt->fDrawing); XFlush((Display*)fDisplay); } else { - SetColor(gGCpxmp, 0); - XFillRectangle((Display*)fDisplay, gCws->fDrawing, *gGCpxmp, - 0, 0, gCws->fWidth, gCws->fHeight); - SetColor(gGCpxmp, 1); + SetColor(ctxt, &ctxt->fGClist[kGCpxmp], 0); + XFillRectangle((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCpxmp], + 0, 0, ctxt->fWidth, ctxt->fHeight); + SetColor(ctxt, &ctxt->fGClist[kGCpxmp], 1); } } @@ -439,7 +412,7 @@ void TGX11::ClearWindow() void TGX11::ClosePixmap() { - CloseWindow1(); + CloseWindow(); } //////////////////////////////////////////////////////////////////////////////// @@ -447,28 +420,8 @@ void TGX11::ClosePixmap() void TGX11::CloseWindow() { - if (gCws->fShared) - gCws->fOpen = 0; - else - CloseWindow1(); - - // Never close connection. TApplication takes care of that - // if (!gCws) Close(); // close X when no open window left -} - -//////////////////////////////////////////////////////////////////////////////// -/// Delete current window. - -void TGX11::CloseWindow1() -{ - int wid; - - if (gCws->fIsPixmap) - XFreePixmap((Display*)fDisplay, gCws->fWindow); - else - XDestroyWindow((Display*)fDisplay, gCws->fWindow); - - if (gCws->fBuffer) XFreePixmap((Display*)fDisplay, gCws->fBuffer); + if (gCws->fBuffer) + XFreePixmap((Display*)fDisplay, gCws->fBuffer); if (gCws->fNewColors) { if (fRedDiv == -1) @@ -477,18 +430,41 @@ void TGX11::CloseWindow1() gCws->fNewColors = nullptr; } - XFlush((Display*)fDisplay); + if (!gCws->fShared) { // if not QT window + if (gCws->fIsPixmap) + XFreePixmap((Display*)fDisplay, gCws->fWindow); + else + XDestroyWindow((Display*)fDisplay, gCws->fWindow); + + XFlush((Display*)fDisplay); + } + + for (int i = 0; i < kMAXGC; ++i) + XFreeGC((Display*)fDisplay, gCws->fGClist[i]); + + if (gCws->fillPattern != 0) { + XFreePixmap((Display*)fDisplay, gCws->fillPattern); + gCws->fillPattern = 0; + } gCws->fOpen = 0; - // make first window in list the current window - for (wid = 0; wid < fMaxNumberOfWindows; wid++) - if (fWindows[wid].fOpen) { - gCws = &fWindows[wid]; - return; + for (auto iter = fWindows.begin(); iter != fWindows.end(); ++iter) + if (iter->second.get() == gCws) { + fWindows.erase(iter); + gCws = nullptr; + break; } - gCws = nullptr; + if (gCws) + Fatal("CloseWindow", "Not found gCws in list of windows"); + + // select first from active windows + for (auto iter = fWindows.begin(); iter != fWindows.end(); ++iter) + if (iter->second && iter->second->fOpen) { + gCws = iter->second.get(); + return; + } } //////////////////////////////////////////////////////////////////////////////// @@ -496,35 +472,34 @@ void TGX11::CloseWindow1() void TGX11::CopyPixmap(int wid, int xpos, int ypos) { - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); - XCopyArea((Display*)fDisplay, gTws->fDrawing, gCws->fDrawing, *gGCpxmp, 0, 0, gTws->fWidth, + XCopyArea((Display*)fDisplay, gTws->fDrawing, gCws->fDrawing, gTws->fGClist[kGCpxmp], 0, 0, gTws->fWidth, gTws->fHeight, xpos, ypos); XFlush((Display*)fDisplay); } //////////////////////////////////////////////////////////////////////////////// -/// Copy area of current window in the pixmap pix. +/// Draw a box. +/// +/// - mode=0 hollow (kHollow) +/// - mode=1 solid (kSolid) -void TGX11::CopyWindowtoPixmap(Drawable *pix, int xpos, int ypos ) +void TGX11::DrawBox(int x1, int y1, int x2, int y2, EBoxMode mode) { - Window root; - int xx, yy; - unsigned int ww, hh, border, depth; - - XGetGeometry((Display*)fDisplay, *pix, &root, &xx, &yy, &ww, &hh, &border, &depth); - XCopyArea((Display*)fDisplay, gCws->fDrawing, *pix, *gGCpxmp, xpos, ypos, ww, hh, 0, 0); - XFlush((Display*)fDisplay); + DrawBoxW((WinContext_t) gCws, x1, y1, x2, y2, mode); } //////////////////////////////////////////////////////////////////////////////// -/// Draw a box. +/// Draw a box on specified window /// /// - mode=0 hollow (kHollow) /// - mode=1 solid (kSolid) -void TGX11::DrawBox(int x1, int y1, int x2, int y2, EBoxMode mode) +void TGX11::DrawBoxW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) { + auto ctxt = (XWindow_t *) wctxt; + Int_t x = TMath::Min(x1, x2); Int_t y = TMath::Min(y1, y2); Int_t w = TMath::Abs(x2 - x1); @@ -533,11 +508,11 @@ void TGX11::DrawBox(int x1, int y1, int x2, int y2, EBoxMode mode) switch (mode) { case kHollow: - XDrawRectangle((Display*)fDisplay, gCws->fDrawing, *gGCline, x, y, w, h); + XDrawRectangle((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCline], x, y, w, h); break; case kFilled: - XFillRectangle((Display*)fDisplay, gCws->fDrawing, *gGCfill, x, y, w, h); + XFillRectangle((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCfill], x, y, w, h); break; default: @@ -571,10 +546,10 @@ void TGX11::DrawCellArray(int x1, int y1, int x2, int y2, int nx, int ny, int *i for (j = 0; j < ny; j++) { icol = ic[i+(nx*j)]; if (icol != current_icol) { - XSetForeground((Display*)fDisplay, *gGCfill, GetColor(icol).fPixel); + XSetForeground((Display*)fDisplay, gCws->fGClist[kGCfill], GetColor(icol).fPixel); current_icol = icol; } - XFillRectangle((Display*)fDisplay, gCws->fDrawing, *gGCfill, ix, iy, w, h); + XFillRectangle((Display*)fDisplay, gCws->fDrawing, gCws->fGClist[kGCfill], ix, iy, w, h); iy = iy-h; } ix = ix+w; @@ -589,13 +564,25 @@ void TGX11::DrawCellArray(int x1, int y1, int x2, int y2, int nx, int ny, int *i void TGX11::DrawFillArea(int n, TPoint *xy) { - XPoint *xyp = (XPoint*)xy; + DrawFillAreaW((WinContext_t) gCws, n, xy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill area described by polygon on specified window +/// +/// \param [in] n number of points +/// \param [in] xy list of points + +void TGX11::DrawFillAreaW(WinContext_t wctxt, Int_t n, TPoint *xy) +{ + auto ctxt = (XWindow_t *) wctxt; + XPoint *xyp = (XPoint *)xy; - if (gFillHollow) - XDrawLines((Display*)fDisplay, gCws->fDrawing, *gGCfill, xyp, n, CoordModeOrigin); + if (ctxt->fillHollow) + XDrawLines((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCfill], xyp, n, CoordModeOrigin); else { - XFillPolygon((Display*)fDisplay, gCws->fDrawing, *gGCfill, + XFillPolygon((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCfill], xyp, n, Nonconvex, CoordModeOrigin); } } @@ -608,11 +595,24 @@ void TGX11::DrawFillArea(int n, TPoint *xy) void TGX11::DrawLine(Int_t x1, Int_t y1, Int_t x2, Int_t y2) { - if (gLineStyle == LineSolid) - XDrawLine((Display*)fDisplay, gCws->fDrawing, *gGCline, x1, y1, x2, y2); + DrawLineW((WinContext_t) gCws, x1, y1, x2, y2); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw a line on specified window. +/// +/// \param [in] x1,y1 : begin of line +/// \param [in] x2,y2 : end of line + +void TGX11::DrawLineW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2) +{ + auto ctxt = (XWindow_t *) wctxt; + + if (ctxt->lineStyle == LineSolid) + XDrawLine((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCline], x1, y1, x2, y2); else { - XSetDashes((Display*)fDisplay, *gGCdash, gDashOffset, gDashList, gDashSize); - XDrawLine((Display*)fDisplay, gCws->fDrawing, *gGCdash, x1, y1, x2, y2); + XSetDashes((Display*)fDisplay, ctxt->fGClist[kGCdash], ctxt->dashOffset, ctxt->dashList.data(), ctxt->dashList.size()); + XDrawLine((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCdash], x1, y1, x2, y2); } } @@ -624,6 +624,18 @@ void TGX11::DrawLine(Int_t x1, Int_t y1, Int_t x2, Int_t y2) void TGX11::DrawPolyLine(int n, TPoint *xy) { + DrawPolyLineW((WinContext_t) gCws, n, xy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw a line through all points on specified window. +/// +/// \param [in] n number of points +/// \param [in] xy list of points + +void TGX11::DrawPolyLineW(WinContext_t wctxt, Int_t n, TPoint *xy) +{ + auto ctxt = (XWindow_t *) wctxt; XPoint *xyp = (XPoint*)xy; const Int_t kMaxPoints = 1000001; @@ -632,39 +644,36 @@ void TGX11::DrawPolyLine(int n, TPoint *xy) int ibeg = 0; int iend = kMaxPoints - 1; while (iend < n) { - DrawPolyLine( kMaxPoints, &xy[ibeg] ); + DrawPolyLineW(wctxt, kMaxPoints, &xy[ibeg]); ibeg = iend; iend += kMaxPoints - 1; } if (ibeg < n) { int npt = n - ibeg; - DrawPolyLine( npt, &xy[ibeg] ); + DrawPolyLineW(wctxt, npt, &xy[ibeg]); } } else if (n > 1) { - if (gLineStyle == LineSolid) - XDrawLines((Display*)fDisplay, gCws->fDrawing, *gGCline, xyp, n, CoordModeOrigin); + if (ctxt->lineStyle == LineSolid) + XDrawLines((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCline], xyp, n, CoordModeOrigin); else { - int i; - XSetDashes((Display*)fDisplay, *gGCdash, - gDashOffset, gDashList, gDashSize); - XDrawLines((Display*)fDisplay, gCws->fDrawing, *gGCdash, xyp, n, CoordModeOrigin); + XSetDashes((Display*)fDisplay, ctxt->fGClist[kGCdash], ctxt->dashOffset, ctxt->dashList.data(), ctxt->dashList.size()); + XDrawLines((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCdash], xyp, n, CoordModeOrigin); // calculate length of line to update dash offset - for (i = 1; i < n; i++) { + for (int i = 1; i < n; i++) { int dx = xyp[i].x - xyp[i-1].x; int dy = xyp[i].y - xyp[i-1].y; if (dx < 0) dx = - dx; if (dy < 0) dy = - dy; - gDashOffset += dx > dy ? dx : dy; + ctxt->dashOffset += dx > dy ? dx : dy; } - gDashOffset %= gDashLength; + ctxt->dashOffset %= ctxt->dashLength; } } else { - int px,py; - px=xyp[0].x; - py=xyp[0].y; - XDrawPoint((Display*)fDisplay, gCws->fDrawing, - gLineStyle == LineSolid ? *gGCline : *gGCdash, px, py); + int px = xyp[0].x; + int py = xyp[0].y; + XDrawPoint((Display*)fDisplay, ctxt->fDrawing, + ctxt->lineStyle == LineSolid ? ctxt->fGClist[kGCline] : ctxt->fGClist[kGCdash], px, py); } } @@ -676,21 +685,34 @@ void TGX11::DrawPolyLine(int n, TPoint *xy) void TGX11::DrawLinesSegments(Int_t n, TPoint *xy) { + DrawLinesSegmentsW((WinContext_t) gCws, n, xy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draws N segments between provided points on specified windows +/// +/// \param [in] n number of segements +/// \param [in] xy list of points, size 2*n + +void TGX11::DrawLinesSegmentsW(WinContext_t wctxt, Int_t n, TPoint *xy) +{ + auto ctxt = (XWindow_t *) wctxt; + const Int_t kMaxSegments = 500000; if (n > kMaxSegments) { Int_t ibeg = 0; Int_t iend = kMaxSegments; while (ibeg < n) { - DrawLinesSegments(iend - ibeg, &xy[ibeg*2]); + DrawLinesSegmentsW(wctxt, iend - ibeg, &xy[ibeg*2]); ibeg = iend; iend = TMath::Min(n, iend + kMaxSegments); } } else if (n > 0) { - if (gLineStyle == LineSolid) - XDrawSegments((Display*)fDisplay, gCws->fDrawing, *gGCline, (XSegment *) xy, n); + if (ctxt->lineStyle == LineSolid) + XDrawSegments((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCline], (XSegment *) xy, n); else { - XSetDashes((Display*)fDisplay, *gGCdash, gDashOffset, gDashList, gDashSize); - XDrawSegments((Display*)fDisplay, gCws->fDrawing, *gGCdash, (XSegment *) xy, n); + XSetDashes((Display*)fDisplay, ctxt->fGClist[kGCdash], ctxt->dashOffset, ctxt->dashList.data(), ctxt->dashList.size()); + XDrawSegments((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCdash], (XSegment *) xy, n); } } } @@ -704,63 +726,69 @@ void TGX11::DrawLinesSegments(Int_t n, TPoint *xy) void TGX11::DrawPolyMarker(int n, TPoint *xy) { - XPoint *xyp = (XPoint*)xy; + DrawPolyMarkerW((WinContext_t) gCws, n, xy); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw n markers with the current attributes at position x, y on specified window. +/// +/// \param [in] n number of markers to draw +/// \param [in] xy x,y coordinates of markers + +void TGX11::DrawPolyMarkerW(WinContext_t wctxt, Int_t n, TPoint *xy) +{ + auto ctxt = (XWindow_t *) wctxt; + XPoint *xyp = (XPoint *) xy; - if (gMarker.n <= 0) { + if ((ctxt->markerShape.size() == 0) && (ctxt->markerSize <= 0)) { const int kNMAX = 1000000; int nt = n/kNMAX; for (int it=0;it<=nt;it++) { if (it < nt) { - XDrawPoints((Display*)fDisplay, gCws->fDrawing, *gGCmark, &xyp[it*kNMAX], kNMAX, CoordModeOrigin); + XDrawPoints((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCmark], &xyp[it*kNMAX], kNMAX, CoordModeOrigin); } else { - XDrawPoints((Display*)fDisplay, gCws->fDrawing, *gGCmark, &xyp[it*kNMAX], n-it*kNMAX, CoordModeOrigin); + XDrawPoints((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCmark], &xyp[it*kNMAX], n-it*kNMAX, CoordModeOrigin); } } } else { - int r = gMarker.n / 2; - int m; - - for (m = 0; m < n; m++) { - int hollow = 0; - - switch (gMarker.type) { - int i; - - case 0: // hollow circle - XDrawArc((Display*)fDisplay, gCws->fDrawing, *gGCmark, - xyp[m].x - r, xyp[m].y - r, gMarker.n, gMarker.n, 0, 360*64); - break; - - case 1: // filled circle - XFillArc((Display*)fDisplay, gCws->fDrawing, *gGCmark, - xyp[m].x - r, xyp[m].y - r, gMarker.n, gMarker.n, 0, 360*64); - break; - - case 2: // hollow polygon - hollow = 1; - case 3: // filled polygon - for (i = 0; i < gMarker.n; i++) { - gMarker.xy[i].x += xyp[m].x; - gMarker.xy[i].y += xyp[m].y; - } - if (hollow) - XDrawLines((Display*)fDisplay, gCws->fDrawing, *gGCmark, - gMarker.xy, gMarker.n, CoordModeOrigin); - else - XFillPolygon((Display*)fDisplay, gCws->fDrawing, *gGCmark, - gMarker.xy, gMarker.n, Nonconvex, CoordModeOrigin); - for (i = 0; i < gMarker.n; i++) { - gMarker.xy[i].x -= xyp[m].x; - gMarker.xy[i].y -= xyp[m].y; - } - break; - - case 4: // segmented line - for (i = 0; i < gMarker.n; i += 2) - XDrawLine((Display*)fDisplay, gCws->fDrawing, *gGCmark, - xyp[m].x + gMarker.xy[i].x, xyp[m].y + gMarker.xy[i].y, - xyp[m].x + gMarker.xy[i+1].x, xyp[m].y + gMarker.xy[i+1].y); - break; + int r = ctxt->markerSize / 2; + auto &shape = ctxt->markerShape; + + for (int m = 0; m < n; m++) { + if (ctxt->markerType == 0) { + // hollow circle + XDrawArc((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCmark], + xyp[m].x - r, xyp[m].y - r, ctxt->markerSize, ctxt->markerSize, 0, 360*64); + } else if (ctxt->markerType == 1) { + // filled circle + XFillArc((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCmark], + xyp[m].x - r, xyp[m].y - r, ctxt->markerSize, ctxt->markerSize, 0, 360*64); + } else { + for (size_t i = 0; i < shape.size(); i++) { + shape[i].x += xyp[m].x; + shape[i].y += xyp[m].y; + } + switch(ctxt->markerType) { + case 2: + // hollow polygon + XDrawLines((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCmark], + shape.data(), shape.size(), CoordModeOrigin); + break; + case 3: + // filled polygon + XFillPolygon((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCmark], + shape.data(), shape.size(), Nonconvex, CoordModeOrigin); + break; + case 4: + // segmented line + XDrawSegments((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCmark], + (XSegment *) shape.data(), shape.size()/2); + break; + } + for (size_t i = 0; i < shape.size(); i++) { + shape[i].x -= xyp[m].x; + shape[i].y -= xyp[m].y; + } } } } @@ -780,20 +808,57 @@ void TGX11::DrawPolyMarker(int n, TPoint *xy) void TGX11::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode) { - XRotSetMagnification(mgn); + DrawTextW((WinContext_t) gCws, x, y, angle, mgn, text, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw a text string using current font. +/// +/// \param [in] mode : drawing mode +/// - mode=0 : the background is not drawn (kClear) +/// - mode=1 : the background is drawn (kOpaque) +/// \param [in] x,y : text position +/// \param [in] angle : text angle +/// \param [in] mgn : magnification factor +/// \param [in] text : text string + +void TGX11::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, + const wchar_t *text, ETextMode mode) +{ + DrawTextW((WinContext_t) gCws, x, y, angle, mgn, text, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Draw a text string using current font on specified window. +/// +/// \param [in] mode : drawing mode +/// - mode=0 : the background is not drawn (kClear) +/// - mode=1 : the background is drawn (kOpaque) +/// \param [in] x,y : text position +/// \param [in] angle : text angle +/// \param [in] mgn : magnification factor +/// \param [in] text : text string + +void TGX11::DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, + const char *text, ETextMode mode) +{ + auto ctxt = (XWindow_t *) wctxt; - if (!text) return; + if (!text || !ctxt->textFont || ctxt->fAttText.GetTextSize() < 0) + return; + + XRotSetMagnification(mgn); switch (mode) { case kClear: - XRotDrawAlignedString((Display*)fDisplay, gTextFont, angle, - gCws->fDrawing, *gGCtext, x, y, (char*)text, fTextAlign); + XRotDrawAlignedString((Display*)fDisplay, ctxt->textFont, angle, + ctxt->fDrawing, ctxt->fGClist[kGCtext], x, y, (char*)text, (int) ctxt->textAlign); break; case kOpaque: - XRotDrawAlignedImageString((Display*)fDisplay, gTextFont, angle, - gCws->fDrawing, *gGCtext, x, y, (char*)text, fTextAlign); + XRotDrawAlignedImageString((Display*)fDisplay, ctxt->textFont, angle, + ctxt->fDrawing, ctxt->fGClist[kGCtext], x, y, (char*)text, (int) ctxt->textAlign); break; default: @@ -946,7 +1011,7 @@ XColor_t &TGX11::GetColor(Int_t cid) } //////////////////////////////////////////////////////////////////////////////// -/// Return current window pointer. Protected method used by TGX11TTF. +/// Return current window pointer. Window_t TGX11::GetCurrentWindow() const { @@ -963,7 +1028,50 @@ void *TGX11::GetGC(Int_t which) const Error("GetGC", "trying to get illegal GC (which = %d)", which); return nullptr; } - return &gGClist[which]; + if (!gCws) { + Error("GetGC", "No current window selected"); + return nullptr; + } + return &gCws->fGClist[which]; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return X11 window for specified window context. +/// Protected method used by TGX11TTF. + +Window_t TGX11::GetWindow(WinContext_t wctxt) const +{ + auto ctxt = (XWindow_t *) wctxt; + return (Window_t) (ctxt ? ctxt->fDrawing : 0); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return X11 Graphics Context for specified window context. +/// Protected method used by TGX11TTF. + +void *TGX11::GetGCW(WinContext_t wctxt, Int_t which) const +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) { + Error("GetGC", "No window context specified"); + return nullptr; + } + + if (which >= kMAXGC || which < 0) { + Error("GetGC", "trying to get illegal GC (which = %d)", which); + return nullptr; + } + return &ctxt->fGClist[which]; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return text align value for specified window context. +/// Protected method used by TGX11TTF. + +TGX11::EAlign TGX11::GetTextAlignW(WinContext_t wctxt) const +{ + auto ctxt = (XWindow_t *) wctxt; + return ctxt ? ctxt->textAlign : kAlignNone; } //////////////////////////////////////////////////////////////////////////////// @@ -971,7 +1079,7 @@ void *TGX11::GetGC(Int_t which) const Int_t TGX11::GetDoubleBuffer(int wid) { - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); if (!gTws->fOpen) return -1; else @@ -1001,7 +1109,7 @@ void TGX11::GetGeometry(int wid, int &x, int &y, unsigned int &w, unsigned int & unsigned int border, depth; unsigned int width, height; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); XGetGeometry((Display*)fDisplay, gTws->fWindow, &root, &x, &y, &width, &height, &border, &depth); XTranslateCoordinates((Display*)fDisplay, gTws->fWindow, fRootWin, @@ -1096,7 +1204,8 @@ void TGX11::GetTextExtent(UInt_t &w, UInt_t &h, char *mess) Window_t TGX11::GetWindowID(int wid) { - return (Window_t) fWindows[wid].fWindow; + if (fWindows.count(wid) == 0) return 0; + return (Window_t) fWindows[wid]->fWindow; } //////////////////////////////////////////////////////////////////////////////// @@ -1108,7 +1217,7 @@ Window_t TGX11::GetWindowID(int wid) void TGX11::MoveWindow(Int_t wid, Int_t x, Int_t y) { - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); if (!gTws->fOpen) return; XMoveWindow((Display*)fDisplay, gTws->fWindow, x, y); @@ -1141,23 +1250,6 @@ Int_t TGX11::OpenDisplay(void *disp) char vendor[132]; strlcpy(vendor, XServerVendor((Display*)fDisplay),132); - // Create primitives graphic contexts - for (i = 0; i < kMAXGC; i++) - gGClist[i] = XCreateGC((Display*)fDisplay, fVisRootWin, 0, nullptr); - - XGCValues values; - if (XGetGCValues((Display*)fDisplay, *gGCtext, GCForeground|GCBackground, &values)) { - XSetForeground((Display*)fDisplay, *gGCinvt, values.background); - XSetBackground((Display*)fDisplay, *gGCinvt, values.foreground); - } else { - Error("OpenDisplay", "cannot get GC values"); - } - - // Turn-off GraphicsExpose and NoExpose event reporting for the pixmap - // manipulation GC, this to prevent these events from being stacked up - // without ever being processed and thereby wasting a lot of memory. - XSetGraphicsExposures((Display*)fDisplay, *gGCpxmp, False); - // Create input echo graphic context XGCValues echov; echov.foreground = fBlackPixel; @@ -1264,6 +1356,60 @@ Int_t TGX11::OpenDisplay(void *disp) return 0; } + +//////////////////////////////////////////////////////////////////////////////// +/// Add new window handle +/// Only for private usage + +Int_t TGX11::AddWindowHandle() +{ + Int_t maxid = 0; + for (auto & pair : fWindows) { + if (!pair.second->fOpen) { + pair.second->fOpen = 1; + return pair.first; + } + if (pair.first > maxid) + maxid = pair.first; + } + + if (fWindows.size() == (size_t) maxid) { + // all ids are in use - just add maximal+1 + maxid++; + } else + for (int id = 1; id < maxid; id++) { + if (fWindows.count(id) == 0) { + maxid = id; + break; + } + } + + fWindows.emplace(maxid, std::make_unique()); + + auto ctxt = fWindows[maxid].get(); + ctxt->fOpen = 1; + ctxt->drawMode = TVirtualX::kCopy; + for (int n = 0; n < kMAXGC; ++n) + ctxt->fGClist[n] = XCreateGC((Display*)fDisplay, fVisRootWin, 0, nullptr); + + XGCValues values; + if (XGetGCValues((Display*)fDisplay, ctxt->fGClist[kGCtext], GCForeground|GCBackground, &values)) { + XSetForeground((Display*)fDisplay, ctxt->fGClist[kGCinvt], values.background); + XSetBackground((Display*)fDisplay, ctxt->fGClist[kGCinvt], values.foreground); + } else { + Error("AddWindowHandle", "cannot get GC values"); + } + + // Turn-off GraphicsExpose and NoExpose event reporting for the pixmap + // manipulation GC, this to prevent these events from being stacked up + // without ever being processed and thereby wasting a lot of memory. + XSetGraphicsExposures((Display*)fDisplay, ctxt->fGClist[kGCpxmp], False); + + return maxid; +} + + + //////////////////////////////////////////////////////////////////////////////// /// Open a new pixmap. /// @@ -1273,40 +1419,24 @@ Int_t TGX11::OpenPixmap(unsigned int w, unsigned int h) { Window root; unsigned int wval, hval; - int xx, yy, i, wid; + int xx, yy; unsigned int ww, hh, border, depth; wval = w; hval = h; // Select next free window number - -again: - for (wid = 0; wid < fMaxNumberOfWindows; wid++) - if (!fWindows[wid].fOpen) { - fWindows[wid].fOpen = 1; - gCws = &fWindows[wid]; - break; - } - - if (wid == fMaxNumberOfWindows) { - int newsize = fMaxNumberOfWindows + 10; - fWindows = (XWindow_t*) TStorage::ReAlloc(fWindows, newsize*sizeof(XWindow_t), - fMaxNumberOfWindows*sizeof(XWindow_t)); - for (i = fMaxNumberOfWindows; i < newsize; i++) - fWindows[i].fOpen = 0; - fMaxNumberOfWindows = newsize; - goto again; - } + int wid = AddWindowHandle(); + gCws = fWindows[wid].get(); gCws->fWindow = XCreatePixmap((Display*)fDisplay, fRootWin, wval, hval, fDepth); XGetGeometry((Display*)fDisplay, gCws->fWindow, &root, &xx, &yy, &ww, &hh, &border, &depth); - for (i = 0; i < kMAXGC; i++) - XSetClipMask((Display*)fDisplay, gGClist[i], None); + for (int i = 0; i < kMAXGC; i++) + XSetClipMask((Display*)fDisplay, gCws->fGClist[i], None); - SetColor(gGCpxmp, 0); - XFillRectangle((Display*)fDisplay, gCws->fWindow, *gGCpxmp, 0, 0, ww, hh); - SetColor(gGCpxmp, 1); + SetColor(gCws, &gCws->fGClist[kGCpxmp], 0); + XFillRectangle((Display*)fDisplay, gCws->fWindow, gCws->fGClist[kGCpxmp], 0, 0, ww, hh); + SetColor(gCws, &gCws->fGClist[kGCpxmp], 1); // Initialise the window structure gCws->fDrawing = gCws->fWindow; @@ -1331,7 +1461,6 @@ Int_t TGX11::InitWindow(ULong_t win) { XSetWindowAttributes attributes; ULong_t attr_mask = 0; - int wid; int xval, yval; unsigned int wval, hval, border, depth; Window root; @@ -1342,24 +1471,9 @@ Int_t TGX11::InitWindow(ULong_t win) // Select next free window number -again: - for (wid = 0; wid < fMaxNumberOfWindows; wid++) - if (!fWindows[wid].fOpen) { - fWindows[wid].fOpen = 1; - fWindows[wid].fDoubleBuffer = 0; - gCws = &fWindows[wid]; - break; - } - - if (wid == fMaxNumberOfWindows) { - int newsize = fMaxNumberOfWindows + 10; - fWindows = (XWindow_t*) TStorage::ReAlloc(fWindows, newsize*sizeof(XWindow_t), - fMaxNumberOfWindows*sizeof(XWindow_t)); - for (int i = fMaxNumberOfWindows; i < newsize; i++) - fWindows[i].fOpen = 0; - fMaxNumberOfWindows = newsize; - goto again; - } + int wid = AddWindowHandle(); + gCws = fWindows[wid].get(); + gCws->fDoubleBuffer = 0; // Create window @@ -1406,28 +1520,10 @@ Int_t TGX11::InitWindow(ULong_t win) Int_t TGX11::AddWindow(ULong_t qwid, UInt_t w, UInt_t h) { - Int_t wid; - // Select next free window number - -again: - for (wid = 0; wid < fMaxNumberOfWindows; wid++) - if (!fWindows[wid].fOpen) { - fWindows[wid].fOpen = 1; - fWindows[wid].fDoubleBuffer = 0; - gCws = &fWindows[wid]; - break; - } - - if (wid == fMaxNumberOfWindows) { - int newsize = fMaxNumberOfWindows + 10; - fWindows = (XWindow_t*) TStorage::ReAlloc(fWindows, newsize*sizeof(XWindow_t), - fMaxNumberOfWindows*sizeof(XWindow_t)); - for (int i = fMaxNumberOfWindows; i < newsize; i++) - fWindows[i].fOpen = 0; - fMaxNumberOfWindows = newsize; - goto again; - } + int wid = AddWindowHandle(); + gCws = fWindows[wid].get(); + gCws->fDoubleBuffer = 0; gCws->fWindow = qwid; @@ -1446,32 +1542,14 @@ Int_t TGX11::AddWindow(ULong_t qwid, UInt_t w, UInt_t h) } //////////////////////////////////////////////////////////////////////////////// -/// Remove a window created by Qt (like CloseWindow1()). +/// Remove a window created by Qt (like CloseWindow()). void TGX11::RemoveWindow(ULong_t qwid) { - SelectWindow((int)qwid); + SelectWindow((int) qwid); - if (gCws->fBuffer) XFreePixmap((Display*)fDisplay, gCws->fBuffer); - - if (gCws->fNewColors) { - if (fRedDiv == -1) - XFreeColors((Display*)fDisplay, fColormap, gCws->fNewColors, gCws->fNcolors, 0); - delete [] gCws->fNewColors; - gCws->fNewColors = nullptr; - } - - gCws->fOpen = 0; - - // make first window in list the current window - for (Int_t wid = 0; wid < fMaxNumberOfWindows; wid++) - if (fWindows[wid].fOpen) { - gCws = &fWindows[wid]; - return; - } - - gCws = nullptr; -} + CloseWindow(); +} //////////////////////////////////////////////////////////////////////////////// /// Query pointer position. @@ -1495,13 +1573,6 @@ void TGX11::QueryPointer(Int_t &ix, Int_t &iy) iy = root_y_return; } -//////////////////////////////////////////////////////////////////////////////// -/// Remove the pixmap pix. - -void TGX11::RemovePixmap(Drawable *pix) -{ - XFreePixmap((Display*)fDisplay,*pix); -} //////////////////////////////////////////////////////////////////////////////// /// Request Locator position. @@ -1740,11 +1811,11 @@ Int_t TGX11::RequestString(int x, int y, char *text) char nbytes; int dx; int i; - XDrawImageString((Display*)fDisplay, gCws->fWindow, *gGCtext, x, y, text, nt); - dx = XTextWidth(gTextFont, text, nt); - XDrawImageString((Display*)fDisplay, gCws->fWindow, *gGCtext, x + dx, y, " ", 1); - dx = pt == 0 ? 0 : XTextWidth(gTextFont, text, pt); - XDrawImageString((Display*)fDisplay, gCws->fWindow, *gGCinvt, + XDrawImageString((Display*)fDisplay, gCws->fWindow, gCws->fGClist[kGCtext], x, y, text, nt); + dx = XTextWidth(gCws->textFont, text, nt); + XDrawImageString((Display*)fDisplay, gCws->fWindow, gCws->fGClist[kGCtext], x + dx, y, " ", 1); + dx = pt == 0 ? 0 : XTextWidth(gCws->textFont, text, pt); + XDrawImageString((Display*)fDisplay, gCws->fWindow, gCws->fGClist[kGCinvt], x + dx, y, pt < len_text ? &text[pt] : " ", 1); XWindowEvent((Display*)fDisplay, gCws->fWindow, gKeybdMask, &event); switch (event.type) { @@ -1869,9 +1940,7 @@ Int_t TGX11::RequestString(int x, int y, char *text) void TGX11::RescaleWindow(int wid, unsigned int w, unsigned int h) { - int i; - - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); if (!gTws->fOpen) return; // don't do anything when size did not change @@ -1885,11 +1954,13 @@ void TGX11::RescaleWindow(int wid, unsigned int w, unsigned int h) XFreePixmap((Display*)fDisplay,gTws->fBuffer); gTws->fBuffer = XCreatePixmap((Display*)fDisplay, fRootWin, w, h, fDepth); } - for (i = 0; i < kMAXGC; i++) XSetClipMask((Display*)fDisplay, gGClist[i], None); - SetColor(gGCpxmp, 0); - XFillRectangle( (Display*)fDisplay, gTws->fBuffer, *gGCpxmp, 0, 0, w, h); - SetColor(gGCpxmp, 1); - if (gTws->fDoubleBuffer) gTws->fDrawing = gTws->fBuffer; + for (int i = 0; i < kMAXGC; i++) + XSetClipMask((Display*)fDisplay, gTws->fGClist[i], None); + SetColor(gTws, &gTws->fGClist[kGCpxmp], 0); + XFillRectangle((Display*)fDisplay, gTws->fBuffer, gTws->fGClist[kGCpxmp], 0, 0, w, h); + SetColor(gTws, &gTws->fGClist[kGCpxmp], 1); + if (gTws->fDoubleBuffer) + gTws->fDrawing = gTws->fBuffer; } gTws->fWidth = w; gTws->fHeight = h; @@ -1910,7 +1981,7 @@ int TGX11::ResizePixmap(int wid, unsigned int w, unsigned int h) wval = w; hval = h; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); // don't do anything when size did not change // if (gTws->fWidth == wval && gTws->fHeight == hval) return 0; @@ -1928,11 +1999,11 @@ int TGX11::ResizePixmap(int wid, unsigned int w, unsigned int h) XGetGeometry((Display*)fDisplay, gTws->fWindow, &root, &xx, &yy, &ww, &hh, &border, &depth); for (i = 0; i < kMAXGC; i++) - XSetClipMask((Display*)fDisplay, gGClist[i], None); + XSetClipMask((Display*)fDisplay, gTws->fGClist[i], None); - SetColor(gGCpxmp, 0); - XFillRectangle((Display*)fDisplay, gTws->fWindow, *gGCpxmp, 0, 0, ww, hh); - SetColor(gGCpxmp, 1); + SetColor(gTws, &gTws->fGClist[kGCpxmp], 0); + XFillRectangle((Display*)fDisplay, gTws->fWindow, gTws->fGClist[kGCpxmp], 0, 0, ww, hh); + SetColor(gTws, &gTws->fGClist[kGCpxmp], 1); // Initialise the window structure gTws->fDrawing = gTws->fWindow; @@ -1947,12 +2018,11 @@ int TGX11::ResizePixmap(int wid, unsigned int w, unsigned int h) void TGX11::ResizeWindow(Int_t wid) { - int i; int xval=0, yval=0; Window win, root=0; unsigned int wval=0, hval=0, border=0, depth=0; - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); win = gTws->fWindow; @@ -1971,11 +2041,13 @@ void TGX11::ResizeWindow(Int_t wid) XFreePixmap((Display*)fDisplay,gTws->fBuffer); gTws->fBuffer = XCreatePixmap((Display*)fDisplay, fRootWin, wval, hval, fDepth); } - for (i = 0; i < kMAXGC; i++) XSetClipMask((Display*)fDisplay, gGClist[i], None); - SetColor(gGCpxmp, 0); - XFillRectangle((Display*)fDisplay, gTws->fBuffer, *gGCpxmp, 0, 0, wval, hval); - SetColor(gGCpxmp, 1); - if (gTws->fDoubleBuffer) gTws->fDrawing = gTws->fBuffer; + for (int i = 0; i < kMAXGC; i++) + XSetClipMask((Display*)fDisplay, gTws->fGClist[i], None); + SetColor(gTws, &gTws->fGClist[kGCpxmp], 0); + XFillRectangle((Display*)fDisplay, gTws->fBuffer, gTws->fGClist[kGCpxmp], 0, 0, wval, hval); + SetColor(gTws, &gTws->fGClist[kGCpxmp], 1); + if (gTws->fDoubleBuffer) + gTws->fDrawing = gTws->fBuffer; } gTws->fWidth = wval; gTws->fHeight = hval; @@ -1986,23 +2058,25 @@ void TGX11::ResizeWindow(Int_t wid) void TGX11::SelectWindow(int wid) { - XRectangle region; - int i; + if ((wid == 0) || (fWindows.count(wid) == 0)) + return; - if (wid < 0 || wid >= fMaxNumberOfWindows || !fWindows[wid].fOpen) return; + if (!fWindows[wid]->fOpen) + return; - gCws = &fWindows[wid]; + gCws = fWindows[wid].get(); if (gCws->fClip && !gCws->fIsPixmap && !gCws->fDoubleBuffer) { + XRectangle region; region.x = gCws->fXclip; region.y = gCws->fYclip; region.width = gCws->fWclip; region.height = gCws->fHclip; - for (i = 0; i < kMAXGC; i++) - XSetClipRectangles((Display*)fDisplay, gGClist[i], 0, 0, ®ion, 1, YXBanded); + for (int i = 0; i < kMAXGC; i++) + XSetClipRectangles((Display*)fDisplay, gCws->fGClist[i], 0, 0, ®ion, 1, YXBanded); } else { - for (i = 0; i < kMAXGC; i++) - XSetClipMask((Display*)fDisplay, gGClist[i], None); + for (int i = 0; i < kMAXGC; i++) + XSetClipMask((Display*)fDisplay, gCws->fGClist[i], None); } } @@ -2032,11 +2106,11 @@ void TGX11::SetCharacterUp(Float_t chupx, Float_t chupy) void TGX11::SetClipOFF(int wid) { - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); gTws->fClip = 0; for (int i = 0; i < kMAXGC; i++) - XSetClipMask( (Display*)fDisplay, gGClist[i], None ); + XSetClipMask( (Display*)fDisplay, gTws->fGClist[i], None ); } //////////////////////////////////////////////////////////////////////////////// @@ -2048,8 +2122,7 @@ void TGX11::SetClipOFF(int wid) void TGX11::SetClipRegion(int wid, int x, int y, unsigned int w, unsigned int h) { - - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); gTws->fXclip = x; gTws->fYclip = y; gTws->fWclip = w; @@ -2062,14 +2135,14 @@ void TGX11::SetClipRegion(int wid, int x, int y, unsigned int w, unsigned int h) region.width = gTws->fWclip; region.height = gTws->fHclip; for (int i = 0; i < kMAXGC; i++) - XSetClipRectangles((Display*)fDisplay, gGClist[i], 0, 0, ®ion, 1, YXBanded); + XSetClipRectangles((Display*)fDisplay, gTws->fGClist[i], 0, 0, ®ion, 1, YXBanded); } } //////////////////////////////////////////////////////////////////////////////// /// Set the foreground color in GC. -void TGX11::SetColor(void *gci, int ci) +void TGX11::SetColor(XWindow_t *ctxt, void *gci, int ci) { GC gc = *(GC *)gci; @@ -2084,7 +2157,7 @@ void TGX11::SetColor(void *gci, int ci) col = GetColor(0); } - if (fDrawMode == kXor) { + if (ctxt && ctxt->drawMode == kXor) { XGCValues values; XGetGCValues((Display*)fDisplay, gc, GCBackground, &values); XSetForeground((Display*)fDisplay, gc, col.fPixel ^ values.background); @@ -2104,7 +2177,7 @@ void TGX11::SetColor(void *gci, int ci) void TGX11::SetCursor(Int_t wid, ECursor cursor) { - gTws = &fWindows[wid]; + gTws = fWindows[wid].get(); XDefineCursor((Display*)fDisplay, gTws->fWindow, fCursors[cursor]); } @@ -2120,8 +2193,8 @@ void TGX11::SetCursor(Int_t wid, ECursor cursor) void TGX11::SetDoubleBuffer(int wid, int mode) { if (wid == 999) { - for (int i = 0; i < fMaxNumberOfWindows; i++) { - gTws = &fWindows[i]; + for (auto & pair : fWindows) { + gTws = pair.second.get(); if (gTws->fOpen) { switch (mode) { case 1 : @@ -2134,8 +2207,9 @@ void TGX11::SetDoubleBuffer(int wid, int mode) } } } else { - gTws = &fWindows[wid]; - if (!gTws->fOpen) return; + gTws = fWindows[wid].get(); + if (!gTws->fOpen) + return; switch (mode) { case 1 : SetDoubleBufferON(); @@ -2162,15 +2236,17 @@ void TGX11::SetDoubleBufferOFF() void TGX11::SetDoubleBufferON() { - if (gTws->fDoubleBuffer || gTws->fIsPixmap) return; + if (gTws->fDoubleBuffer || gTws->fIsPixmap) + return; if (!gTws->fBuffer) { gTws->fBuffer = XCreatePixmap((Display*)fDisplay, fRootWin, gTws->fWidth, gTws->fHeight, fDepth); - SetColor(gGCpxmp, 0); - XFillRectangle((Display*)fDisplay, gTws->fBuffer, *gGCpxmp, 0, 0, gTws->fWidth, gTws->fHeight); - SetColor(gGCpxmp, 1); + SetColor(gTws, &gTws->fGClist[kGCpxmp], 0); + XFillRectangle((Display*)fDisplay, gTws->fBuffer, gTws->fGClist[kGCpxmp], 0, 0, gTws->fWidth, gTws->fHeight); + SetColor(gTws, &gTws->fGClist[kGCpxmp], 1); } - for (int i = 0; i < kMAXGC; i++) XSetClipMask((Display*)fDisplay, gGClist[i], None); + for (int i = 0; i < kMAXGC; i++) + XSetClipMask((Display*)fDisplay, gTws->fGClist[i], None); gTws->fDoubleBuffer = 1; gTws->fDrawing = gTws->fBuffer; } @@ -2187,23 +2263,7 @@ void TGX11::SetDoubleBufferON() void TGX11::SetDrawMode(EDrawMode mode) { - int i; - if (fDisplay) { - switch (mode) { - case kCopy: - for (i = 0; i < kMAXGC; i++) XSetFunction((Display*)fDisplay, gGClist[i], GXcopy); - break; - - case kXor: - for (i = 0; i < kMAXGC; i++) XSetFunction((Display*)fDisplay, gGClist[i], GXxor); - break; - - case kInvert: - for (i = 0; i < kMAXGC; i++) XSetFunction((Display*)fDisplay, gGClist[i], GXinvert); - break; - } - } - fDrawMode = mode; + SetDrawModeW((WinContext_t) gCws, mode); } //////////////////////////////////////////////////////////////////////////////// @@ -2211,15 +2271,14 @@ void TGX11::SetDrawMode(EDrawMode mode) void TGX11::SetFillColor(Color_t cindex) { - if (!gStyle->GetFillColor() && cindex > 1) cindex = 0; - if (cindex >= 0) SetColor(gGCfill, Int_t(cindex)); - fFillColor = cindex; + if (cindex < 0) return; - // invalidate fill pattern - if (gFillPattern != 0) { - XFreePixmap((Display*)fDisplay, gFillPattern); - gFillPattern = 0; - } + TAttFill::SetFillColor(cindex); + + TAttFill arg(gCws->fAttFill); + arg.SetFillColor(cindex); + + SetAttFill((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -2230,54 +2289,21 @@ void TGX11::SetFillColor(Color_t cindex) void TGX11::SetFillStyle(Style_t fstyle) { - if (fFillStyle == fstyle) return; - fFillStyle = fstyle; - Int_t style = fstyle/1000; - Int_t fasi = fstyle%1000; - SetFillStyleIndex(style,fasi); + TAttFill::SetFillStyle(fstyle); + + TAttFill arg(gCws->fAttFill); + arg.SetFillStyle(fstyle); + + SetAttFill((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// -/// Set fill area style index. +/// Return current fill style +/// FIXME: Only as temporary solution while some code analyze current fill style -void TGX11::SetFillStyleIndex(Int_t style, Int_t fasi) +Style_t TGX11::GetFillStyle() const { - static int current_fasi = 0; - - fFillStyle = 1000*style + fasi; - - switch (style) { - - case 1: // solid - gFillHollow = 0; - XSetFillStyle((Display*)fDisplay, *gGCfill, FillSolid); - break; - - case 2: // pattern - gFillHollow = 1; - break; - - case 3: // hatch - gFillHollow = 0; - XSetFillStyle((Display*)fDisplay, *gGCfill, FillStippled); - if (fasi != current_fasi) { - if (gFillPattern != 0) { - XFreePixmap((Display*)fDisplay, gFillPattern); - gFillPattern = 0; - } - int stn = (fasi >= 1 && fasi <=25) ? fasi : 2; - - gFillPattern = XCreateBitmapFromData((Display*)fDisplay, fRootWin, - (const char*)gStipples[stn], 16, 16); - - XSetStipple( (Display*)fDisplay, *gGCfill, gFillPattern ); - current_fasi = fasi; - } - break; - - default: - gFillHollow = 1; - } + return gCws ? gCws->fAttFill.GetFillStyle() : TAttFill::GetFillStyle(); } //////////////////////////////////////////////////////////////////////////////// @@ -2308,8 +2334,10 @@ void TGX11::SetLineColor(Color_t cindex) TAttLine::SetLineColor(cindex); - SetColor(gGCline, Int_t(cindex)); - SetColor(gGCdash, Int_t(cindex)); + TAttLine arg(gCws->fAttLine); + arg.SetLineColor(cindex); + + SetAttLine((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -2323,27 +2351,9 @@ void TGX11::SetLineColor(Color_t cindex) /// e.g. N=4,DASH=(6,3,1,3) gives a dashed-dotted line with dash length 6 /// and a gap of 7 between dashes -void TGX11::SetLineType(int n, int *dash) +void TGX11::SetLineType(int /* n */, int * /* dash */) { - if (n <= 0) { - gLineStyle = LineSolid; - XSetLineAttributes((Display*)fDisplay, *gGCline, gLineWidth, - gLineStyle, gCapStyle, gJoinStyle); - } else { - gDashSize = TMath::Min((int)sizeof(gDashList),n); - gDashLength = 0; - for (int i = 0; i < gDashSize; i++ ) { - gDashList[i] = dash[i]; - gDashLength += gDashList[i]; - } - gDashOffset = 0; - gLineStyle = LineOnOffDash; - if (gLineWidth == 0) gLineWidth =1; - XSetLineAttributes((Display*)fDisplay, *gGCline, gLineWidth, - gLineStyle, gCapStyle, gJoinStyle); - XSetLineAttributes((Display*)fDisplay, *gGCdash, gLineWidth, - gLineStyle, gCapStyle, gJoinStyle); - } + Warning("SetLineType", "DEPRECATED, use SetAttLine() instead"); } //////////////////////////////////////////////////////////////////////////////// @@ -2351,36 +2361,21 @@ void TGX11::SetLineType(int n, int *dash) void TGX11::SetLineStyle(Style_t lstyle) { - static Int_t dashed[2] = {3,3}; - static Int_t dotted[2] = {1,2}; - static Int_t dasheddotted[4] = {3,4,1,4}; - - if (fLineStyle != lstyle) { //set style index only if different - fLineStyle = lstyle; - if (lstyle <= 1 ) { - SetLineType(0, nullptr); - } else if (lstyle == 2 ) { - SetLineType(2, dashed); - } else if (lstyle == 3 ) { - SetLineType(2, dotted); - } else if (lstyle == 4 ) { - SetLineType(4, dasheddotted); - } else { - TString st = (TString)gStyle->GetLineStyleString(lstyle); - TObjArray *tokens = st.Tokenize(" "); - Int_t nt; - nt = tokens->GetEntries(); - Int_t *linestyle = new Int_t[nt]; - for (Int_t j = 0; jAt(j))->GetName(), "%d", &it); - linestyle[j] = (Int_t)(it/4); - } - SetLineType(nt, linestyle); - delete [] linestyle; - delete tokens; - } - } + TAttLine::SetLineStyle(lstyle); + + TAttLine arg(gCws->fAttLine); + arg.SetLineStyle(lstyle); + + SetAttLine((WinContext_t) gCws, arg); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return current line style +/// FIXME: Only as temporary solution while some code analyze current line style + +Style_t TGX11::GetLineStyle() const +{ + return gCws ? gCws->fAttLine.GetLineStyle() : TAttLine::GetLineStyle(); } //////////////////////////////////////////////////////////////////////////////// @@ -2388,20 +2383,23 @@ void TGX11::SetLineStyle(Style_t lstyle) /// /// \param [in] width : line width in pixels -void TGX11::SetLineWidth(Width_t width ) +void TGX11::SetLineWidth(Width_t width) { - if (fLineWidth == width) return; - fLineWidth = width; + TAttLine::SetLineWidth(width); + + TAttLine arg(gCws->fAttLine); + arg.SetLineWidth(width); - if (width == 1 && gLineStyle == LineSolid) gLineWidth = 0; - else gLineWidth = width; + SetAttLine((WinContext_t) gCws, arg); +} - if (gLineWidth < 0) return; +//////////////////////////////////////////////////////////////////////////////// +/// Return current line width +/// FIXME: Only as temporary solution while some code analyze current line wide - XSetLineAttributes((Display*)fDisplay, *gGCline, gLineWidth, - gLineStyle, gCapStyle, gJoinStyle); - XSetLineAttributes((Display*)fDisplay, *gGCdash, gLineWidth, - gLineStyle, gCapStyle, gJoinStyle); +Width_t TGX11::GetLineWidth() const +{ + return gCws ? gCws->fAttLine.GetLineWidth() : TAttLine::GetLineWidth(); } //////////////////////////////////////////////////////////////////////////////// @@ -2413,7 +2411,10 @@ void TGX11::SetMarkerColor(Color_t cindex) TAttMarker::SetMarkerColor(cindex); - SetColor(gGCmark, Int_t(cindex)); + TAttMarker arg(gCws->fAttMarker); + arg.SetMarkerColor(cindex); + + SetAttMarker((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// @@ -2423,587 +2424,474 @@ void TGX11::SetMarkerColor(Color_t cindex) void TGX11::SetMarkerSize(Float_t msize) { - if (msize == fMarkerSize) return; + TAttMarker::SetMarkerSize(msize); - fMarkerSize = msize; - if (msize < 0) return; + TAttMarker arg(gCws->fAttMarker); + arg.SetMarkerSize(msize); - SetMarkerStyle(-fMarkerStyle); + SetAttMarker((WinContext_t) gCws, arg); } //////////////////////////////////////////////////////////////////////////////// -/// Set marker type. -/// -/// \param [in] type : marker type -/// \param [in] n : length of marker description -/// \param [in] xy : list of points describing marker shape -/// -/// - if n == 0 marker is a single point -/// - if TYPE == 0 marker is hollow circle of diameter N -/// - if TYPE == 1 marker is filled circle of diameter N -/// - if TYPE == 2 marker is a hollow polygon describe by line XY -/// - if TYPE == 3 marker is a filled polygon describe by line XY -/// - if TYPE == 4 marker is described by segmented line XY -/// e.g. TYPE=4,N=4,XY=(-3,0,3,0,0,-3,0,3) sets a plus shape of 7x7 pixels - -void TGX11::SetMarkerType(int type, int n, RXPoint *xy) -{ - gMarker.type = type; - gMarker.n = n < kMAXMK ? n : kMAXMK; - if (gMarker.type >= 2) { - for (int i = 0; i < gMarker.n; i++) { - gMarker.xy[i].x = xy[i].x; - gMarker.xy[i].y = xy[i].y; +/// Set marker style. + +void TGX11::SetMarkerStyle(Style_t markerstyle) +{ + TAttMarker::SetMarkerStyle(markerstyle); + + TAttMarker arg(gCws->fAttMarker); + arg.SetMarkerStyle(markerstyle); + + SetAttMarker((WinContext_t) gCws, arg); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set opacity of a window. This image manipulation routine works +/// by adding to a percent amount of neutral to each pixels RGB. +/// Since it requires quite some additional color map entries is it +/// only supported on displays with more than > 8 color planes (> 256 +/// colors). + +void TGX11::SetOpacity(Int_t percent) +{ + if (fDepth <= 8) return; + if (percent == 0) return; + // if 100 percent then just make white + + ULong_t *orgcolors = nullptr, *tmpc = nullptr; + Int_t maxcolors = 0, ncolors = 0, ntmpc = 0; + + // save previous allocated colors, delete at end when not used anymore + if (gCws->fNewColors) { + tmpc = gCws->fNewColors; + ntmpc = gCws->fNcolors; + } + + // get pixmap from server as image + XImage *image = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, gCws->fWidth, + gCws->fHeight, AllPlanes, ZPixmap); + if (!image) return; + // collect different image colors + int x, y; + for (y = 0; y < (int) gCws->fHeight; y++) { + for (x = 0; x < (int) gCws->fWidth; x++) { + ULong_t pixel = XGetPixel(image, x, y); + CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + } + } + if (ncolors == 0) { + XDestroyImage(image); + ::operator delete(orgcolors); + return; + } + + // create opaque counter parts + MakeOpaqueColors(percent, orgcolors, ncolors); + + if (gCws->fNewColors) { + // put opaque colors in image + for (y = 0; y < (int) gCws->fHeight; y++) { + for (x = 0; x < (int) gCws->fWidth; x++) { + ULong_t pixel = XGetPixel(image, x, y); + Int_t idx = FindColor(pixel, orgcolors, ncolors); + XPutPixel(image, x, y, gCws->fNewColors[idx]); + } } } + + // put image back in pixmap on server + XPutImage((Display*)fDisplay, gCws->fDrawing, gCws->fGClist[kGCpxmp], image, 0, 0, 0, 0, + gCws->fWidth, gCws->fHeight); + XFlush((Display*)fDisplay); + + // clean up + if (tmpc) { + if (fRedDiv == -1) + XFreeColors((Display*)fDisplay, fColormap, tmpc, ntmpc, 0); + delete [] tmpc; + } + XDestroyImage(image); + ::operator delete(orgcolors); } //////////////////////////////////////////////////////////////////////////////// -/// Set marker style. +/// Collect in orgcolors all different original image colors. -void TGX11::SetMarkerStyle(Style_t markerstyle) +void TGX11::CollectImageColors(ULong_t pixel, ULong_t *&orgcolors, Int_t &ncolors, + Int_t &maxcolors) { - if (fMarkerStyle == markerstyle) return; - static RXPoint shape[30]; - fMarkerStyle = TMath::Abs(markerstyle); - markerstyle = TAttMarker::GetMarkerStyleBase(fMarkerStyle); - gMarkerLineWidth = TAttMarker::GetMarkerLineWidth(fMarkerStyle); + if (maxcolors == 0) { + ncolors = 0; + maxcolors = 100; + orgcolors = (ULong_t*) ::operator new(maxcolors*sizeof(ULong_t)); + } - // The fast pixel markers need to be treated separately - if (markerstyle == 1 || markerstyle == 6 || markerstyle == 7) { - XSetLineAttributes((Display*)fDisplay, *gGCmark, 0, LineSolid, CapButt, JoinMiter); - } else { - XSetLineAttributes((Display*)fDisplay, *gGCmark, gMarkerLineWidth, - gMarkerLineStyle, gMarkerCapStyle, gMarkerJoinStyle); + for (int i = 0; i < ncolors; i++) + if (pixel == orgcolors[i]) return; + + if (ncolors >= maxcolors) { + orgcolors = (ULong_t*) TStorage::ReAlloc(orgcolors, + maxcolors*2*sizeof(ULong_t), maxcolors*sizeof(ULong_t)); + maxcolors *= 2; } - Float_t MarkerSizeReduced = fMarkerSize - TMath::Floor(gMarkerLineWidth/2.)/4.; - Int_t im = Int_t(4*MarkerSizeReduced + 0.5); - if (markerstyle == 2) { - // + shaped marker - shape[0].x = -im; shape[0].y = 0; - shape[1].x = im; shape[1].y = 0; - shape[2].x = 0 ; shape[2].y = -im; - shape[3].x = 0 ; shape[3].y = im; - SetMarkerType(4,4,shape); - } else if (markerstyle == 3 || markerstyle == 31) { - // * shaped marker - shape[0].x = -im; shape[0].y = 0; - shape[1].x = im; shape[1].y = 0; - shape[2].x = 0 ; shape[2].y = -im; - shape[3].x = 0 ; shape[3].y = im; - im = Int_t(0.707*Float_t(im) + 0.5); - shape[4].x = -im; shape[4].y = -im; - shape[5].x = im; shape[5].y = im; - shape[6].x = -im; shape[6].y = im; - shape[7].x = im; shape[7].y = -im; - SetMarkerType(4,8,shape); - } else if (markerstyle == 4 || markerstyle == 24) { - // O shaped marker - SetMarkerType(0,im*2,shape); - } else if (markerstyle == 5) { - // X shaped marker - im = Int_t(0.707*Float_t(im) + 0.5); - shape[0].x = -im; shape[0].y = -im; - shape[1].x = im; shape[1].y = im; - shape[2].x = -im; shape[2].y = im; - shape[3].x = im; shape[3].y = -im; - SetMarkerType(4,4,shape); - } else if (markerstyle == 6) { - // + shaped marker (with 1 pixel) - shape[0].x = -1 ; shape[0].y = 0; - shape[1].x = 1 ; shape[1].y = 0; - shape[2].x = 0 ; shape[2].y = -1; - shape[3].x = 0 ; shape[3].y = 1; - SetMarkerType(4,4,shape); - } else if (markerstyle == 7) { - // . shaped marker (with 9 pixel) - shape[0].x = -1 ; shape[0].y = 1; - shape[1].x = 1 ; shape[1].y = 1; - shape[2].x = -1 ; shape[2].y = 0; - shape[3].x = 1 ; shape[3].y = 0; - shape[4].x = -1 ; shape[4].y = -1; - shape[5].x = 1 ; shape[5].y = -1; - SetMarkerType(4,6,shape); - } else if (markerstyle == 8 || markerstyle == 20) { - // O shaped marker (filled) - SetMarkerType(1,im*2,shape); - } else if (markerstyle == 21) { - // full square - shape[0].x = -im; shape[0].y = -im; - shape[1].x = im; shape[1].y = -im; - shape[2].x = im; shape[2].y = im; - shape[3].x = -im; shape[3].y = im; - shape[4].x = -im; shape[4].y = -im; - SetMarkerType(3,5,shape); - } else if (markerstyle == 22) { - // full triangle up - shape[0].x = -im; shape[0].y = im; - shape[1].x = im; shape[1].y = im; - shape[2].x = 0; shape[2].y = -im; - shape[3].x = -im; shape[3].y = im; - SetMarkerType(3,4,shape); - } else if (markerstyle == 23) { - // full triangle down - shape[0].x = 0; shape[0].y = im; - shape[1].x = im; shape[1].y = -im; - shape[2].x = -im; shape[2].y = -im; - shape[3].x = 0; shape[3].y = im; - SetMarkerType(3,4,shape); - } else if (markerstyle == 25) { - // open square - shape[0].x = -im; shape[0].y = -im; - shape[1].x = im; shape[1].y = -im; - shape[2].x = im; shape[2].y = im; - shape[3].x = -im; shape[3].y = im; - shape[4].x = -im; shape[4].y = -im; - SetMarkerType(2,5,shape); - } else if (markerstyle == 26) { - // open triangle up - shape[0].x = -im; shape[0].y = im; - shape[1].x = im; shape[1].y = im; - shape[2].x = 0; shape[2].y = -im; - shape[3].x = -im; shape[3].y = im; - SetMarkerType(2,4,shape); - } else if (markerstyle == 27) { - // open losange - Int_t imx = Int_t(2.66*MarkerSizeReduced + 0.5); - shape[0].x =-imx; shape[0].y = 0; - shape[1].x = 0; shape[1].y = -im; - shape[2].x = imx; shape[2].y = 0; - shape[3].x = 0; shape[3].y = im; - shape[4].x =-imx; shape[4].y = 0; - SetMarkerType(2,5,shape); - } else if (markerstyle == 28) { - // open cross - Int_t imx = Int_t(1.33*MarkerSizeReduced + 0.5); - shape[0].x = -im; shape[0].y =-imx; - shape[1].x =-imx; shape[1].y =-imx; - shape[2].x =-imx; shape[2].y = -im; - shape[3].x = imx; shape[3].y = -im; - shape[4].x = imx; shape[4].y =-imx; - shape[5].x = im; shape[5].y =-imx; - shape[6].x = im; shape[6].y = imx; - shape[7].x = imx; shape[7].y = imx; - shape[8].x = imx; shape[8].y = im; - shape[9].x =-imx; shape[9].y = im; - shape[10].x=-imx; shape[10].y= imx; - shape[11].x= -im; shape[11].y= imx; - shape[12].x= -im; shape[12].y=-imx; - SetMarkerType(2,13,shape); - } else if (markerstyle == 29) { - // full star pentagone - Int_t im1 = Int_t(0.66*MarkerSizeReduced + 0.5); - Int_t im2 = Int_t(2.00*MarkerSizeReduced + 0.5); - Int_t im3 = Int_t(2.66*MarkerSizeReduced + 0.5); - Int_t im4 = Int_t(1.33*MarkerSizeReduced + 0.5); - shape[0].x = -im; shape[0].y = im4; - shape[1].x =-im2; shape[1].y =-im1; - shape[2].x =-im3; shape[2].y = -im; - shape[3].x = 0; shape[3].y =-im2; - shape[4].x = im3; shape[4].y = -im; - shape[5].x = im2; shape[5].y =-im1; - shape[6].x = im; shape[6].y = im4; - shape[7].x = im4; shape[7].y = im4; - shape[8].x = 0; shape[8].y = im; - shape[9].x =-im4; shape[9].y = im4; - shape[10].x= -im; shape[10].y= im4; - SetMarkerType(3,11,shape); - } else if (markerstyle == 30) { - // open star pentagone - Int_t im1 = Int_t(0.66*MarkerSizeReduced + 0.5); - Int_t im2 = Int_t(2.00*MarkerSizeReduced + 0.5); - Int_t im3 = Int_t(2.66*MarkerSizeReduced + 0.5); - Int_t im4 = Int_t(1.33*MarkerSizeReduced + 0.5); - shape[0].x = -im; shape[0].y = im4; - shape[1].x =-im2; shape[1].y =-im1; - shape[2].x =-im3; shape[2].y = -im; - shape[3].x = 0; shape[3].y =-im2; - shape[4].x = im3; shape[4].y = -im; - shape[5].x = im2; shape[5].y =-im1; - shape[6].x = im; shape[6].y = im4; - shape[7].x = im4; shape[7].y = im4; - shape[8].x = 0; shape[8].y = im; - shape[9].x =-im4; shape[9].y = im4; - shape[10].x= -im; shape[10].y= im4; - SetMarkerType(2,11,shape); - } else if (markerstyle == 32) { - // open triangle down - shape[0].x = 0; shape[0].y = im; - shape[1].x = im; shape[1].y = -im; - shape[2].x = -im; shape[2].y = -im; - shape[3].x = 0; shape[3].y = im; - SetMarkerType(2,4,shape); - } else if (markerstyle == 33) { - // full losange - Int_t imx = Int_t(2.66*MarkerSizeReduced + 0.5); - shape[0].x =-imx; shape[0].y = 0; - shape[1].x = 0; shape[1].y = -im; - shape[2].x = imx; shape[2].y = 0; - shape[3].x = 0; shape[3].y = im; - shape[4].x =-imx; shape[4].y = 0; - SetMarkerType(3,5,shape); - } else if (markerstyle == 34) { - // full cross - Int_t imx = Int_t(1.33*MarkerSizeReduced + 0.5); - shape[0].x = -im; shape[0].y =-imx; - shape[1].x =-imx; shape[1].y =-imx; - shape[2].x =-imx; shape[2].y = -im; - shape[3].x = imx; shape[3].y = -im; - shape[4].x = imx; shape[4].y =-imx; - shape[5].x = im; shape[5].y =-imx; - shape[6].x = im; shape[6].y = imx; - shape[7].x = imx; shape[7].y = imx; - shape[8].x = imx; shape[8].y = im; - shape[9].x =-imx; shape[9].y = im; - shape[10].x=-imx; shape[10].y= imx; - shape[11].x= -im; shape[11].y= imx; - shape[12].x= -im; shape[12].y=-imx; - SetMarkerType(3,13,shape); - } else if (markerstyle == 35) { - // diamond with cross - shape[0].x =-im; shape[0].y = 0; - shape[1].x = 0; shape[1].y = -im; - shape[2].x = im; shape[2].y = 0; - shape[3].x = 0; shape[3].y = im; - shape[4].x =-im; shape[4].y = 0; - shape[5].x = im; shape[5].y = 0; - shape[6].x = 0; shape[6].y = im; - shape[7].x = 0; shape[7].y =-im; - SetMarkerType(2,8,shape); - } else if (markerstyle == 36) { - // square with diagonal cross - shape[0].x = -im; shape[0].y = -im; - shape[1].x = im; shape[1].y = -im; - shape[2].x = im; shape[2].y = im; - shape[3].x = -im; shape[3].y = im; - shape[4].x = -im; shape[4].y = -im; - shape[5].x = im; shape[5].y = im; - shape[6].x = -im; shape[6].y = im; - shape[7].x = im; shape[7].y = -im; - SetMarkerType(2,8,shape); - } else if (markerstyle == 37) { - // open three triangles - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = 0; - shape[1].x =-im2; shape[1].y = im; - shape[2].x = im2; shape[2].y = im; - shape[3].x = 0; shape[3].y = 0; - shape[4].x =-im2; shape[4].y = -im; - shape[5].x = -im; shape[5].y = 0; - shape[6].x = 0; shape[6].y = 0; - shape[7].x = im; shape[7].y = 0; - shape[8].x = im2; shape[8].y = -im; - shape[9].x = 0; shape[9].y = 0; - SetMarkerType(2, 10,shape); - } else if (markerstyle == 38) { - // + shaped marker with octagon - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = -im; shape[0].y = 0; - shape[1].x = -im; shape[1].y =-im2; - shape[2].x =-im2; shape[2].y = -im; - shape[3].x = im2; shape[3].y = -im; - shape[4].x = im; shape[4].y =-im2; - shape[5].x = im; shape[5].y = im2; - shape[6].x = im2; shape[6].y = im; - shape[7].x =-im2; shape[7].y = im; - shape[8].x = -im; shape[8].y = im2; - shape[9].x = -im; shape[9].y = 0; - shape[10].x = im; shape[10].y = 0; - shape[11].x = 0; shape[11].y = 0; - shape[12].x = 0; shape[12].y = -im; - shape[13].x = 0; shape[13].y = im; - shape[14].x = 0; shape[14].y = 0; - SetMarkerType(2,15,shape); - } else if (markerstyle == 39) { - // filled three triangles - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = 0; - shape[1].x =-im2; shape[1].y = im; - shape[2].x = im2; shape[2].y = im; - shape[3].x = 0; shape[3].y = 0; - shape[4].x =-im2; shape[4].y = -im; - shape[5].x = -im; shape[5].y = 0; - shape[6].x = 0; shape[6].y = 0; - shape[7].x = im; shape[7].y = 0; - shape[8].x = im2; shape[8].y = -im; - SetMarkerType(3,9,shape); - } else if (markerstyle == 40) { - // four open triangles X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = 0; - shape[1].x = im2; shape[1].y = im; - shape[2].x = im; shape[2].y = im2; - shape[3].x = 0; shape[3].y = 0; - shape[4].x = im; shape[4].y = -im2; - shape[5].x = im2; shape[5].y = -im; - shape[6].x = 0; shape[6].y = 0; - shape[7].x = -im2; shape[7].y = -im; - shape[8].x = -im; shape[8].y = -im2; - shape[9].x = 0; shape[9].y = 0; - shape[10].x = -im; shape[10].y = im2; - shape[11].x = -im2; shape[11].y = im; - shape[12].x = 0; shape[12].y = 0; - SetMarkerType(2,13,shape); - } else if (markerstyle == 41) { - // four filled triangles X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = 0; - shape[1].x = im2; shape[1].y = im; - shape[2].x = im; shape[2].y = im2; - shape[3].x = 0; shape[3].y = 0; - shape[4].x = im; shape[4].y = -im2; - shape[5].x = im2; shape[5].y = -im; - shape[6].x = 0; shape[6].y = 0; - shape[7].x = -im2; shape[7].y = -im; - shape[8].x = -im; shape[8].y = -im2; - shape[9].x = 0; shape[9].y = 0; - shape[10].x = -im; shape[10].y = im2; - shape[11].x = -im2; shape[11].y = im; - shape[12].x = 0; shape[12].y = 0; - SetMarkerType(3,13,shape); - } else if (markerstyle == 42) { - // open double diamonds - Int_t imx = Int_t(MarkerSizeReduced + 0.5); - shape[0].x= 0; shape[0].y= im; - shape[1].x= -imx; shape[1].y= imx; - shape[2].x = -im; shape[2].y = 0; - shape[3].x = -imx; shape[3].y = -imx; - shape[4].x = 0; shape[4].y = -im; - shape[5].x = imx; shape[5].y = -imx; - shape[6].x = im; shape[6].y = 0; - shape[7].x= imx; shape[7].y= imx; - shape[8].x= 0; shape[8].y= im; - SetMarkerType(2,9,shape); - } else if (markerstyle == 43) { - // filled double diamonds - Int_t imx = Int_t(MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = im; - shape[1].x = -imx; shape[1].y = imx; - shape[2].x = -im; shape[2].y = 0; - shape[3].x = -imx; shape[3].y = -imx; - shape[4].x = 0; shape[4].y = -im; - shape[5].x = imx; shape[5].y = -imx; - shape[6].x = im; shape[6].y = 0; - shape[7].x = imx; shape[7].y = imx; - shape[8].x = 0; shape[8].y = im; - SetMarkerType(3,9,shape); - } else if (markerstyle == 44) { - // open four triangles plus - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = 0; - shape[1].x = im2; shape[1].y = im; - shape[2].x = -im2; shape[2].y = im; - shape[3].x = im2; shape[3].y = -im; - shape[4].x = -im2; shape[4].y = -im; - shape[5].x = 0; shape[5].y = 0; - shape[6].x = im; shape[6].y = im2; - shape[7].x = im; shape[7].y = -im2; - shape[8].x = -im; shape[8].y = im2; - shape[9].x = -im; shape[9].y = -im2; - shape[10].x = 0; shape[10].y = 0; - SetMarkerType(2,11,shape); - } else if (markerstyle == 45) { - // filled four triangles plus - Int_t im0 = Int_t(0.4*MarkerSizeReduced + 0.5); - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = im0; shape[0].y = im0; - shape[1].x = im2; shape[1].y = im; - shape[2].x = -im2; shape[2].y = im; - shape[3].x = -im0; shape[3].y = im0; - shape[4].x = -im; shape[4].y = im2; - shape[5].x = -im; shape[5].y = -im2; - shape[6].x = -im0; shape[6].y = -im0; - shape[7].x = -im2; shape[7].y = -im; - shape[8].x = im2; shape[8].y = -im; - shape[9].x = im0; shape[9].y = -im0; - shape[10].x = im; shape[10].y = -im2; - shape[11].x = im; shape[11].y = im2; - shape[12].x = im0; shape[12].y = im0; - SetMarkerType(3,13,shape); - } else if (markerstyle == 46) { - // open four triangles X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = im2; - shape[1].x = -im2; shape[1].y = im; - shape[2].x = -im; shape[2].y = im2; - shape[3].x = -im2; shape[3].y = 0; - shape[4].x = -im; shape[4].y = -im2; - shape[5].x = -im2; shape[5].y = -im; - shape[6].x = 0; shape[6].y = -im2; - shape[7].x = im2; shape[7].y = -im; - shape[8].x = im; shape[8].y = -im2; - shape[9].x = im2; shape[9].y = 0; - shape[10].x = im; shape[10].y = im2; - shape[11].x = im2; shape[11].y = im; - shape[12].x = 0; shape[12].y = im2; - SetMarkerType(2,13,shape); - } else if (markerstyle == 47) { - // filled four triangles X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = im2; - shape[1].x = -im2; shape[1].y = im; - shape[2].x = -im; shape[2].y = im2; - shape[3].x = -im2; shape[3].y = 0; - shape[4].x = -im; shape[4].y = -im2; - shape[5].x = -im2; shape[5].y = -im; - shape[6].x = 0; shape[6].y = -im2; - shape[7].x = im2; shape[7].y = -im; - shape[8].x = im; shape[8].y = -im2; - shape[9].x = im2; shape[9].y = 0; - shape[10].x = im; shape[10].y = im2; - shape[11].x = im2; shape[11].y = im; - shape[12].x = 0; shape[12].y = im2; - SetMarkerType(3,13,shape); - } else if (markerstyle == 48) { - // four filled squares X - Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); - shape[0].x = 0; shape[0].y = im2*1.005; - shape[1].x = -im2; shape[1].y = im; - shape[2].x = -im; shape[2].y = im2; - shape[3].x = -im2; shape[3].y = 0; - shape[4].x = -im; shape[4].y = -im2; - shape[5].x = -im2; shape[5].y = -im; - shape[6].x = 0; shape[6].y = -im2; - shape[7].x = im2; shape[7].y = -im; - shape[8].x = im; shape[8].y = -im2; - shape[9].x = im2; shape[9].y = 0; - shape[10].x = im; shape[10].y = im2; - shape[11].x = im2; shape[11].y = im; - shape[12].x = 0; shape[12].y = im2*0.995; - shape[13].x = im2*0.995; shape[13].y = 0; - shape[14].x = 0; shape[14].y = -im2*0.995; - shape[15].x = -im2*0.995; shape[15].y = 0; - shape[16].x = 0; shape[16].y = im2*0.995; - SetMarkerType(3,16,shape); - } else if (markerstyle == 49) { - // four filled squares plus - Int_t imx = Int_t(1.33*MarkerSizeReduced + 0.5); - shape[0].x =-imx; shape[0].y =-imx*1.005; - shape[1].x =-imx; shape[1].y = -im; - shape[2].x = imx; shape[2].y = -im; - shape[3].x = imx; shape[3].y =-imx; - shape[4].x = im; shape[4].y =-imx; - shape[5].x = im; shape[5].y = imx; - shape[6].x = imx; shape[6].y = imx; - shape[7].x = imx; shape[7].y = im; - shape[8].x =-imx; shape[8].y = im; - shape[9].x =-imx; shape[9].y = imx; - shape[10].x = -im; shape[10].y = imx; - shape[11].x = -im; shape[11].y =-imx; - shape[12].x =-imx; shape[12].y =-imx*0.995; - shape[13].x =-imx; shape[13].y = imx; - shape[14].x = imx; shape[14].y = imx; - shape[15].x = imx; shape[15].y =-imx; - shape[16].x =-imx; shape[16].y =-imx*1.005; - SetMarkerType(3,17,shape); - } else { - // single dot - SetMarkerType(0,0,shape); + orgcolors[ncolors++] = pixel; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get RGB values for orgcolors, add percent neutral to the RGB and +/// allocate fNewColors. + +void TGX11::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors) +{ + if (ncolors == 0) return; + + RXColor *xcol = new RXColor[ncolors]; + + int i; + for (i = 0; i < ncolors; i++) { + xcol[i].pixel = orgcolors[i]; + xcol[i].red = xcol[i].green = xcol[i].blue = 0; + xcol[i].flags = DoRed | DoGreen | DoBlue; + } + QueryColors(fColormap, xcol, ncolors); + + UShort_t add = percent * kBIGGEST_RGB_VALUE / 100; + + Int_t val; + for (i = 0; i < ncolors; i++) { + val = xcol[i].red + add; + if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; + xcol[i].red = (UShort_t) val; + val = xcol[i].green + add; + if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; + xcol[i].green = (UShort_t) val; + val = xcol[i].blue + add; + if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; + xcol[i].blue = (UShort_t) val; + if (!AllocColor(fColormap, &xcol[i])) + Warning("MakeOpaqueColors", "failed to allocate color %hd, %hd, %hd", + xcol[i].red, xcol[i].green, xcol[i].blue); + // assumes that in case of failure xcol[i].pixel is not changed + } + + gCws->fNewColors = new ULong_t[ncolors]; + gCws->fNcolors = ncolors; + + for (i = 0; i < ncolors; i++) + gCws->fNewColors[i] = xcol[i].pixel; + + delete [] xcol; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns index in orgcolors (and fNewColors) for pixel. + +Int_t TGX11::FindColor(ULong_t pixel, ULong_t *orgcolors, Int_t ncolors) +{ + for (int i = 0; i < ncolors; i++) + if (pixel == orgcolors[i]) return i; + + Error("FindColor", "did not find color, should never happen!"); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set color intensities for given color index. +/// +/// \param [in] cindex : color index +/// \param [in] r,g,b : red, green, blue intensities between 0.0 and 1.0 + +void TGX11::SetRGB(int cindex, float r, float g, float b) +{ + if (fColormap) { + RXColor xcol; + xcol.red = (UShort_t)(r * kBIGGEST_RGB_VALUE); + xcol.green = (UShort_t)(g * kBIGGEST_RGB_VALUE); + xcol.blue = (UShort_t)(b * kBIGGEST_RGB_VALUE); + xcol.flags = DoRed | DoGreen | DoBlue; + XColor_t &col = GetColor(cindex); + if (col.fDefined) { + // if color is already defined with same rgb just return + if (col.fRed == xcol.red && col.fGreen == xcol.green && + col.fBlue == xcol.blue) + return; + col.fDefined = kFALSE; + if (fRedDiv == -1) + XFreeColors((Display*)fDisplay, fColormap, &col.fPixel, 1, 0); + } + if (AllocColor(fColormap, &xcol)) { + col.fDefined = kTRUE; + col.fPixel = xcol.pixel; + col.fRed = xcol.red; + col.fGreen = xcol.green; + col.fBlue = xcol.blue; + } } } //////////////////////////////////////////////////////////////////////////////// -/// Set opacity of a window. This image manipulation routine works -/// by adding to a percent amount of neutral to each pixels RGB. -/// Since it requires quite some additional color map entries is it -/// only supported on displays with more than > 8 color planes (> 256 -/// colors). +/// Set text alignment. +/// +/// \param [in] talign text alignment -void TGX11::SetOpacity(Int_t percent) +void TGX11::SetTextAlign(Short_t talign) { - if (fDepth <= 8) return; - if (percent == 0) return; - // if 100 percent then just make white + TAttText::SetTextAlign(talign); - ULong_t *orgcolors = nullptr, *tmpc = nullptr; - Int_t maxcolors = 0, ncolors = 0, ntmpc = 0; + TAttText arg(gCws->fAttText); + arg.SetTextAlign(talign); - // save previous allocated colors, delete at end when not used anymore - if (gCws->fNewColors) { - tmpc = gCws->fNewColors; - ntmpc = gCws->fNcolors; + SetAttText((WinContext_t) gCws, arg); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set color index for text. + +void TGX11::SetTextColor(Color_t cindex) +{ + if (cindex < 0) return; + + TAttText::SetTextColor(cindex); + + TAttText arg(gCws->fAttText); + arg.SetTextColor(cindex); + + SetAttText((WinContext_t) gCws, arg); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set text font to specified name. +/// +/// \param [in] fontname font name +/// \param [in] mode loading flag +/// - mode=0 search if the font exist (kCheck) +/// - mode=1 search the font and load it if it exists (kLoad) +/// +/// Set text font to specified name. This function returns 0 if +/// the specified font is found, 1 if not. + +Int_t TGX11::SetTextFont(char *fontname, ETextSetMode mode) +{ + char **fontlist; + int fontcount; + + if (mode == kLoad) { + for (int i = 0; i < kMAXFONT; i++) { + if (strcmp(fontname, gFont[i].name) == 0) { + gTextFont = gFont[i].id; + if (gCws) { + gCws->textFont = gTextFont; + XSetFont((Display*)fDisplay, gCws->fGClist[kGCtext], gCws->textFont->fid); + XSetFont((Display*)fDisplay, gCws->fGClist[kGCinvt], gCws->textFont->fid); + } + return 0; + } + } } - // get pixmap from server as image - XImage *image = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, gCws->fWidth, - gCws->fHeight, AllPlanes, ZPixmap); - if (!image) return; - // collect different image colors - int x, y; - for (y = 0; y < (int) gCws->fHeight; y++) { - for (x = 0; x < (int) gCws->fWidth; x++) { - ULong_t pixel = XGetPixel(image, x, y); - CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + fontlist = XListFonts((Display*)fDisplay, fontname, 1, &fontcount); + + if (fontlist && fontcount != 0) { + if (mode == kLoad) { + if (gFont[gCurrentFontNumber].id) + XFreeFont((Display*)fDisplay, gFont[gCurrentFontNumber].id); + gTextFont = XLoadQueryFont((Display*)fDisplay, fontlist[0]); + gFont[gCurrentFontNumber].id = gTextFont; + strlcpy(gFont[gCurrentFontNumber].name,fontname,80); + gCurrentFontNumber++; + if (gCurrentFontNumber == kMAXFONT) gCurrentFontNumber = 0; } + XFreeFontNames(fontlist); + return 0; + } else { + return 1; } - if (ncolors == 0) { - XDestroyImage(image); - ::operator delete(orgcolors); - return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set current text font number. + +void TGX11::SetTextFont(Font_t fontnumber) +{ + TAttText::SetTextFont(fontnumber); + + TAttText arg(gCws->fAttText); + arg.SetTextFont(fontnumber); + + SetAttText((WinContext_t) gCws, arg); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set current text size. + +void TGX11::SetTextSize(Float_t textsize) +{ + TAttText::SetTextSize(textsize); + + TAttText arg(gCws->fAttText); + arg.SetTextSize(textsize); + + SetAttText((WinContext_t) gCws, arg); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set synchronisation on or off. +/// +/// \param [in] mode : synchronisation on/off +/// - mode=1 on +/// - mode<>0 off + +void TGX11::Sync(int mode) +{ + switch (mode) { + + case 1 : + XSynchronize((Display*)fDisplay,1); + break; + + default: + XSynchronize((Display*)fDisplay,0); + break; } +} - // create opaque counter parts - MakeOpaqueColors(percent, orgcolors, ncolors); +//////////////////////////////////////////////////////////////////////////////// +/// Update display. +/// +/// \param [in] mode : (1) update (0) sync +/// +/// Synchronise client and server once (not permanent). +/// Copy the pixmap gCws->fDrawing on the window gCws->fWindow +/// if the double buffer is on. - if (gCws->fNewColors) { - // put opaque colors in image - for (y = 0; y < (int) gCws->fHeight; y++) { - for (x = 0; x < (int) gCws->fWidth; x++) { - ULong_t pixel = XGetPixel(image, x, y); - Int_t idx = FindColor(pixel, orgcolors, ncolors); - XPutPixel(image, x, y, gCws->fNewColors[idx]); - } - } +void TGX11::UpdateWindow(int mode) +{ + UpdateWindowW((WinContext_t) gCws, mode); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Update display for specified window. +/// +/// \param [in] mode : (1) update (0) sync +/// +/// Synchronise client and server once (not permanent). +/// Copy the pixmap gCws->fDrawing on the window gCws->fWindow +/// if the double buffer is on. + +void TGX11::UpdateWindowW(WinContext_t wctxt, Int_t mode) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; + + if (ctxt->fDoubleBuffer) { + XCopyArea((Display*)fDisplay, ctxt->fDrawing, ctxt->fWindow, + ctxt->fGClist[kGCpxmp], 0, 0, ctxt->fWidth, ctxt->fHeight, 0, 0); + } + if (mode == 1) { + XFlush((Display*)fDisplay); + } else { + XSync((Display*)fDisplay, False); } +} - // put image back in pixmap on server - XPutImage((Display*)fDisplay, gCws->fDrawing, *gGCpxmp, image, 0, 0, 0, 0, - gCws->fWidth, gCws->fHeight); - XFlush((Display*)fDisplay); +//////////////////////////////////////////////////////////////////////////////// +/// Set pointer position. +/// +/// \param [in] ix New X coordinate of pointer +/// \param [in] iy New Y coordinate of pointer +/// \param [in] id Window identifier +/// +/// Coordinates are relative to the origin of the window id +/// or to the origin of the current window if id == 0. - // clean up - if (tmpc) { - if (fRedDiv == -1) - XFreeColors((Display*)fDisplay, fColormap, tmpc, ntmpc, 0); - delete [] tmpc; +void TGX11::Warp(Int_t ix, Int_t iy, Window_t id) +{ + if (!id) { + // Causes problems when calling ProcessEvents()... BadWindow + //XWarpPointer((Display*)fDisplay, None, gCws->fWindow, 0, 0, 0, 0, ix, iy); + } else { + XWarpPointer((Display*)fDisplay, None, (Window) id, 0, 0, 0, 0, ix, iy); } - XDestroyImage(image); - ::operator delete(orgcolors); } //////////////////////////////////////////////////////////////////////////////// -/// Collect in orgcolors all different original image colors. +/// Write the pixmap wid in the bitmap file pxname. +/// +/// \param [in] wid : Pixmap address +/// \param [in] w,h : Width and height of the pixmap. +/// \param [in] pxname : pixmap name + +void TGX11::WritePixmap(int wid, unsigned int w, unsigned int h, char *pxname) +{ + unsigned int wval, hval; + wval = w; + hval = h; + + gTws = fWindows[wid].get(); + XWriteBitmapFile((Display*)fDisplay, pxname, gTws->fDrawing, wval, hval, -1, -1); +} + + +// +// Functions for GIFencode() +// + +static FILE *gOut; // output unit used WriteGIF and PutByte +static XImage *gXimage = nullptr; // image used in WriteGIF and GetPixel + +extern "C" { + int GIFquantize(UInt_t width, UInt_t height, Int_t *ncol, Byte_t *red, Byte_t *green, + Byte_t *blue, Byte_t *outputBuf, Byte_t *outputCmap); + long GIFencode(int Width, int Height, Int_t Ncol, Byte_t R[], Byte_t G[], Byte_t B[], Byte_t ScLine[], + void (*get_scline) (int, int, Byte_t *), void (*pb)(Byte_t)); + int GIFdecode(Byte_t *gifArr, Byte_t *pixArr, int *Width, int *Height, int *Ncols, Byte_t *R, Byte_t *G, Byte_t *B); + int GIFinfo(Byte_t *gifArr, int *Width, int *Height, int *Ncols); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get pixels in line y and put in array scline. -void TGX11::CollectImageColors(ULong_t pixel, ULong_t *&orgcolors, Int_t &ncolors, - Int_t &maxcolors) +static void GetPixel(int y, int width, Byte_t *scline) { - if (maxcolors == 0) { - ncolors = 0; - maxcolors = 100; - orgcolors = (ULong_t*) ::operator new(maxcolors*sizeof(ULong_t)); - } - - for (int i = 0; i < ncolors; i++) - if (pixel == orgcolors[i]) return; + for (int i = 0; i < width; i++) + scline[i] = Byte_t(XGetPixel(gXimage, i, y)); +} - if (ncolors >= maxcolors) { - orgcolors = (ULong_t*) TStorage::ReAlloc(orgcolors, - maxcolors*2*sizeof(ULong_t), maxcolors*sizeof(ULong_t)); - maxcolors *= 2; - } +//////////////////////////////////////////////////////////////////////////////// +/// Put byte b in output stream. - orgcolors[ncolors++] = pixel; +static void PutByte(Byte_t b) +{ + if (ferror(gOut) == 0) fputc(b, gOut); } //////////////////////////////////////////////////////////////////////////////// -/// Get RGB values for orgcolors, add percent neutral to the RGB and -/// allocate fNewColors. +/// Returns in R G B the ncol colors of the palette used by the image. +/// The image pixels are changed to index values in these R G B arrays. +/// This produces a colormap with only the used colors (so even on displays +/// with more than 8 planes we will be able to create GIF's when the image +/// contains no more than 256 different colors). If it does contain more +/// colors we will have to use GIFquantize to reduce the number of colors. +/// The R G B arrays must be deleted by the caller. -void TGX11::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors) +void TGX11::ImgPickPalette(RXImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B) { - if (ncolors == 0) return; + ULong_t *orgcolors = nullptr; + Int_t maxcolors = 0, ncolors = 0; + + // collect different image colors + int x, y; + for (x = 0; x < (int) gCws->fWidth; x++) { + for (y = 0; y < (int) gCws->fHeight; y++) { + ULong_t pixel = XGetPixel(image, x, y); + CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + } + } + // get RGB values belonging to pixels RXColor *xcol = new RXColor[ncolors]; int i; @@ -3014,714 +2902,1050 @@ void TGX11::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors) } QueryColors(fColormap, xcol, ncolors); - UShort_t add = percent * kBIGGEST_RGB_VALUE / 100; + // create RGB arrays and store RGB's for each color and set number of colors + // (space must be delete by caller) + R = new Int_t[ncolors]; + G = new Int_t[ncolors]; + B = new Int_t[ncolors]; - Int_t val; for (i = 0; i < ncolors; i++) { - val = xcol[i].red + add; - if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; - xcol[i].red = (UShort_t) val; - val = xcol[i].green + add; - if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; - xcol[i].green = (UShort_t) val; - val = xcol[i].blue + add; - if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; - xcol[i].blue = (UShort_t) val; - if (!AllocColor(fColormap, &xcol[i])) - Warning("MakeOpaqueColors", "failed to allocate color %hd, %hd, %hd", - xcol[i].red, xcol[i].green, xcol[i].blue); - // assumes that in case of failure xcol[i].pixel is not changed + R[i] = xcol[i].red; + G[i] = xcol[i].green; + B[i] = xcol[i].blue; } + ncol = ncolors; - gCws->fNewColors = new ULong_t[ncolors]; - gCws->fNcolors = ncolors; - - for (i = 0; i < ncolors; i++) - gCws->fNewColors[i] = xcol[i].pixel; + // update image with indices (pixels) into the new RGB colormap + for (x = 0; x < (int) gCws->fWidth; x++) { + for (y = 0; y < (int) gCws->fHeight; y++) { + ULong_t pixel = XGetPixel(image, x, y); + Int_t idx = FindColor(pixel, orgcolors, ncolors); + XPutPixel(image, x, y, idx); + } + } + // cleanup delete [] xcol; + ::operator delete(orgcolors); } //////////////////////////////////////////////////////////////////////////////// -/// Returns index in orgcolors (and fNewColors) for pixel. +/// Writes the current window into GIF file. Returns 1 in case of success, +/// 0 otherwise. -Int_t TGX11::FindColor(ULong_t pixel, ULong_t *orgcolors, Int_t ncolors) +Int_t TGX11::WriteGIF(char *name) { - for (int i = 0; i < ncolors; i++) - if (pixel == orgcolors[i]) return i; + Byte_t scline[2000], r[256], b[256], g[256]; + Int_t *red, *green, *blue; + Int_t ncol, maxcol, i; - Error("FindColor", "did not find color, should never happen!"); + if (gXimage) { + XDestroyImage(gXimage); + gXimage = nullptr; + } - return 0; -} + gXimage = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, + gCws->fWidth, gCws->fHeight, + AllPlanes, ZPixmap); -//////////////////////////////////////////////////////////////////////////////// -/// Set color intensities for given color index. -/// -/// \param [in] cindex : color index -/// \param [in] r,g,b : red, green, blue intensities between 0.0 and 1.0 + ImgPickPalette((RXImage*)gXimage, ncol, red, green, blue); -void TGX11::SetRGB(int cindex, float r, float g, float b) -{ - if (fColormap) { - RXColor xcol; - xcol.red = (UShort_t)(r * kBIGGEST_RGB_VALUE); - xcol.green = (UShort_t)(g * kBIGGEST_RGB_VALUE); - xcol.blue = (UShort_t)(b * kBIGGEST_RGB_VALUE); - xcol.flags = DoRed | DoGreen | DoBlue; - XColor_t &col = GetColor(cindex); - if (col.fDefined) { - // if color is already defined with same rgb just return - if (col.fRed == xcol.red && col.fGreen == xcol.green && - col.fBlue == xcol.blue) - return; - col.fDefined = kFALSE; - if (fRedDiv == -1) - XFreeColors((Display*)fDisplay, fColormap, &col.fPixel, 1, 0); - } - if (AllocColor(fColormap, &xcol)) { - col.fDefined = kTRUE; - col.fPixel = xcol.pixel; - col.fRed = xcol.red; - col.fGreen = xcol.green; - col.fBlue = xcol.blue; + if (ncol > 256) { + //GIFquantize(...); + Error("WriteGIF", "Cannot create GIF of image containing more than 256 colors. Try in batch mode."); + delete [] red; + delete [] green; + delete [] blue; + return 0; + } + + maxcol = 0; + for (i = 0; i < ncol; i++) { + if (maxcol < red[i] ) maxcol = red[i]; + if (maxcol < green[i] ) maxcol = green[i]; + if (maxcol < blue[i] ) maxcol = blue[i]; + r[i] = 0; + g[i] = 0; + b[i] = 0; + } + if (maxcol != 0) { + for (i = 0; i < ncol; i++) { + r[i] = red[i] * 255/maxcol; + g[i] = green[i] * 255/maxcol; + b[i] = blue[i] * 255/maxcol; } } + + gOut = fopen(name, "w+"); + + if (gOut) { + GIFencode(gCws->fWidth, gCws->fHeight, + ncol, r, g, b, scline, ::GetPixel, PutByte); + fclose(gOut); + i = 1; + } else { + Error("WriteGIF","cannot write file: %s",name); + i = 0; + } + delete [] red; + delete [] green; + delete [] blue; + return i; } //////////////////////////////////////////////////////////////////////////////// -/// Set text alignment. -/// -/// \param [in] talign text alignment +/// Draw image. +/// Not used, keep for backward compatibility -void TGX11::SetTextAlign(Short_t talign) +void TGX11::PutImage(Int_t offset,Int_t itran,Int_t x0,Int_t y0,Int_t nx,Int_t ny,Int_t xmin, + Int_t ymin,Int_t xmax,Int_t ymax, UChar_t *image,Drawable_t wid) { - Int_t txalh = talign/10; - Int_t txalv = talign%10; - fTextAlignH = txalh; - fTextAlignV = txalv; + const int maxSegment = 20; + int i, n, x, y, xcur, x1, x2, y1, y2; + unsigned char *jimg, *jbase, icol; + int nlines[256]; + XSegment lines[256][maxSegment]; + Drawable_t id; + GC lineGC; - switch (txalh) { + if (wid) { + id = wid; + lineGC = XCreateGC((Display*)fDisplay, fVisRootWin, 0, nullptr); + } else { + id = gCws->fDrawing; + lineGC = gCws->fGClist[kGCline]; + } - case 0 : - case 1 : - switch (txalv) { //left - case 1 : - fTextAlign = 7; //bottom - break; - case 2 : - fTextAlign = 4; //center - break; - case 3 : - fTextAlign = 1; //top - break; - } - break; - case 2 : - switch (txalv) { //center - case 1 : - fTextAlign = 8; //bottom - break; - case 2 : - fTextAlign = 5; //center - break; - case 3 : - fTextAlign = 2; //top - break; - } - break; - case 3 : - switch (txalv) { //right - case 1 : - fTextAlign = 9; //bottom - break; - case 2 : - fTextAlign = 6; //center - break; - case 3 : - fTextAlign = 3; //top - break; + for (i = 0; i < 256; i++) nlines[i] = 0; + + x1 = x0 + xmin; y1 = y0 + ny - ymax - 1; + x2 = x0 + xmax; y2 = y0 + ny - ymin - 1; + jbase = image + (ymin-1)*nx + xmin; + + for (y = y2; y >= y1; y--) { + xcur = x1; jbase += nx; + for (jimg = jbase, icol = *jimg++, x = x1+1; x <= x2; jimg++, x++) { + if (icol != *jimg) { + if (icol != itran) { + n = nlines[icol]++; + lines[icol][n].x1 = xcur; lines[icol][n].y1 = y; + lines[icol][n].x2 = x-1; lines[icol][n].y2 = y; + if (nlines[icol] == maxSegment) { + SetColor(wid ? nullptr : gCws, &lineGC, (int)icol+offset); + XDrawSegments((Display*)fDisplay,id,lineGC,&lines[icol][0], + maxSegment); + nlines[icol] = 0; + } + } + icol = *jimg; xcur = x; } - break; + } + if (icol != itran) { + n = nlines[icol]++; + lines[icol][n].x1 = xcur; lines[icol][n].y1 = y; + lines[icol][n].x2 = x-1; lines[icol][n].y2 = y; + if (nlines[icol] == maxSegment) { + SetColor(wid ? nullptr : gCws, &lineGC, (int)icol+offset); + XDrawSegments((Display*)fDisplay,id,lineGC,&lines[icol][0], + maxSegment); + nlines[icol] = 0; + } + } + } + + for (i = 0; i < 256; i++) { + if (nlines[i] != 0) { + SetColor(wid ? nullptr : gCws, &lineGC,i+offset); + XDrawSegments((Display*)fDisplay,id,lineGC,&lines[i][0],nlines[i]); + } } - TAttText::SetTextAlign(fTextAlign); + if (wid) + XFreeGC((Display*)fDisplay, lineGC); } //////////////////////////////////////////////////////////////////////////////// -/// Set color index for text. +/// If id is NULL - loads the specified gif file at position [x0,y0] in the +/// current window. Otherwise creates pixmap from gif file -void TGX11::SetTextColor(Color_t cindex) +Pixmap_t TGX11::ReadGIF(int x0, int y0, const char *file, Window_t id) { - if (cindex < 0) return; - - TAttText::SetTextColor(cindex); + FILE *fd; + Seek_t filesize = 0; + unsigned char *gifArr, *pixArr, red[256], green[256], blue[256], *j1, *j2, icol; + int i, j, k, width, height, ncolor, irep, offset; + float rr, gg, bb; + Pixmap_t pic = 0; - SetColor(gGCtext, Int_t(cindex)); + fd = fopen(file, "r"); + if (!fd) { + Error("ReadGIF", "unable to open GIF file"); + return pic; + } - XGCValues values; - if (XGetGCValues((Display*)fDisplay, *gGCtext, GCForeground | GCBackground, &values)) { - XSetForeground( (Display*)fDisplay, *gGCinvt, values.background ); - XSetBackground( (Display*)fDisplay, *gGCinvt, values.foreground ); + fseek(fd, 0L, 2); + long ft = ftell(fd); + if (ft <=0) { + Error("ReadGIF", "unable to open GIF file"); + fclose(fd); + return pic; } else { - Error("SetTextColor", "cannot get GC values"); + filesize = Seek_t(ft); } - XSetBackground((Display*)fDisplay, *gGCtext, GetColor(0).fPixel); -} + fseek(fd, 0L, 0); -//////////////////////////////////////////////////////////////////////////////// -/// Set text font to specified name. -/// -/// \param [in] fontname font name -/// \param [in] mode loading flag -/// - mode=0 search if the font exist (kCheck) -/// - mode=1 search the font and load it if it exists (kLoad) -/// -/// Set text font to specified name. This function returns 0 if -/// the specified font is found, 1 if not. + if (!(gifArr = (unsigned char *) calloc(filesize+256,1))) { + Error("ReadGIF", "unable to allocate array for gif"); + fclose(fd); + return pic; + } -Int_t TGX11::SetTextFont(char *fontname, ETextSetMode mode) -{ - char **fontlist; - int fontcount; - int i; + if (fread(gifArr, filesize, 1, fd) != 1) { + Error("ReadGIF", "GIF file read failed"); + free(gifArr); + fclose(fd); + return pic; + } + fclose(fd); - if (mode == kLoad) { - for (i = 0; i < kMAXFONT; i++) { - if (strcmp(fontname, gFont[i].name) == 0) { - gTextFont = gFont[i].id; - XSetFont((Display*)fDisplay, *gGCtext, gTextFont->fid); - XSetFont((Display*)fDisplay, *gGCinvt, gTextFont->fid); - return 0; - } - } + irep = GIFinfo(gifArr, &width, &height, &ncolor); + if (irep != 0) { + free(gifArr); + return pic; } - fontlist = XListFonts((Display*)fDisplay, fontname, 1, &fontcount); + if (!(pixArr = (unsigned char *) calloc((width*height),1))) { + Error("ReadGIF", "unable to allocate array for image"); + free(gifArr); + return pic; + } - if (fontlist && fontcount != 0) { - if (mode == kLoad) { - if (gFont[gCurrentFontNumber].id) - XFreeFont((Display*)fDisplay, gFont[gCurrentFontNumber].id); - gTextFont = XLoadQueryFont((Display*)fDisplay, fontlist[0]); - XSetFont((Display*)fDisplay, *gGCtext, gTextFont->fid); - XSetFont((Display*)fDisplay, *gGCinvt, gTextFont->fid); - gFont[gCurrentFontNumber].id = gTextFont; - strlcpy(gFont[gCurrentFontNumber].name,fontname,80); - gCurrentFontNumber++; - if (gCurrentFontNumber == kMAXFONT) gCurrentFontNumber = 0; + irep = GIFdecode(gifArr, pixArr, &width, &height, &ncolor, red, green, blue); + if (irep != 0) { + free(gifArr); + free(pixArr); + return pic; + } + + // S E T P A L E T T E + + offset = 8; + + for (i = 0; i < ncolor; i++) { + rr = red[i]/255.; + gg = green[i]/255.; + bb = blue[i]/255.; + j = i+offset; + SetRGB(j,rr,gg,bb); + } + + // O U T P U T I M A G E + + for (i = 1; i <= height/2; i++) { + j1 = pixArr + (i-1)*width; + j2 = pixArr + (height-i)*width; + for (k = 0; k < width; k++) { + icol = *j1; *j1++ = *j2; *j2++ = icol; } - XFreeFontNames(fontlist); - return 0; - } else { - return 1; } + if (id) pic = CreatePixmap(id, width, height); + PutImage(offset,-1,x0,y0,width,height,0,0,width-1,height-1,pixArr,pic); + + free(gifArr); + free(pixArr); + + if (pic) + return pic; + else if (gCws->fDrawing) + return (Pixmap_t)gCws->fDrawing; + return 0; } //////////////////////////////////////////////////////////////////////////////// -/// Set current text font number. +/// Returns an array of pixels created from a part of drawable +/// (defined by x, y, w, h) in format: +/// `b1, g1, r1, 0, b2, g2, r2, 0, ..., bn, gn, rn, 0`. +/// +/// Pixels are numbered from left to right and from top to bottom. +/// By default all pixels from the whole drawable are returned. +/// +/// Note that return array is 32-bit aligned -void TGX11::SetTextFont(Font_t fontnumber) +unsigned char *TGX11::GetColorBits(Drawable_t /*wid*/, Int_t /*x*/, Int_t /*y*/, + UInt_t /*w*/, UInt_t /*h*/) { - fTextFont = fontnumber; + return nullptr; } //////////////////////////////////////////////////////////////////////////////// -/// Set current text size. +/// create pixmap from RGB data. RGB data is in format : +/// b1, g1, r1, 0, b2, g2, r2, 0 ... bn, gn, rn, 0 .. +/// +/// Pixels are numbered from left to right and from top to bottom. +/// Note that data must be 32-bit aligned -void TGX11::SetTextSize(Float_t textsize) +Pixmap_t TGX11::CreatePixmapFromData(unsigned char * /*bits*/, UInt_t /*width*/, + UInt_t /*height*/) { - fTextSize = textsize; + return (Pixmap_t)0; } //////////////////////////////////////////////////////////////////////////////// -/// Set synchronisation on or off. +/// Register pixmap created by gVirtualGL /// -/// \param [in] mode : synchronisation on/off -/// - mode=1 on -/// - mode<>0 off +/// \param [in] pixid Pixmap identifier +/// \param [in] w,h Width and height of the pixmap +/// +/// register new pixmap -void TGX11::Sync(int mode) +Int_t TGX11::AddPixmap(ULong_t pixid, UInt_t w, UInt_t h) { - switch (mode) { + Int_t wid = AddWindowHandle(); - case 1 : - XSynchronize((Display*)fDisplay,1); - break; + gCws = fWindows[wid].get(); + gCws->fWindow = pixid; + gCws->fDrawing = gCws->fWindow; + gCws->fBuffer = 0; + gCws->fDoubleBuffer = 0; + gCws->fIsPixmap = 1; + gCws->fClip = 0; + gCws->fWidth = w; + gCws->fHeight = h; + gCws->fNewColors = nullptr; + gCws->fShared = kFALSE; - default: - XSynchronize((Display*)fDisplay,0); - break; - } + return wid; } //////////////////////////////////////////////////////////////////////////////// -/// Update display. -/// -/// \param [in] mode : (1) update (0) sync -/// -/// Synchronise client and server once (not permanent). -/// Copy the pixmap gCws->fDrawing on the window gCws->fWindow -/// if the double buffer is on. +/// Returns 1 if window system server supports extension given by the +/// argument, returns 0 in case extension is not supported and returns -1 +/// in case of error (like server not initialized). +/// Examples: +/// - "Apple-WM" - does server run on MacOS X; +/// - "XINERAMA" - does server support Xinerama. +/// See also the output of xdpyinfo. -void TGX11::UpdateWindow(int mode) +Int_t TGX11::SupportsExtension(const char *ext) const { - if (gCws->fDoubleBuffer) { - XCopyArea((Display*)fDisplay, gCws->fDrawing, gCws->fWindow, - *gGCpxmp, 0, 0, gCws->fWidth, gCws->fHeight, 0, 0); - } - if (mode == 1) { - XFlush((Display*)fDisplay); - } else { - XSync((Display*)fDisplay, False); - } + Int_t major_opcode, first_event, first_error; + if (!(Display*)fDisplay) + return -1; + return XQueryExtension((Display*)fDisplay, ext, &major_opcode, &first_event, &first_error); } -//////////////////////////////////////////////////////////////////////////////// -/// Set pointer position. -/// -/// \param [in] ix New X coordinate of pointer -/// \param [in] iy New Y coordinate of pointer -/// \param [in] id Window identifier -/// -/// Coordinates are relative to the origin of the window id -/// or to the origin of the current window if id == 0. -void TGX11::Warp(Int_t ix, Int_t iy, Window_t id) +WinContext_t TGX11::GetWindowContext(Int_t wid) { - if (!id) { - // Causes problems when calling ProcessEvents()... BadWindow - //XWarpPointer((Display*)fDisplay, None, gCws->fWindow, 0, 0, 0, 0, ix, iy); - } else { - XWarpPointer((Display*)fDisplay, None, (Window) id, 0, 0, 0, 0, ix, iy); - } + return (WinContext_t) fWindows[wid].get(); } -//////////////////////////////////////////////////////////////////////////////// -/// Write the pixmap wid in the bitmap file pxname. -/// -/// \param [in] wid : Pixmap address -/// \param [in] w,h : Width and height of the pixmap. -/// \param [in] pxname : pixmap name - -void TGX11::WritePixmap(int wid, unsigned int w, unsigned int h, char *pxname) +void TGX11::SetAttFill(WinContext_t wctxt, const TAttFill &att) { - unsigned int wval, hval; - wval = w; - hval = h; + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; - gTws = &fWindows[wid]; - XWriteBitmapFile((Display*)fDisplay, pxname, gTws->fDrawing, wval, hval, -1, -1); -} + Int_t cindex = att.GetFillColor(); + if (!gStyle->GetFillColor() && cindex > 1) + cindex = 0; + if (cindex >= 0) + SetColor(ctxt, &ctxt->fGClist[kGCfill], Int_t(cindex)); + ctxt->fAttFill.SetFillColor(cindex); + Int_t style = att.GetFillStyle() / 1000; + Int_t fasi = att.GetFillStyle() % 1000; + Int_t stn = (fasi >= 1 && fasi <=25) ? fasi : 2; + ctxt->fAttFill.SetFillStyle(style * 1000 + fasi); -// -// Functions for GIFencode() -// + switch (style) { + case 1: // solid + ctxt->fillHollow = 0; + XSetFillStyle((Display*)fDisplay, ctxt->fGClist[kGCfill], FillSolid); + break; -static FILE *gOut; // output unit used WriteGIF and PutByte -static XImage *gXimage = nullptr; // image used in WriteGIF and GetPixel + case 2: // pattern + ctxt->fillHollow = 1; + break; -extern "C" { - int GIFquantize(UInt_t width, UInt_t height, Int_t *ncol, Byte_t *red, Byte_t *green, - Byte_t *blue, Byte_t *outputBuf, Byte_t *outputCmap); - long GIFencode(int Width, int Height, Int_t Ncol, Byte_t R[], Byte_t G[], Byte_t B[], Byte_t ScLine[], - void (*get_scline) (int, int, Byte_t *), void (*pb)(Byte_t)); - int GIFdecode(Byte_t *gifArr, Byte_t *pixArr, int *Width, int *Height, int *Ncols, Byte_t *R, Byte_t *G, Byte_t *B); - int GIFinfo(Byte_t *gifArr, int *Width, int *Height, int *Ncols); -} + case 3: // hatch + ctxt->fillHollow = 0; + XSetFillStyle((Display*)fDisplay, ctxt->fGClist[kGCfill], FillStippled); -//////////////////////////////////////////////////////////////////////////////// -/// Get pixels in line y and put in array scline. + if (stn != ctxt->fillFasi) { + if (ctxt->fillPattern != 0) + XFreePixmap((Display*)fDisplay, ctxt->fillPattern); -static void GetPixel(int y, int width, Byte_t *scline) -{ - for (int i = 0; i < width; i++) - scline[i] = Byte_t(XGetPixel(gXimage, i, y)); -} + ctxt->fillPattern = XCreateBitmapFromData((Display*)fDisplay, fRootWin, + (const char*)gStipples[stn], 16, 16); -//////////////////////////////////////////////////////////////////////////////// -/// Put byte b in output stream. + XSetStipple((Display*)fDisplay, ctxt->fGClist[kGCfill], ctxt->fillPattern); + ctxt->fillFasi = stn; + } + break; -static void PutByte(Byte_t b) -{ - if (ferror(gOut) == 0) fputc(b, gOut); + default: + ctxt->fillHollow = 1; + } } -//////////////////////////////////////////////////////////////////////////////// -/// Returns in R G B the ncol colors of the palette used by the image. -/// The image pixels are changed to index values in these R G B arrays. -/// This produces a colormap with only the used colors (so even on displays -/// with more than 8 planes we will be able to create GIF's when the image -/// contains no more than 256 different colors). If it does contain more -/// colors we will have to use GIFquantize to reduce the number of colors. -/// The R G B arrays must be deleted by the caller. - -void TGX11::ImgPickPalette(RXImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B) +void TGX11::SetAttLine(WinContext_t wctxt, const TAttLine &att) { - ULong_t *orgcolors = nullptr; - Int_t maxcolors = 0, ncolors = 0; + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; - // collect different image colors - int x, y; - for (x = 0; x < (int) gCws->fWidth; x++) { - for (y = 0; y < (int) gCws->fHeight; y++) { - ULong_t pixel = XGetPixel(image, x, y); - CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + if (ctxt->fAttLine.GetLineStyle() != att.GetLineStyle()) { //set style index only if different + if (att.GetLineStyle() <= 1) + ctxt->dashList.clear(); + else if (att.GetLineStyle() == 2) + ctxt->dashList = { 3, 3 }; + else if (att.GetLineStyle() == 3) + ctxt->dashList = { 1, 2 }; + else if (att.GetLineStyle() == 4) { + ctxt->dashList = { 3, 4, 1, 4} ; + } else { + TString st = (TString)gStyle->GetLineStyleString(att.GetLineStyle()); + auto tokens = st.Tokenize(" "); + Int_t nt = tokens->GetEntries(); + ctxt->dashList.resize(nt); + for (Int_t j = 0; j < nt; ++j) { + Int_t it; + sscanf(tokens->At(j)->GetName(), "%d", &it); + ctxt->dashList[j] = (Int_t) (it/4); + } + delete tokens; } + ctxt->dashLength = 0; + for (auto elem : ctxt->dashList) + ctxt->dashLength += elem; + ctxt->dashOffset = 0; + ctxt->lineStyle = ctxt->dashList.size() == 0 ? LineSolid : LineOnOffDash; } - // get RGB values belonging to pixels - RXColor *xcol = new RXColor[ncolors]; - - int i; - for (i = 0; i < ncolors; i++) { - xcol[i].pixel = orgcolors[i]; - xcol[i].red = xcol[i].green = xcol[i].blue = 0; - xcol[i].flags = DoRed | DoGreen | DoBlue; + ctxt->lineWidth = att.GetLineWidth(); + if (ctxt->lineStyle == LineSolid) { + if (ctxt->lineWidth == 1) + ctxt->lineWidth = 0; + } else { + if (ctxt->lineWidth == 0) + ctxt->lineWidth = 1; } - QueryColors(fColormap, xcol, ncolors); - - // create RGB arrays and store RGB's for each color and set number of colors - // (space must be delete by caller) - R = new Int_t[ncolors]; - G = new Int_t[ncolors]; - B = new Int_t[ncolors]; - for (i = 0; i < ncolors; i++) { - R[i] = xcol[i].red; - G[i] = xcol[i].green; - B[i] = xcol[i].blue; + if (ctxt->lineWidth >= 0) { + XSetLineAttributes((Display*)fDisplay, ctxt->fGClist[kGCline], ctxt->lineWidth, + ctxt->lineStyle, gCapStyle, gJoinStyle); + if (ctxt->lineStyle == LineOnOffDash) + XSetLineAttributes((Display*)fDisplay, ctxt->fGClist[kGCdash], ctxt->lineWidth, + ctxt->lineStyle, gCapStyle, gJoinStyle); } - ncol = ncolors; - // update image with indices (pixels) into the new RGB colormap - for (x = 0; x < (int) gCws->fWidth; x++) { - for (y = 0; y < (int) gCws->fHeight; y++) { - ULong_t pixel = XGetPixel(image, x, y); - Int_t idx = FindColor(pixel, orgcolors, ncolors); - XPutPixel(image, x, y, idx); - } + if (att.GetLineColor() >= 0) { + SetColor(ctxt, &ctxt->fGClist[kGCline], (Int_t) att.GetLineColor()); + SetColor(ctxt, &ctxt->fGClist[kGCdash], (Int_t) att.GetLineColor()); } - // cleanup - delete [] xcol; - ::operator delete(orgcolors); + ctxt->fAttLine = att; } -//////////////////////////////////////////////////////////////////////////////// -/// Writes the current window into GIF file. Returns 1 in case of success, -/// 0 otherwise. - -Int_t TGX11::WriteGIF(char *name) +void TGX11::SetAttMarker(WinContext_t wctxt, const TAttMarker &att) { - Byte_t scline[2000], r[256], b[256], g[256]; - Int_t *red, *green, *blue; - Int_t ncol, maxcol, i; + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; - if (gXimage) { - XDestroyImage(gXimage); - gXimage = nullptr; - } + SetColor(ctxt, &ctxt->fGClist[kGCmark], att.GetMarkerColor()); - gXimage = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, - gCws->fWidth, gCws->fHeight, - AllPlanes, ZPixmap); + Bool_t changed = (att.GetMarkerSize() != ctxt->fAttMarker.GetMarkerSize()) || + (att.GetMarkerStyle() != ctxt->fAttMarker.GetMarkerStyle()); - ImgPickPalette((RXImage*)gXimage, ncol, red, green, blue); + ctxt->fAttMarker = att; - if (ncol > 256) { - //GIFquantize(...); - Error("WriteGIF", "Cannot create GIF of image containing more than 256 colors. Try in batch mode."); - delete [] red; - delete [] green; - delete [] blue; - return 0; - } + if (!changed) + return; - maxcol = 0; - for (i = 0; i < ncol; i++) { - if (maxcol < red[i] ) maxcol = red[i]; - if (maxcol < green[i] ) maxcol = green[i]; - if (maxcol < blue[i] ) maxcol = blue[i]; - r[i] = 0; - g[i] = 0; - b[i] = 0; - } - if (maxcol != 0) { - for (i = 0; i < ncol; i++) { - r[i] = red[i] * 255/maxcol; - g[i] = green[i] * 255/maxcol; - b[i] = blue[i] * 255/maxcol; - } - } + Int_t markerstyle = TAttMarker::GetMarkerStyleBase(att.GetMarkerStyle()); + ctxt->markerLineWidth = TAttMarker::GetMarkerLineWidth(att.GetMarkerStyle()); - gOut = fopen(name, "w+"); + // The fast pixel markers need to be treated separately + if (markerstyle == 1 || markerstyle == 6 || markerstyle == 7) { + XSetLineAttributes((Display*)fDisplay, ctxt->fGClist[kGCmark], 0, LineSolid, CapButt, JoinMiter); + } else { + XSetLineAttributes((Display*)fDisplay, ctxt->fGClist[kGCmark], ctxt->markerLineWidth, + gMarkerLineStyle, gMarkerCapStyle, gMarkerJoinStyle); + } - if (gOut) { - GIFencode(gCws->fWidth, gCws->fHeight, - ncol, r, g, b, scline, ::GetPixel, PutByte); - fclose(gOut); - i = 1; + Float_t MarkerSizeReduced = att.GetMarkerSize() - TMath::Floor(ctxt->markerLineWidth/2.)/4.; + Int_t im = Int_t(4*MarkerSizeReduced + 0.5); + auto &shape = ctxt->markerShape; + ctxt->markerSize = 0; + ctxt->markerType = 0; + if (markerstyle == 2) { + // + shaped marker + shape.resize(4); + shape[0].x = -im; shape[0].y = 0; + shape[1].x = im; shape[1].y = 0; + shape[2].x = 0 ; shape[2].y = -im; + shape[3].x = 0 ; shape[3].y = im; + ctxt->markerType = 4; + } else if (markerstyle == 3 || markerstyle == 31) { + // * shaped marker + shape.resize(8); + shape[0].x = -im; shape[0].y = 0; + shape[1].x = im; shape[1].y = 0; + shape[2].x = 0 ; shape[2].y = -im; + shape[3].x = 0 ; shape[3].y = im; + im = Int_t(0.707*Float_t(im) + 0.5); + shape[4].x = -im; shape[4].y = -im; + shape[5].x = im; shape[5].y = im; + shape[6].x = -im; shape[6].y = im; + shape[7].x = im; shape[7].y = -im; + ctxt->markerType = 4; + } else if (markerstyle == 4 || markerstyle == 24) { + // O shaped marker + shape.resize(0); + ctxt->markerType = 0; + ctxt->markerSize = im*2; + } else if (markerstyle == 5) { + // X shaped marker + shape.resize(4); + im = Int_t(0.707*Float_t(im) + 0.5); + shape[0].x = -im; shape[0].y = -im; + shape[1].x = im; shape[1].y = im; + shape[2].x = -im; shape[2].y = im; + shape[3].x = im; shape[3].y = -im; + ctxt->markerType = 4; + } else if (markerstyle == 6) { + // + shaped marker (with 1 pixel) + shape.resize(4); + shape[0].x = -1 ; shape[0].y = 0; + shape[1].x = 1 ; shape[1].y = 0; + shape[2].x = 0 ; shape[2].y = -1; + shape[3].x = 0 ; shape[3].y = 1; + ctxt->markerType = 4; + } else if (markerstyle == 7) { + // . shaped marker (with 9 pixel) + shape.resize(6); + shape[0].x = -1 ; shape[0].y = 1; + shape[1].x = 1 ; shape[1].y = 1; + shape[2].x = -1 ; shape[2].y = 0; + shape[3].x = 1 ; shape[3].y = 0; + shape[4].x = -1 ; shape[4].y = -1; + shape[5].x = 1 ; shape[5].y = -1; + ctxt->markerType = 4; + } else if (markerstyle == 8 || markerstyle == 20) { + // O shaped marker (filled) + shape.resize(0); + ctxt->markerType = 1; + ctxt->markerSize = im*2; + } else if (markerstyle == 21) { + // full square + shape.resize(5); + shape[0].x = -im; shape[0].y = -im; + shape[1].x = im; shape[1].y = -im; + shape[2].x = im; shape[2].y = im; + shape[3].x = -im; shape[3].y = im; + shape[4].x = -im; shape[4].y = -im; + ctxt->markerType = 3; + } else if (markerstyle == 22) { + // full triangle up + shape.resize(4); + shape[0].x = -im; shape[0].y = im; + shape[1].x = im; shape[1].y = im; + shape[2].x = 0; shape[2].y = -im; + shape[3].x = -im; shape[3].y = im; + ctxt->markerType = 3; + } else if (markerstyle == 23) { + // full triangle down + shape.resize(4); + shape[0].x = 0; shape[0].y = im; + shape[1].x = im; shape[1].y = -im; + shape[2].x = -im; shape[2].y = -im; + shape[3].x = 0; shape[3].y = im; + ctxt->markerType = 3; + } else if (markerstyle == 25) { + // open square + shape.resize(5); + shape[0].x = -im; shape[0].y = -im; + shape[1].x = im; shape[1].y = -im; + shape[2].x = im; shape[2].y = im; + shape[3].x = -im; shape[3].y = im; + shape[4].x = -im; shape[4].y = -im; + ctxt->markerType = 2; + } else if (markerstyle == 26) { + // open triangle up + shape.resize(4); + shape[0].x = -im; shape[0].y = im; + shape[1].x = im; shape[1].y = im; + shape[2].x = 0; shape[2].y = -im; + shape[3].x = -im; shape[3].y = im; + ctxt->markerType = 2; + } else if (markerstyle == 27) { + // open losange + shape.resize(5); + Int_t imx = Int_t(2.66*MarkerSizeReduced + 0.5); + shape[0].x =-imx; shape[0].y = 0; + shape[1].x = 0; shape[1].y = -im; + shape[2].x = imx; shape[2].y = 0; + shape[3].x = 0; shape[3].y = im; + shape[4].x =-imx; shape[4].y = 0; + ctxt->markerType = 2; + } else if (markerstyle == 28) { + // open cross + shape.resize(13); + Int_t imx = Int_t(1.33*MarkerSizeReduced + 0.5); + shape[0].x = -im; shape[0].y =-imx; + shape[1].x =-imx; shape[1].y =-imx; + shape[2].x =-imx; shape[2].y = -im; + shape[3].x = imx; shape[3].y = -im; + shape[4].x = imx; shape[4].y =-imx; + shape[5].x = im; shape[5].y =-imx; + shape[6].x = im; shape[6].y = imx; + shape[7].x = imx; shape[7].y = imx; + shape[8].x = imx; shape[8].y = im; + shape[9].x =-imx; shape[9].y = im; + shape[10].x=-imx; shape[10].y= imx; + shape[11].x= -im; shape[11].y= imx; + shape[12].x= -im; shape[12].y=-imx; + ctxt->markerType = 2; + } else if (markerstyle == 29) { + // full star pentagone + shape.resize(11); + Int_t im1 = Int_t(0.66*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.00*MarkerSizeReduced + 0.5); + Int_t im3 = Int_t(2.66*MarkerSizeReduced + 0.5); + Int_t im4 = Int_t(1.33*MarkerSizeReduced + 0.5); + shape[0].x = -im; shape[0].y = im4; + shape[1].x =-im2; shape[1].y =-im1; + shape[2].x =-im3; shape[2].y = -im; + shape[3].x = 0; shape[3].y =-im2; + shape[4].x = im3; shape[4].y = -im; + shape[5].x = im2; shape[5].y =-im1; + shape[6].x = im; shape[6].y = im4; + shape[7].x = im4; shape[7].y = im4; + shape[8].x = 0; shape[8].y = im; + shape[9].x =-im4; shape[9].y = im4; + shape[10].x= -im; shape[10].y= im4; + ctxt->markerType = 3; + } else if (markerstyle == 30) { + // open star pentagone + shape.resize(11); + Int_t im1 = Int_t(0.66*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.00*MarkerSizeReduced + 0.5); + Int_t im3 = Int_t(2.66*MarkerSizeReduced + 0.5); + Int_t im4 = Int_t(1.33*MarkerSizeReduced + 0.5); + shape[0].x = -im; shape[0].y = im4; + shape[1].x =-im2; shape[1].y =-im1; + shape[2].x =-im3; shape[2].y = -im; + shape[3].x = 0; shape[3].y =-im2; + shape[4].x = im3; shape[4].y = -im; + shape[5].x = im2; shape[5].y =-im1; + shape[6].x = im; shape[6].y = im4; + shape[7].x = im4; shape[7].y = im4; + shape[8].x = 0; shape[8].y = im; + shape[9].x =-im4; shape[9].y = im4; + shape[10].x= -im; shape[10].y= im4; + ctxt->markerType = 2; + } else if (markerstyle == 32) { + // open triangle down + shape.resize(4); + shape[0].x = 0; shape[0].y = im; + shape[1].x = im; shape[1].y = -im; + shape[2].x = -im; shape[2].y = -im; + shape[3].x = 0; shape[3].y = im; + ctxt->markerType = 2; + } else if (markerstyle == 33) { + // full losange + shape.resize(5); + Int_t imx = Int_t(2.66*MarkerSizeReduced + 0.5); + shape[0].x =-imx; shape[0].y = 0; + shape[1].x = 0; shape[1].y = -im; + shape[2].x = imx; shape[2].y = 0; + shape[3].x = 0; shape[3].y = im; + shape[4].x =-imx; shape[4].y = 0; + ctxt->markerType = 3; + } else if (markerstyle == 34) { + // full cross + shape.resize(13); + Int_t imx = Int_t(1.33*MarkerSizeReduced + 0.5); + shape[0].x = -im; shape[0].y =-imx; + shape[1].x =-imx; shape[1].y =-imx; + shape[2].x =-imx; shape[2].y = -im; + shape[3].x = imx; shape[3].y = -im; + shape[4].x = imx; shape[4].y =-imx; + shape[5].x = im; shape[5].y =-imx; + shape[6].x = im; shape[6].y = imx; + shape[7].x = imx; shape[7].y = imx; + shape[8].x = imx; shape[8].y = im; + shape[9].x =-imx; shape[9].y = im; + shape[10].x=-imx; shape[10].y= imx; + shape[11].x= -im; shape[11].y= imx; + shape[12].x= -im; shape[12].y=-imx; + ctxt->markerType = 3; + } else if (markerstyle == 35) { + // diamond with cross + shape.resize(8); + shape[0].x =-im; shape[0].y = 0; + shape[1].x = 0; shape[1].y = -im; + shape[2].x = im; shape[2].y = 0; + shape[3].x = 0; shape[3].y = im; + shape[4].x =-im; shape[4].y = 0; + shape[5].x = im; shape[5].y = 0; + shape[6].x = 0; shape[6].y = im; + shape[7].x = 0; shape[7].y =-im; + ctxt->markerType = 2; + } else if (markerstyle == 36) { + // square with diagonal cross + shape.resize(8); + shape[0].x = -im; shape[0].y = -im; + shape[1].x = im; shape[1].y = -im; + shape[2].x = im; shape[2].y = im; + shape[3].x = -im; shape[3].y = im; + shape[4].x = -im; shape[4].y = -im; + shape[5].x = im; shape[5].y = im; + shape[6].x = -im; shape[6].y = im; + shape[7].x = im; shape[7].y = -im; + ctxt->markerType = 2; + } else if (markerstyle == 37) { + // open three triangles + shape.resize(10); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = 0; + shape[1].x =-im2; shape[1].y = im; + shape[2].x = im2; shape[2].y = im; + shape[3].x = 0; shape[3].y = 0; + shape[4].x =-im2; shape[4].y = -im; + shape[5].x = -im; shape[5].y = 0; + shape[6].x = 0; shape[6].y = 0; + shape[7].x = im; shape[7].y = 0; + shape[8].x = im2; shape[8].y = -im; + shape[9].x = 0; shape[9].y = 0; + ctxt->markerType = 2; + } else if (markerstyle == 38) { + // + shaped marker with octagon + shape.resize(15); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = -im; shape[0].y = 0; + shape[1].x = -im; shape[1].y =-im2; + shape[2].x =-im2; shape[2].y = -im; + shape[3].x = im2; shape[3].y = -im; + shape[4].x = im; shape[4].y =-im2; + shape[5].x = im; shape[5].y = im2; + shape[6].x = im2; shape[6].y = im; + shape[7].x =-im2; shape[7].y = im; + shape[8].x = -im; shape[8].y = im2; + shape[9].x = -im; shape[9].y = 0; + shape[10].x = im; shape[10].y = 0; + shape[11].x = 0; shape[11].y = 0; + shape[12].x = 0; shape[12].y = -im; + shape[13].x = 0; shape[13].y = im; + shape[14].x = 0; shape[14].y = 0; + ctxt->markerType = 2; + } else if (markerstyle == 39) { + // filled three triangles + shape.resize(9); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = 0; + shape[1].x =-im2; shape[1].y = im; + shape[2].x = im2; shape[2].y = im; + shape[3].x = 0; shape[3].y = 0; + shape[4].x =-im2; shape[4].y = -im; + shape[5].x = -im; shape[5].y = 0; + shape[6].x = 0; shape[6].y = 0; + shape[7].x = im; shape[7].y = 0; + shape[8].x = im2; shape[8].y = -im; + ctxt->markerType = 3; + } else if (markerstyle == 40) { + // four open triangles X + shape.resize(13); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = 0; + shape[1].x = im2; shape[1].y = im; + shape[2].x = im; shape[2].y = im2; + shape[3].x = 0; shape[3].y = 0; + shape[4].x = im; shape[4].y = -im2; + shape[5].x = im2; shape[5].y = -im; + shape[6].x = 0; shape[6].y = 0; + shape[7].x = -im2; shape[7].y = -im; + shape[8].x = -im; shape[8].y = -im2; + shape[9].x = 0; shape[9].y = 0; + shape[10].x = -im; shape[10].y = im2; + shape[11].x = -im2; shape[11].y = im; + shape[12].x = 0; shape[12].y = 0; + ctxt->markerType = 2; + } else if (markerstyle == 41) { + // four filled triangles X + shape.resize(13); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = 0; + shape[1].x = im2; shape[1].y = im; + shape[2].x = im; shape[2].y = im2; + shape[3].x = 0; shape[3].y = 0; + shape[4].x = im; shape[4].y = -im2; + shape[5].x = im2; shape[5].y = -im; + shape[6].x = 0; shape[6].y = 0; + shape[7].x = -im2; shape[7].y = -im; + shape[8].x = -im; shape[8].y = -im2; + shape[9].x = 0; shape[9].y = 0; + shape[10].x = -im; shape[10].y = im2; + shape[11].x = -im2; shape[11].y = im; + shape[12].x = 0; shape[12].y = 0; + ctxt->markerType = 3; + } else if (markerstyle == 42) { + // open double diamonds + shape.resize(9); + Int_t imx = Int_t(MarkerSizeReduced + 0.5); + shape[0].x= 0; shape[0].y= im; + shape[1].x= -imx; shape[1].y= imx; + shape[2].x = -im; shape[2].y = 0; + shape[3].x = -imx; shape[3].y = -imx; + shape[4].x = 0; shape[4].y = -im; + shape[5].x = imx; shape[5].y = -imx; + shape[6].x = im; shape[6].y = 0; + shape[7].x= imx; shape[7].y= imx; + shape[8].x= 0; shape[8].y= im; + ctxt->markerType = 2; + } else if (markerstyle == 43) { + // filled double diamonds + shape.resize(9); + Int_t imx = Int_t(MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = im; + shape[1].x = -imx; shape[1].y = imx; + shape[2].x = -im; shape[2].y = 0; + shape[3].x = -imx; shape[3].y = -imx; + shape[4].x = 0; shape[4].y = -im; + shape[5].x = imx; shape[5].y = -imx; + shape[6].x = im; shape[6].y = 0; + shape[7].x = imx; shape[7].y = imx; + shape[8].x = 0; shape[8].y = im; + ctxt->markerType = 3; + } else if (markerstyle == 44) { + // open four triangles plus + shape.resize(11); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = 0; + shape[1].x = im2; shape[1].y = im; + shape[2].x = -im2; shape[2].y = im; + shape[3].x = im2; shape[3].y = -im; + shape[4].x = -im2; shape[4].y = -im; + shape[5].x = 0; shape[5].y = 0; + shape[6].x = im; shape[6].y = im2; + shape[7].x = im; shape[7].y = -im2; + shape[8].x = -im; shape[8].y = im2; + shape[9].x = -im; shape[9].y = -im2; + shape[10].x = 0; shape[10].y = 0; + ctxt->markerType = 2; + } else if (markerstyle == 45) { + // filled four triangles plus + shape.resize(13); + Int_t im0 = Int_t(0.4*MarkerSizeReduced + 0.5); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = im0; shape[0].y = im0; + shape[1].x = im2; shape[1].y = im; + shape[2].x = -im2; shape[2].y = im; + shape[3].x = -im0; shape[3].y = im0; + shape[4].x = -im; shape[4].y = im2; + shape[5].x = -im; shape[5].y = -im2; + shape[6].x = -im0; shape[6].y = -im0; + shape[7].x = -im2; shape[7].y = -im; + shape[8].x = im2; shape[8].y = -im; + shape[9].x = im0; shape[9].y = -im0; + shape[10].x = im; shape[10].y = -im2; + shape[11].x = im; shape[11].y = im2; + shape[12].x = im0; shape[12].y = im0; + ctxt->markerType = 3; + } else if (markerstyle == 46) { + // open four triangles X + shape.resize(13); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = im2; + shape[1].x = -im2; shape[1].y = im; + shape[2].x = -im; shape[2].y = im2; + shape[3].x = -im2; shape[3].y = 0; + shape[4].x = -im; shape[4].y = -im2; + shape[5].x = -im2; shape[5].y = -im; + shape[6].x = 0; shape[6].y = -im2; + shape[7].x = im2; shape[7].y = -im; + shape[8].x = im; shape[8].y = -im2; + shape[9].x = im2; shape[9].y = 0; + shape[10].x = im; shape[10].y = im2; + shape[11].x = im2; shape[11].y = im; + shape[12].x = 0; shape[12].y = im2; + ctxt->markerType = 2; + } else if (markerstyle == 47) { + // filled four triangles X + shape.resize(13); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = im2; + shape[1].x = -im2; shape[1].y = im; + shape[2].x = -im; shape[2].y = im2; + shape[3].x = -im2; shape[3].y = 0; + shape[4].x = -im; shape[4].y = -im2; + shape[5].x = -im2; shape[5].y = -im; + shape[6].x = 0; shape[6].y = -im2; + shape[7].x = im2; shape[7].y = -im; + shape[8].x = im; shape[8].y = -im2; + shape[9].x = im2; shape[9].y = 0; + shape[10].x = im; shape[10].y = im2; + shape[11].x = im2; shape[11].y = im; + shape[12].x = 0; shape[12].y = im2; + ctxt->markerType = 3; + } else if (markerstyle == 48) { + // four filled squares X + shape.resize(17); + Int_t im2 = Int_t(2.0*MarkerSizeReduced + 0.5); + shape[0].x = 0; shape[0].y = im2*1.005; + shape[1].x = -im2; shape[1].y = im; + shape[2].x = -im; shape[2].y = im2; + shape[3].x = -im2; shape[3].y = 0; + shape[4].x = -im; shape[4].y = -im2; + shape[5].x = -im2; shape[5].y = -im; + shape[6].x = 0; shape[6].y = -im2; + shape[7].x = im2; shape[7].y = -im; + shape[8].x = im; shape[8].y = -im2; + shape[9].x = im2; shape[9].y = 0; + shape[10].x = im; shape[10].y = im2; + shape[11].x = im2; shape[11].y = im; + shape[12].x = 0; shape[12].y = im2*0.995; + shape[13].x = im2*0.995; shape[13].y = 0; + shape[14].x = 0; shape[14].y = -im2*0.995; + shape[15].x = -im2*0.995; shape[15].y = 0; + shape[16].x = 0; shape[16].y = im2*0.995; + ctxt->markerType = 3; + } else if (markerstyle == 49) { + // four filled squares plus + shape.resize(17); + Int_t imx = Int_t(1.33*MarkerSizeReduced + 0.5); + shape[0].x =-imx; shape[0].y =-imx*1.005; + shape[1].x =-imx; shape[1].y = -im; + shape[2].x = imx; shape[2].y = -im; + shape[3].x = imx; shape[3].y =-imx; + shape[4].x = im; shape[4].y =-imx; + shape[5].x = im; shape[5].y = imx; + shape[6].x = imx; shape[6].y = imx; + shape[7].x = imx; shape[7].y = im; + shape[8].x =-imx; shape[8].y = im; + shape[9].x =-imx; shape[9].y = imx; + shape[10].x = -im; shape[10].y = imx; + shape[11].x = -im; shape[11].y =-imx; + shape[12].x =-imx; shape[12].y =-imx*0.995; + shape[13].x =-imx; shape[13].y = imx; + shape[14].x = imx; shape[14].y = imx; + shape[15].x = imx; shape[15].y =-imx; + shape[16].x =-imx; shape[16].y =-imx*1.005; + ctxt->markerType = 3; } else { - Error("WriteGIF","cannot write file: %s",name); - i = 0; + // single dot + shape.resize(0); + ctxt->markerType = 0; + ctxt->markerSize = 0; } - delete [] red; - delete [] green; - delete [] blue; - return i; } -//////////////////////////////////////////////////////////////////////////////// -/// Draw image. - -void TGX11::PutImage(Int_t offset,Int_t itran,Int_t x0,Int_t y0,Int_t nx,Int_t ny,Int_t xmin, - Int_t ymin,Int_t xmax,Int_t ymax, UChar_t *image,Drawable_t wid) +void TGX11::SetAttText(WinContext_t wctxt, const TAttText &att) { - const int maxSegment = 20; - int i, n, x, y, xcur, x1, x2, y1, y2; - unsigned char *jimg, *jbase, icol; - int nlines[256]; - XSegment lines[256][maxSegment]; - Drawable_t id; - - if (wid) { - id = wid; - } else { - id = gCws->fDrawing; - } + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return; - for (i = 0; i < 256; i++) nlines[i] = 0; + Int_t txalh = att.GetTextAlign() / 10; + Int_t txalv = att.GetTextAlign() % 10; - x1 = x0 + xmin; y1 = y0 + ny - ymax - 1; - x2 = x0 + xmax; y2 = y0 + ny - ymin - 1; - jbase = image + (ymin-1)*nx + xmin; + ctxt->textAlign = kAlignNone; - for (y = y2; y >= y1; y--) { - xcur = x1; jbase += nx; - for (jimg = jbase, icol = *jimg++, x = x1+1; x <= x2; jimg++, x++) { - if (icol != *jimg) { - if (icol != itran) { - n = nlines[icol]++; - lines[icol][n].x1 = xcur; lines[icol][n].y1 = y; - lines[icol][n].x2 = x-1; lines[icol][n].y2 = y; - if (nlines[icol] == maxSegment) { - SetColor(gGCline,(int)icol+offset); - XDrawSegments((Display*)fDisplay,id,*gGCline,&lines[icol][0], - maxSegment); - nlines[icol] = 0; - } - } - icol = *jimg; xcur = x; + switch (txalh) { + case 0 : + case 1 : + switch (txalv) { //left + case 1 : + ctxt->textAlign = kBLeft; //bottom + break; + case 2 : + ctxt->textAlign = kMLeft; //middle + break; + case 3 : + ctxt->textAlign = kTLeft; //top + break; } - } - if (icol != itran) { - n = nlines[icol]++; - lines[icol][n].x1 = xcur; lines[icol][n].y1 = y; - lines[icol][n].x2 = x-1; lines[icol][n].y2 = y; - if (nlines[icol] == maxSegment) { - SetColor(gGCline,(int)icol+offset); - XDrawSegments((Display*)fDisplay,id,*gGCline,&lines[icol][0], - maxSegment); - nlines[icol] = 0; + break; + case 2 : + switch (txalv) { //center + case 1 : + ctxt->textAlign = kBCenter; //bottom + break; + case 2 : + ctxt->textAlign = kMCenter; //middle + break; + case 3 : + ctxt->textAlign = kTCenter; //top + break; } - } - } - - for (i = 0; i < 256; i++) { - if (nlines[i] != 0) { - SetColor(gGCline,i+offset); - XDrawSegments((Display*)fDisplay,id,*gGCline,&lines[i][0],nlines[i]); - } + break; + case 3 : + switch (txalv) { //right + case 1 : + ctxt->textAlign = kBRight; //bottom + break; + case 2 : + ctxt->textAlign = kMRight; //center + break; + case 3 : + ctxt->textAlign = kTRight; //top + break; + } + break; } -} - -//////////////////////////////////////////////////////////////////////////////// -/// If id is NULL - loads the specified gif file at position [x0,y0] in the -/// current window. Otherwise creates pixmap from gif file - -Pixmap_t TGX11::ReadGIF(int x0, int y0, const char *file, Window_t id) -{ - FILE *fd; - Seek_t filesize = 0; - unsigned char *gifArr, *pixArr, red[256], green[256], blue[256], *j1, *j2, icol; - int i, j, k, width, height, ncolor, irep, offset; - float rr, gg, bb; - Pixmap_t pic = 0; - fd = fopen(file, "r"); - if (!fd) { - Error("ReadGIF", "unable to open GIF file"); - return pic; - } + SetColor(ctxt, &ctxt->fGClist[kGCtext], att.GetTextColor()); - fseek(fd, 0L, 2); - long ft = ftell(fd); - if (ft <=0) { - Error("ReadGIF", "unable to open GIF file"); - fclose(fd); - return pic; + XGCValues values; + if (XGetGCValues((Display*)fDisplay, ctxt->fGClist[kGCtext], GCForeground | GCBackground, &values)) { + XSetForeground( (Display*)fDisplay, ctxt->fGClist[kGCinvt], values.background ); + XSetBackground( (Display*)fDisplay, ctxt->fGClist[kGCinvt], values.foreground ); } else { - filesize = Seek_t(ft); - } - fseek(fd, 0L, 0); - - if (!(gifArr = (unsigned char *) calloc(filesize+256,1))) { - Error("ReadGIF", "unable to allocate array for gif"); - fclose(fd); - return pic; - } - - if (fread(gifArr, filesize, 1, fd) != 1) { - Error("ReadGIF", "GIF file read failed"); - free(gifArr); - fclose(fd); - return pic; - } - fclose(fd); - - irep = GIFinfo(gifArr, &width, &height, &ncolor); - if (irep != 0) { - free(gifArr); - return pic; - } - - if (!(pixArr = (unsigned char *) calloc((width*height),1))) { - Error("ReadGIF", "unable to allocate array for image"); - free(gifArr); - return pic; - } - - irep = GIFdecode(gifArr, pixArr, &width, &height, &ncolor, red, green, blue); - if (irep != 0) { - free(gifArr); - free(pixArr); - return pic; - } - - // S E T P A L E T T E - - offset = 8; - - for (i = 0; i < ncolor; i++) { - rr = red[i]/255.; - gg = green[i]/255.; - bb = blue[i]/255.; - j = i+offset; - SetRGB(j,rr,gg,bb); + Error("SetAttText", "cannot get GC values"); } + XSetBackground((Display*)fDisplay, ctxt->fGClist[kGCtext], GetColor(0).fPixel); - // O U T P U T I M A G E - - for (i = 1; i <= height/2; i++) { - j1 = pixArr + (i-1)*width; - j2 = pixArr + (height-i)*width; - for (k = 0; k < width; k++) { - icol = *j1; *j1++ = *j2; *j2++ = icol; + // use first existing font + for (int i = 0; i < kMAXFONT; i++) + if (gFont[i].id) { + gCws->textFont = gFont[i].id; + XSetFont((Display*)fDisplay, gCws->fGClist[kGCtext], gCws->textFont->fid); + XSetFont((Display*)fDisplay, gCws->fGClist[kGCinvt], gCws->textFont->fid); + break; } - } - if (id) pic = CreatePixmap(id, width, height); - PutImage(offset,-1,x0,y0,width,height,0,0,width-1,height-1,pixArr,pic); - - free(gifArr); - free(pixArr); - - if (pic) - return pic; - else if (gCws->fDrawing) - return (Pixmap_t)gCws->fDrawing; - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -/// Returns an array of pixels created from a part of drawable -/// (defined by x, y, w, h) in format: -/// `b1, g1, r1, 0, b2, g2, r2, 0, ..., bn, gn, rn, 0`. -/// -/// Pixels are numbered from left to right and from top to bottom. -/// By default all pixels from the whole drawable are returned. -/// -/// Note that return array is 32-bit aligned - -unsigned char *TGX11::GetColorBits(Drawable_t /*wid*/, Int_t /*x*/, Int_t /*y*/, - UInt_t /*w*/, UInt_t /*h*/) -{ - return nullptr; -} - -//////////////////////////////////////////////////////////////////////////////// -/// create pixmap from RGB data. RGB data is in format : -/// b1, g1, r1, 0, b2, g2, r2, 0 ... bn, gn, rn, 0 .. -/// -/// Pixels are numbered from left to right and from top to bottom. -/// Note that data must be 32-bit aligned -Pixmap_t TGX11::CreatePixmapFromData(unsigned char * /*bits*/, UInt_t /*width*/, - UInt_t /*height*/) -{ - return (Pixmap_t)0; + ctxt->fAttText = att; } //////////////////////////////////////////////////////////////////////////////// -/// Register pixmap created by gVirtualGL -/// -/// \param [in] pixid Pixmap identifier -/// \param [in] w,h Width and height of the pixmap -/// -/// register new pixmap +/// Set window draw mode -Int_t TGX11::AddPixmap(ULong_t pixid, UInt_t w, UInt_t h) +void TGX11::SetDrawModeW(WinContext_t wctxt, EDrawMode mode) { - Int_t wid = 0; - - // Select next free window number - for (; wid < fMaxNumberOfWindows; ++wid) - if (!fWindows[wid].fOpen) - break; - - if (wid == fMaxNumberOfWindows) { - Int_t newsize = fMaxNumberOfWindows + 10; - fWindows = (XWindow_t*) TStorage::ReAlloc( - fWindows, newsize * sizeof(XWindow_t), - fMaxNumberOfWindows*sizeof(XWindow_t) - ); - - for (Int_t i = fMaxNumberOfWindows; i < newsize; ++i) - fWindows[i].fOpen = 0; + fDrawMode = mode; - fMaxNumberOfWindows = newsize; - } + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt || !fDisplay) + return; - fWindows[wid].fOpen = 1; - gCws = fWindows + wid; - gCws->fWindow = pixid; - gCws->fDrawing = gCws->fWindow; - gCws->fBuffer = 0; - gCws->fDoubleBuffer = 0; - gCws->fIsPixmap = 1; - gCws->fClip = 0; - gCws->fWidth = w; - gCws->fHeight = h; - gCws->fNewColors = nullptr; - gCws->fShared = kFALSE; + auto gxmode = GXcopy; + if (mode == kXor) + gxmode = GXxor; + else if (mode == kInvert) + gxmode = GXinvert; + for (int i = 0; i < kMAXGC; i++) + XSetFunction((Display*)fDisplay, ctxt->fGClist[i], gxmode); - return wid; + ctxt->drawMode = mode; } //////////////////////////////////////////////////////////////////////////////// -/// Returns 1 if window system server supports extension given by the -/// argument, returns 0 in case extension is not supported and returns -1 -/// in case of error (like server not initialized). -/// Examples: -/// - "Apple-WM" - does server run on MacOS X; -/// - "XINERAMA" - does server support Xinerama. -/// See also the output of xdpyinfo. +/// Returns window draw mode -Int_t TGX11::SupportsExtension(const char *ext) const +TVirtualX::EDrawMode TGX11::GetDrawModeW(WinContext_t wctxt) { - Int_t major_opcode, first_event, first_error; - if (!(Display*)fDisplay) - return -1; - return XQueryExtension((Display*)fDisplay, ext, &major_opcode, &first_event, &first_error); + auto ctxt = (XWindow_t *) wctxt; + return ctxt ? ctxt->drawMode : kCopy; } diff --git a/graf2d/x11ttf/inc/TGX11TTF.h b/graf2d/x11ttf/inc/TGX11TTF.h index 99b4dbae09486..0b83aaddf3cd3 100644 --- a/graf2d/x11ttf/inc/TGX11TTF.h +++ b/graf2d/x11ttf/inc/TGX11TTF.h @@ -27,44 +27,35 @@ class TXftFontHash; class TGX11TTF : public TGX11 { private: - enum EAlign { -// clang++ (gVirtualX)) { - TGX11 *oldg = (TGX11 *) gVirtualX; - gVirtualX = new TGX11TTF(*oldg); + if (auto oldg = dynamic_cast(gVirtualX)) { + gVirtualX = new TGX11TTF(std::move(*oldg)); delete oldg; } } @@ -209,9 +208,9 @@ Bool_t TGX11TTF::Init(void *display) /// then the rotation is applied on the alignment variables. /// SetRotation and LayoutGlyphs should have been called before. -void TGX11TTF::Align(void) +void TGX11TTF::Align(WinContext_t wctxt) { - EAlign align = (EAlign) fTextAlign; + auto align = GetTextAlignW(wctxt); // vertical alignment if (align == kTLeft || align == kTCenter || align == kTRight) { @@ -361,52 +360,52 @@ void TGX11TTF::DrawImage(FT_Bitmap *source, ULong_t fore, ULong_t back, //////////////////////////////////////////////////////////////////////////////// /// Draw text using TrueType fonts. If TrueType fonts are not available the -/// text is drawn with TGX11::DrawText. +/// text is drawn with TGX11::DrawTextW. -void TGX11TTF::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, - const char *text, ETextMode mode) +void TGX11TTF::DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, + const char *text, ETextMode mode) { if (!fHasTTFonts) { - TGX11::DrawText(x, y, angle, mgn, text, mode); + TGX11::DrawTextW(wctxt, x, y, angle, mgn, text, mode); } else { if (!TTF::fgInit) TTF::Init(); TTF::SetRotationMatrix(angle); TTF::PrepareString(text); TTF::LayoutGlyphs(); - Align(); - RenderString(x, y, mode); + Align(wctxt); + RenderString(wctxt, x, y, mode); } } //////////////////////////////////////////////////////////////////////////////// /// Draw text using TrueType fonts. If TrueType fonts are not available the -/// text is drawn with TGX11::DrawText. +/// text is drawn with TGX11::DrawTextW. -void TGX11TTF::DrawText(Int_t x, Int_t y, Float_t angle, Float_t mgn, - const wchar_t *text, ETextMode mode) +void TGX11TTF::DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, + const wchar_t *text, ETextMode mode) { if (!fHasTTFonts) { - TGX11::DrawText(x, y, angle, mgn, text, mode); + TGX11::DrawTextW(wctxt, x, y, angle, mgn, text, mode); } else { if (!TTF::fgInit) TTF::Init(); TTF::SetRotationMatrix(angle); TTF::PrepareString(text); TTF::LayoutGlyphs(); - Align(); - RenderString(x, y, mode); + Align(wctxt); + RenderString(wctxt, x, y, mode); } } //////////////////////////////////////////////////////////////////////////////// /// Get the background of the current window in an XImage. -RXImage *TGX11TTF::GetBackground(Int_t x, Int_t y, UInt_t w, UInt_t h) +RXImage *TGX11TTF::GetBackground(WinContext_t wctxt, Int_t x, Int_t y, UInt_t w, UInt_t h) { - Window_t cws = GetCurrentWindow(); + Window_t cws = GetWindow(wctxt); UInt_t width; UInt_t height; Int_t xy; - gVirtualX->GetWindowSize(cws, xy, xy, width, height); + GetWindowSize(cws, xy, xy, width, height); if (x < 0) { w += x; @@ -420,32 +419,37 @@ RXImage *TGX11TTF::GetBackground(Int_t x, Int_t y, UInt_t w, UInt_t h) if (x+w > width) w = width - x; if (y+h > height) h = height - y; - return (RXImage*)XGetImage((Display*)fDisplay, cws, x, y, w, h, AllPlanes, ZPixmap); + return (RXImage *)XGetImage((Display*)fDisplay, cws, x, y, w, h, AllPlanes, ZPixmap); } //////////////////////////////////////////////////////////////////////////////// /// Test if there is really something to render. -Bool_t TGX11TTF::IsVisible(Int_t x, Int_t y, UInt_t w, UInt_t h) +Bool_t TGX11TTF::IsVisible(WinContext_t wctxt, Int_t x, Int_t y, UInt_t w, UInt_t h) { - Window_t cws = GetCurrentWindow(); + Window_t cws = GetWindow(wctxt); UInt_t width; UInt_t height; Int_t xy; - gVirtualX->GetWindowSize(cws, xy, xy, width, height); + GetWindowSize(cws, xy, xy, width, height); // If w or h is 0, very likely the string is only blank characters - if ((int)w == 0 || (int)h == 0) return kFALSE; + if ((int)w == 0 || (int)h == 0) + return kFALSE; // If string falls outside window, there is probably no need to draw it. - if (x + (int)w <= 0 || x >= (int)width) return kFALSE; - if (y + (int)h <= 0 || y >= (int)height) return kFALSE; + if (x + (int)w <= 0 || x >= (int)width) + return kFALSE; + if (y + (int)h <= 0 || y >= (int)height) + return kFALSE; // If w or h are much larger than the window size, there is probably no need // to draw it. Moreover a to large text size may produce a Seg Fault in // malloc in RenderString. - if (w > 10*width) return kFALSE; - if (h > 10*height) return kFALSE; + if (w > 10*width) + return kFALSE; + if (h > 10*height) + return kFALSE; return kTRUE; } @@ -454,7 +458,7 @@ Bool_t TGX11TTF::IsVisible(Int_t x, Int_t y, UInt_t w, UInt_t h) /// Perform the string rendering in the pad. /// LayoutGlyphs should have been called before. -void TGX11TTF::RenderString(Int_t x, Int_t y, ETextMode mode) +void TGX11TTF::RenderString(WinContext_t wctxt, Int_t x, Int_t y, ETextMode mode) { TTF::TTGlyph* glyph = TTF::fgGlyphs; @@ -466,7 +470,8 @@ void TGX11TTF::RenderString(Int_t x, Int_t y, ETextMode mode) Int_t x1 = x-Xoff-fAlign.x; Int_t y1 = y+Yoff+fAlign.y-h; - if (!IsVisible(x1, y1, w, h)) return; + if (!IsVisible(wctxt, x1, y1, w, h)) + return; // create the XImage that will contain the text UInt_t depth = fDepth; @@ -482,9 +487,9 @@ void TGX11TTF::RenderString(Int_t x, Int_t y, ETextMode mode) ULong_t bg; XGCValues values; - GC *gc = (GC*)GetGC(3); + auto gc = (GC *) GetGCW(wctxt, 3); if (!gc) { - Error("DrawText", "error getting Graphics Context"); + Error("RenderString", "error getting Graphics Context"); return; } XGetGCValues((Display*)fDisplay, *gc, GCForeground | GCBackground, &values); @@ -492,7 +497,7 @@ void TGX11TTF::RenderString(Int_t x, Int_t y, ETextMode mode) // get the background if (mode == kClear) { // if mode == kClear we need to get an image of the background - XImage *bim = GetBackground(x1, y1, w, h); + XImage *bim = GetBackground(wctxt, x1, y1, w, h); if (!bim) { Error("DrawText", "error getting background image"); return; @@ -534,22 +539,25 @@ void TGX11TTF::RenderString(Int_t x, Int_t y, ETextMode mode) } // put the Ximage on the screen - Window_t cws = GetCurrentWindow(); - gc = (GC*)GetGC(6); - if (gc) XPutImage((Display*)fDisplay, cws, *gc, xim, 0, 0, x1, y1, w, h); + Window_t cws = GetWindow(wctxt); + gc = (GC *) GetGCW(wctxt, 6); + if (gc) + XPutImage((Display*)fDisplay, cws, *gc, xim, 0, 0, x1, y1, w, h); XDestroyImage(xim); } //////////////////////////////////////////////////////////////////////////////// /// Set specified font. -void TGX11TTF::SetTextFont(Font_t fontnumber) +void TGX11TTF::SetAttText(WinContext_t wctxt, const TAttText &att) { - fTextFont = fontnumber; - if (!fHasTTFonts) { - TGX11::SetTextFont(fontnumber); - } else { - TTF::SetTextFont(fontnumber); + // TODO: add to window context custom part for TTF, + // it can be allocated and provided via private interface + TGX11::SetAttText(wctxt, att); + + if (fHasTTFonts) { + TTF::SetTextFont(att.GetTextFont()); + TTF::SetTextSize(att.GetTextSize()); } } @@ -572,19 +580,6 @@ Int_t TGX11TTF::SetTextFont(char *fontname, ETextSetMode mode) } } -//////////////////////////////////////////////////////////////////////////////// -/// Set current text size. - -void TGX11TTF::SetTextSize(Float_t textsize) -{ - fTextSize = textsize; - if (!fHasTTFonts) { - TGX11::SetTextSize(textsize); - } else { - TTF::SetTextSize(textsize); - } -} - #ifdef R__HAS_XFT ///////////////////////////// Xft font methods ///////////////////////////////// diff --git a/graf3d/ftgl/CMakeLists.txt b/graf3d/ftgl/CMakeLists.txt index 92ee0bb663648..38bd2046db022 100644 --- a/graf3d/ftgl/CMakeLists.txt +++ b/graf3d/ftgl/CMakeLists.txt @@ -50,4 +50,8 @@ target_include_directories(FTGL PRIVATE ${CMAKE_SOURCE_DIR}/graf3d/gl/inc ) +if(builtin_freetype) + add_dependencies(FTGL BUILTIN_FREETYPE) +endif() + ROOT_INSTALL_HEADERS() diff --git a/graf3d/gl/CMakeLists.txt b/graf3d/gl/CMakeLists.txt index 4bf49d6d07f08..4fb6341aa7d8a 100644 --- a/graf3d/gl/CMakeLists.txt +++ b/graf3d/gl/CMakeLists.txt @@ -225,7 +225,6 @@ target_include_directories(RGL PRIVATE ${OPENGL_INCLUDE_DIR} ${FTGL_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS} - ${GLEW_INCLUDE_DIRS} ) if(builtin_gl2ps) diff --git a/graf3d/gl/inc/TGLPadPainter.h b/graf3d/gl/inc/TGLPadPainter.h index d09646ed9c705..5ee513c53d0e7 100644 --- a/graf3d/gl/inc/TGLPadPainter.h +++ b/graf3d/gl/inc/TGLPadPainter.h @@ -16,6 +16,7 @@ #include "TGLFontManager.h" #include "TGLPadUtils.h" #include "TPoint.h" +#include "GuiTypes.h" #include @@ -32,6 +33,8 @@ class TGLPadPainter : public TVirtualPadPainter { Rgl::Pad::MarkerPainter fMarker; Rgl::Pad::GLLimits fLimits; + WinContext_t fWinContext; // context of selected drawable + std::vector fVs;//Vertex buffer for tesselator. TGLFontManager fFM; @@ -100,6 +103,8 @@ class TGLPadPainter : public TVirtualPadPainter { void CopyDrawable(Int_t device, Int_t px, Int_t py) override; void DestroyDrawable(Int_t device) override; void SelectDrawable(Int_t device) override; + void UpdateDrawable(Int_t mode) override; + void SetDrawMode(Int_t device, Int_t mode) override; void InitPainter() override; void InvalidateCS() override; @@ -138,6 +143,8 @@ class TGLPadPainter : public TVirtualPadPainter { Bool_t IsCocoa() const override; + Bool_t IsSupportAlpha() const override { return kTRUE; } + private: //Attention! GL_PROJECTION will become diff --git a/graf3d/gl/src/TGLFontManager.cxx b/graf3d/gl/src/TGLFontManager.cxx index 58ea1188d5936..a5a708b861462 100644 --- a/graf3d/gl/src/TGLFontManager.cxx +++ b/graf3d/gl/src/TGLFontManager.cxx @@ -200,7 +200,7 @@ void TGLFont::RenderHelper(const Char *txt, Double_t x, Double_t y, Double_t ang //later gVirtualX->GetTextAling() will give you 7. Brilliant! //But with Cocoa you'll have 11. As it should be, of course. - if (gVirtualX->InheritsFrom("TGCocoa")) { + if (gVirtualX->InheritsFrom("TGCocoa") || true) { const UInt_t hAlign = UInt_t(align / 10); switch (hAlign) { case 1: diff --git a/graf3d/gl/src/TGLPadPainter.cxx b/graf3d/gl/src/TGLPadPainter.cxx index 61d88d7a9e2cf..915434d6ac958 100644 --- a/graf3d/gl/src/TGLPadPainter.cxx +++ b/graf3d/gl/src/TGLPadPainter.cxx @@ -21,6 +21,7 @@ #include "TImage.h" #include "TROOT.h" #include "TPad.h" +#include "TCanvas.h" #include "TColorGradient.h" #include "TGLPadPainter.h" @@ -61,6 +62,7 @@ TGLPadPainter::TGLPadPainter() fLocked(kTRUE) { fVp[0] = fVp[1] = fVp[2] = fVp[3] = 0; + fWinContext = 0; } @@ -363,6 +365,7 @@ void TGLPadPainter::DestroyDrawable(Int_t /* device */) { // gVirtualX->SelectWindow(device); // gVirtualX->ClosePixmap(); + fWinContext = 0; } //////////////////////////////////////////////////////////////////////////////// @@ -371,12 +374,16 @@ void TGLPadPainter::DestroyDrawable(Int_t /* device */) ///this pixmap. For OpenGL this means the change of ///coordinate system and viewport. -void TGLPadPainter::SelectDrawable(Int_t /*device*/) +void TGLPadPainter::SelectDrawable(Int_t /* device */) { if (fLocked) return; if (TPad *pad = dynamic_cast(gPad)) { + // GL painter does not use proper id for sub-pads (see CreateDrawable) + // so one always use canvas ID to execute TVirtualX-specific commands + fWinContext = gVirtualX->GetWindowContext(pad->GetCanvasID()); + Int_t px = 0, py = 0; pad->XYtoAbsPixel(pad->GetX1(), pad->GetY1(), px, py); @@ -404,6 +411,28 @@ void TGLPadPainter::SelectDrawable(Int_t /*device*/) } } +//////////////////////////////////////////////////////////////////////////////// +/// Call low-level update of selected drawable, redirect to gVirtualX. + +void TGLPadPainter::UpdateDrawable(Int_t mode) +{ + if (fWinContext) + gVirtualX->UpdateWindowW(fWinContext, mode); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// Set drawing mode for specified device + +void TGLPadPainter::SetDrawMode(Int_t device, Int_t mode) +{ + auto ctxt = fWinContext; + if (device) + ctxt = gVirtualX->GetWindowContext(device); + if (ctxt) + gVirtualX->SetDrawModeW(ctxt, (TVirtualX::EDrawMode) mode); +} + //////////////////////////////////////////////////////////////////////////////// ///Init gl-pad painter: ///1. 2D painter does not use depth test, should not modify @@ -495,8 +524,9 @@ void TGLPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2) //from TView3D::ExecuteRotateView. This means in fact, //that TView3D wants to draw itself in a XOR mode, via //gVirtualX. - if (gVirtualX->GetDrawMode() == TVirtualX::kInvert) { - gVirtualX->DrawLine(gPad->XtoAbsPixel(x1), gPad->YtoAbsPixel(y1), + if (fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert)) { + gVirtualX->DrawLineW(fWinContext, + gPad->XtoAbsPixel(x1), gPad->YtoAbsPixel(y1), gPad->XtoAbsPixel(x2), gPad->YtoAbsPixel(y2)); } @@ -533,7 +563,17 @@ void TGLPadPainter::DrawLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2) void TGLPadPainter::DrawLineNDC(Double_t u1, Double_t v1, Double_t u2, Double_t v2) { - if (fLocked) return; + if (fLocked) { + // this code used when crosshair cursor is drawn + if (fWinContext && (gVirtualX->GetDrawModeW(fWinContext) == TVirtualX::kInvert)) { + const Int_t px1 = gPad->UtoPixel(u1); + const Int_t py1 = gPad->VtoPixel(v1); + const Int_t px2 = gPad->UtoPixel(u2); + const Int_t py2 = gPad->VtoPixel(v2); + gVirtualX->DrawLineW(fWinContext, px1, py1, px2, py2); + } + return; + } const Rgl::Pad::LineAttribSet lineAttribs(kTRUE, gVirtualX->GetLineStyle(), fLimits.GetMaxLineWidth(), kTRUE); const Double_t xRange = gPad->GetX2() - gPad->GetX1(); @@ -1059,11 +1099,11 @@ void TGLPadPainter::RestoreViewport() void TGLPadPainter::SaveImage(TVirtualPad *pad, const char *fileName, Int_t type) const { - TVirtualPad *canvas = (TVirtualPad *)pad->GetCanvas(); + auto canvas = pad->GetCanvas(); if (!canvas) return; - gROOT->ProcessLine(Form("((TCanvas *)0x%zx)->Flush();", (size_t)canvas)); + canvas->Flush(); std::vector buff(canvas->GetWw() * canvas->GetWh()); glPixelStorei(GL_PACK_ALIGNMENT, 1); diff --git a/graf3d/gl/src/TGLWSIncludes.h b/graf3d/gl/src/TGLWSIncludes.h index 6ea691ba915c5..d8261874e3d38 100644 --- a/graf3d/gl/src/TGLWSIncludes.h +++ b/graf3d/gl/src/TGLWSIncludes.h @@ -20,9 +20,6 @@ #if defined(WIN32) #include #else -#if defined(__APPLE__) && !defined(R__HAS_COCOA) -#define GLEW_APPLE_GLX -#endif #if !defined(R__HAS_COCOA) #include #endif diff --git a/gui/gui/src/TRootCanvas.cxx b/gui/gui/src/TRootCanvas.cxx index fdf1c3005d5c9..db1bcf4b7ae84 100644 --- a/gui/gui/src/TRootCanvas.cxx +++ b/gui/gui/src/TRootCanvas.cxx @@ -1745,7 +1745,7 @@ Bool_t TRootCanvas::HandleContainerButton(Event_t *event) if (event->fType == kButtonPress) { if (fToolTip && fCanvas->GetShowToolTips()) { fToolTip->Hide(); - gVirtualX->UpdateWindow(0); + gVirtualX->UpdateWindowW(gVirtualX->GetWindowContext(fCanvasID), 0); gSystem->ProcessEvents(); } fButton = button; diff --git a/gui/webgui6/inc/TWebPadPainter.h b/gui/webgui6/inc/TWebPadPainter.h index 64a0bafd5fee7..0c4dd9310c0de 100644 --- a/gui/webgui6/inc/TWebPadPainter.h +++ b/gui/webgui6/inc/TWebPadPainter.h @@ -125,6 +125,8 @@ friend class TWebCanvas; void DrawTextUrl(Double_t x, Double_t y, const char *text, const char *url) override; + Bool_t IsSupportAlpha() const override { return kTRUE; } + private: //Let's make this clear: TWebPadPainter(const TWebPadPainter &rhs) = delete; diff --git a/hist/histv7/doc/CodeArchitecture.md b/hist/histv7/doc/CodeArchitecture.md index 15bd648dc7291..0cb28176b5150 100644 --- a/hist/histv7/doc/CodeArchitecture.md +++ b/hist/histv7/doc/CodeArchitecture.md @@ -79,7 +79,7 @@ Each instance has a local `RHistStats` object to avoid contention on the global A single bin index, which is just an integer for normal bins. `Underflow()` and `Overflow()` are special values and not ordered with respect to others. -Objects of this type are passed by value; most notably to `GetBinContent`. +Objects of this type are passed by value; most notably to `GetBinContent` and `SetBinContent`. ### `RBinIndexRange` diff --git a/hist/histv7/doc/DesignImplementation.md b/hist/histv7/doc/DesignImplementation.md index e58b776d89ac2..836abf60c9b90 100644 --- a/hist/histv7/doc/DesignImplementation.md +++ b/hist/histv7/doc/DesignImplementation.md @@ -58,10 +58,12 @@ Special arguments are passed last. For example ```cpp template void Fill(const std::tuple &args, RWeight w); +template void SetBinContent(const std::array &indices, const V &value); ``` -The same works for the variadic function templates that will check the type of the last argument. +Note that we accept mandatory arguments with a template type as well to allow automatic conversion. -For profiles, we accept the value with a template type as well to allow automatic conversion to `double`, for example from `int`. +Variadic function templates receive all arguments in a single function parameter pack. +For optional arguments, the function will check the type of the last argument to determine if it was passed. ## Miscellaneous diff --git a/hist/histv7/inc/ROOT/RAxisVariant.hxx b/hist/histv7/inc/ROOT/RAxisVariant.hxx index e912bd35dbaed..9884003460c6e 100644 --- a/hist/histv7/inc/ROOT/RAxisVariant.hxx +++ b/hist/histv7/inc/ROOT/RAxisVariant.hxx @@ -127,6 +127,26 @@ public: } } + /// Slice this axis according to the specification. + /// + /// Axes throw exceptions if the slicing cannot be performed. For example, the rebin operation must divide the + /// number of normal bins for RRegularAxis and RVariableBinAxis, while RCategoricalAxis cannot be sliced at all. + /// + /// \param[in] sliceSpec the slice specification + /// \return the sliced axis + RAxisVariant Slice(const RSliceSpec &sliceSpec) const + { + if (auto *regular = GetRegularAxis()) { + return regular->Slice(sliceSpec); + } else if (auto *variable = GetVariableBinAxis()) { + return variable->Slice(sliceSpec); + } else if (auto *categorical = GetCategoricalAxis()) { + return categorical->Slice(sliceSpec); + } else { + throw std::logic_error("unimplemented axis type"); // GCOVR_EXCL_LINE + } + } + friend bool operator==(const RAxisVariant &lhs, const RAxisVariant &rhs) { return lhs.fVariant == rhs.fVariant; } /// %ROOT Streamer function to throw when trying to store an object of this class. diff --git a/hist/histv7/inc/ROOT/RCategoricalAxis.hxx b/hist/histv7/inc/ROOT/RCategoricalAxis.hxx index 60bb4fe69c4f7..5cb161562f6bb 100644 --- a/hist/histv7/inc/ROOT/RCategoricalAxis.hxx +++ b/hist/histv7/inc/ROOT/RCategoricalAxis.hxx @@ -8,6 +8,7 @@ #include "RBinIndex.hxx" #include "RBinIndexRange.hxx" #include "RLinearizedIndex.hxx" +#include "RSliceSpec.hxx" #include #include @@ -167,6 +168,31 @@ public: : GetNormalRange(); } + /// Slice this axis according to the specification. + /// + /// A categorical axis cannot be sliced. The method will throw if a specification other than the default slice + /// operation is passed. + /// + /// \param[in] sliceSpec the slice specification + /// \return the sliced / copied axis + RCategoricalAxis Slice(const RSliceSpec &sliceSpec) const + { + if (sliceSpec.GetOperationSum() != nullptr) { + throw std::runtime_error("sum operation makes dimension disappear"); + } + + if (!sliceSpec.GetRange().IsInvalid()) { + throw std::runtime_error("slicing of RCategoricalAxis not implemented"); + } + if (sliceSpec.GetOperationRebin() != nullptr) { + throw std::runtime_error("cannot rebin RCategoricalAxis"); + } + + // The sliced axis always has flow bins enabled, for symmetry with other axis types. + bool enableOverflowBin = true; + return RCategoricalAxis(fCategories, enableOverflowBin); + } + /// %ROOT Streamer function to throw when trying to store an object of this class. void Streamer(TBuffer &) { throw std::runtime_error("unable to store RCategoricalAxis"); } }; diff --git a/hist/histv7/inc/ROOT/RHist.hxx b/hist/histv7/inc/ROOT/RHist.hxx index 9054e00b91e6e..10fa9cc679782 100644 --- a/hist/histv7/inc/ROOT/RHist.hxx +++ b/hist/histv7/inc/ROOT/RHist.hxx @@ -238,6 +238,64 @@ public: /// \return the multidimensional range RBinIndexMultiDimRange GetFullMultiDimRange() const { return fEngine.GetFullMultiDimRange(); } + /// Set the content of a single bin. + /// + /// \code + /// ROOT::Experimental::RHist hist({/* two dimensions */}); + /// std::array indices = {3, 5}; + /// int value = /* ... */; + /// hist.SetBinContent(indices, value); + /// \endcode + /// + /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special + /// values. See also the class documentation of RBinIndex. + /// + /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found. + /// + /// \warning Setting the bin content will taint the global histogram statistics. Attempting to access its values, for + /// example calling GetNEntries(), will throw exceptions. + /// + /// \param[in] indices the array of indices for each axis + /// \param[in] value the new value of the bin content + /// \par See also + /// the \ref SetBinContent(const A &... args) const "variadic function template overload" accepting arguments + /// directly + template + void SetBinContent(const std::array &indices, const V &value) + { + fEngine.SetBinContent(indices, value); + fStats.Taint(); + } + + /// Set the content of a single bin. + /// + /// \code + /// ROOT::Experimental::RHist hist({/* two dimensions */}); + /// int value = /* ... */; + /// hist.SetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5), value); + /// // ... or construct the RBinIndex arguments implicitly from integers: + /// hist.SetBinContent(3, 5, value); + /// \endcode + /// + /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special + /// values. See also the class documentation of RBinIndex. + /// + /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found. + /// + /// \warning Setting the bin content will taint the global histogram statistics. Attempting to access its values, for + /// example calling GetNEntries(), will throw exceptions. + /// + /// \param[in] args the arguments for each axis and the new value of the bin content + /// \par See also + /// the \ref SetBinContent(const std::array &indices, const V &value) const "function overload" + /// accepting `std::array` + template + void SetBinContent(const A &...args) + { + fEngine.SetBinContent(args...); + fStats.Taint(); + } + /// Add all bin contents and statistics of another histogram. /// /// Throws an exception if the axes configurations are not identical. @@ -393,6 +451,115 @@ public: fStats.Scale(factor); } + /// Slice this histogram with an RSliceSpec per dimension. + /// + /// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and + /// overflow bins: + /// \code + /// ROOT::Experimental::RHist hist(/* one dimension */); + /// // Fill the histogram with a number of entries... + /// auto sliced = hist.Slice({hist.GetAxes()[0].GetNormalRange(1, 5)}); + /// // The returned histogram will have 4 normal bins, an underflow and an overflow bin. + /// \endcode + /// + /// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin + /// the histogram axis, grouping a number of normal bins into a new one: + /// \code + /// ROOT::Experimental::RHist hist(/* one dimension */); + /// // Fill the histogram with a number of entries... + /// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2)); + /// // The returned histogram has groups of two normal bins merged. + /// \endcode + /// + /// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional + /// histogram: + /// \code + /// ROOT::Experimental::RHist hist({/* two dimensions */}); + /// // Fill the histogram with a number of entries... + /// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{}); + /// // The returned histogram has one dimension, with bin contents summed along the second axis. + /// \endcode + /// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar. + /// + /// Ranges and operations can be combined. In that case, the range is applied before the operation. + /// + /// \warning Combining a range and the sum operation drops bin contents, which will taint the global histogram + /// statistics. Attempting to access its values, for example calling GetNEntries(), will throw exceptions. + /// + /// \param[in] sliceSpecs the slice specifications for each axis + /// \return the sliced histogram + /// \par See also + /// the \ref Slice(const A &... args) const "variadic function template overload" accepting arguments directly + RHist Slice(const std::vector &sliceSpecs) const + { + bool dropped = false; + RHist sliced(fEngine.SliceImpl(sliceSpecs, dropped)); + assert(sliced.fStats.GetNDimensions() == sliced.GetNDimensions()); + if (dropped || fStats.IsTainted()) { + sliced.fStats.Taint(); + } else { + sliced.fStats.fNEntries = fStats.fNEntries; + sliced.fStats.fSumW = fStats.fSumW; + sliced.fStats.fSumW2 = fStats.fSumW2; + std::size_t slicedDim = 0; + for (std::size_t i = 0; i < sliceSpecs.size(); i++) { + // A sum operation makes the dimension disappear. + if (sliceSpecs[i].GetOperationSum() == nullptr) { + sliced.fStats.fDimensionStats[slicedDim] = fStats.fDimensionStats[i]; + slicedDim++; + } + } + assert(slicedDim == sliced.GetNDimensions()); + } + return sliced; + } + + /// Slice this histogram with an RSliceSpec per dimension. + /// + /// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and + /// overflow bins: + /// \code + /// ROOT::Experimental::RHist hist(/* one dimension */); + /// // Fill the histogram with a number of entries... + /// auto sliced = hist.Slice(hist.GetAxes()[0].GetNormalRange(1, 5)); + /// // The returned histogram will have 4 normal bins, an underflow and an overflow bin. + /// \endcode + /// + /// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin + /// the histogram axis, grouping a number of normal bins into a new one: + /// \code + /// ROOT::Experimental::RHist hist(/* one dimension */); + /// // Fill the histogram with a number of entries... + /// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2)); + /// // The returned histogram has groups of two normal bins merged. + /// \endcode + /// + /// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional + /// histogram: + /// \code + /// ROOT::Experimental::RHist hist({/* two dimensions */}); + /// // Fill the histogram with a number of entries... + /// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{}); + /// // The returned histogram has one dimension, with bin contents summed along the second axis. + /// \endcode + /// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar. + /// + /// Ranges and operations can be combined. In that case, the range is applied before the operation. + /// + /// \warning Combining a range and the sum operation drops bin contents, which will taint the global histogram + /// statistics. Attempting to access its values, for example calling GetNEntries(), will throw exceptions. + /// + /// \param[in] args the arguments for each axis + /// \return the sliced histogram + /// \par See also + /// the \ref Slice(const std::vector &sliceSpecs) const "function overload" accepting `std::vector` + template + RHist Slice(const A &...args) const + { + std::vector sliceSpecs{args...}; + return Slice(sliceSpecs); + } + /// %ROOT Streamer function to throw when trying to store an object of this class. void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHist"); } }; diff --git a/hist/histv7/inc/ROOT/RHistEngine.hxx b/hist/histv7/inc/ROOT/RHistEngine.hxx index abd37de3274cc..f04fd03304be8 100644 --- a/hist/histv7/inc/ROOT/RHistEngine.hxx +++ b/hist/histv7/inc/ROOT/RHistEngine.hxx @@ -12,6 +12,8 @@ #include "RHistUtils.hxx" #include "RLinearizedIndex.hxx" #include "RRegularAxis.hxx" +#include "RSliceBinIndexMapper.hxx" +#include "RSliceSpec.hxx" #include "RWeight.hxx" #include @@ -29,13 +31,9 @@ class TBuffer; namespace ROOT { namespace Experimental { -// forward declarations for friend declaration -template -class RHistEngine; -namespace Internal { -template -static void SetBinContent(RHistEngine &hist, const std::array &indices, const T &value); -} // namespace Internal +// forward declaration for friend declaration +template +class RHist; /** A histogram data structure to bin data along multiple dimensions. @@ -73,8 +71,8 @@ class RHistEngine final { template friend class RHistEngine; - template - friend void Internal::SetBinContent(RHistEngine &, const std::array &, const T &); + // For slicing, RHist needs to call SliceImpl. + friend class RHist; /// The axis configuration for this histogram. Relevant methods are forwarded from the public interface. Internal::RAxes fAxes; @@ -247,6 +245,78 @@ public: /// \return the multidimensional range RBinIndexMultiDimRange GetFullMultiDimRange() const { return fAxes.GetFullMultiDimRange(); } + /// Set the content of a single bin. + /// + /// \code + /// ROOT::Experimental::RHistEngine hist({/* two dimensions */}); + /// std::array indices = {3, 5}; + /// int value = /* ... */; + /// hist.SetBinContent(indices, value); + /// \endcode + /// + /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special + /// values. See also the class documentation of RBinIndex. + /// + /// Throws an exception if the number of indices does not match the axis configuration or the bin is not found. + /// + /// \param[in] indices the array of indices for each axis + /// \param[in] value the new value of the bin content + /// \par See also + /// the \ref SetBinContent(const A &... args) const "variadic function template overload" accepting arguments + /// directly + template + void SetBinContent(const std::array &indices, const V &value) + { + // We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might + // be confusing for users. + if (N != GetNDimensions()) { + throw std::invalid_argument("invalid number of indices passed to SetBinContent"); + } + RLinearizedIndex index = fAxes.ComputeGlobalIndex(indices); + if (!index.fValid) { + throw std::invalid_argument("bin not found in SetBinContent"); + } + assert(index.fIndex < fBinContents.size()); + // To allow conversion, we have to accept value with a template type V to capture any argument. Otherwise it would + // select the variadic function template... + fBinContents[index.fIndex] = value; + } + +private: + template + void SetBinContentImpl(const std::tuple &args, std::index_sequence) + { + std::array indices{std::get(args)...}; + SetBinContent(indices, std::get(args)); + } + +public: + /// Set the content of a single bin. + /// + /// \code + /// ROOT::Experimental::RHistEngine hist({/* two dimensions */}); + /// int value = /* ... */; + /// hist.SetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5), value); + /// // ... or construct the RBinIndex arguments implicitly from integers: + /// hist.SetBinContent(3, 5, value); + /// \endcode + /// + /// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special + /// values. See also the class documentation of RBinIndex. + /// + /// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found. + /// + /// \param[in] args the arguments for each axis and the new value of the bin content + /// \par See also + /// the \ref SetBinContent(const std::array &indices, const V &value) const "function overload" + /// accepting `std::array` + template + void SetBinContent(const A &...args) + { + auto t = std::forward_as_tuple(args...); + SetBinContentImpl(t, std::make_index_sequence()); + } + /// Add all bin contents of another histogram. /// /// Throws an exception if the axes configurations are not identical. @@ -578,24 +648,148 @@ public: } } +private: + RHistEngine SliceImpl(const std::vector &sliceSpecs, bool &dropped) const + { + if (sliceSpecs.size() != GetNDimensions()) { + throw std::invalid_argument("invalid number of specifications passed to Slice"); + } + + // Slice the axes. + std::vector axes; + for (std::size_t i = 0; i < sliceSpecs.size(); i++) { + // A sum operation makes the dimension disappear. + if (sliceSpecs[i].GetOperationSum() == nullptr) { + axes.push_back(fAxes.Get()[i].Slice(sliceSpecs[i])); + } + } + if (axes.empty()) { + throw std::invalid_argument("summing across all dimensions is not supported"); + } + + RHistEngine sliced(std::move(axes)); + + // Create the helper objects to map the bin contents to the sliced histogram. + Internal::RSliceBinIndexMapper mapper(sliceSpecs); + assert(mapper.GetMappedDimensionality() == sliced.GetNDimensions()); + std::vector mappedIndices(mapper.GetMappedDimensionality()); + + auto origRange = fAxes.GetFullMultiDimRange(); + auto origRangeIt = origRange.begin(); + + for (std::size_t i = 0; i < fBinContents.size(); i++) { + const auto &origIndices = *origRangeIt; +#ifndef NDEBUG + // Verify that the original indices correspond to the iteration variable. + RLinearizedIndex origIndex = fAxes.ComputeGlobalIndex(origIndices); + assert(origIndex.fValid); + assert(origIndex.fIndex == i); +#endif + + bool success = mapper.Map(origIndices, mappedIndices); + if (success) { + RLinearizedIndex mappedIndex = sliced.fAxes.ComputeGlobalIndex(mappedIndices); + assert(mappedIndex.fValid); + sliced.fBinContents[mappedIndex.fIndex] += fBinContents[i]; + } else { + dropped = true; + } + ++origRangeIt; + } + + return sliced; + } + +public: + /// Slice this histogram with an RSliceSpec per dimension. + /// + /// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and + /// overflow bins: + /// \code + /// ROOT::Experimental::RHistEngine hist(/* one dimension */); + /// // Fill the histogram with a number of entries... + /// auto sliced = hist.Slice({hist.GetAxes()[0].GetNormalRange(1, 5)}); + /// // The returned histogram will have 4 normal bins, an underflow and an overflow bin. + /// \endcode + /// + /// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin + /// the histogram axis, grouping a number of normal bins into a new one: + /// \code + /// ROOT::Experimental::RHistEngine hist(/* one dimension */); + /// // Fill the histogram with a number of entries... + /// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2)); + /// // The returned histogram has groups of two normal bins merged. + /// \endcode + /// + /// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional + /// histogram: + /// \code + /// ROOT::Experimental::RHistEngine hist({/* two dimensions */}); + /// // Fill the histogram with a number of entries... + /// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{}); + /// // The returned histogram has one dimension, with bin contents summed along the second axis. + /// \endcode + /// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar. + /// + /// Ranges and operations can be combined. In that case, the range is applied before the operation. + /// + /// \param[in] sliceSpecs the slice specifications for each axis + /// \return the sliced histogram + /// \par See also + /// the \ref Slice(const A &... args) const "variadic function template overload" accepting arguments directly + RHistEngine Slice(const std::vector &sliceSpecs) const + { + bool dropped = false; + return SliceImpl(sliceSpecs, dropped); + } + + /// Slice this histogram with an RSliceSpec per dimension. + /// + /// With a range, only the specified bins are retained. All other bin contents are transferred to the underflow and + /// overflow bins: + /// \code + /// ROOT::Experimental::RHistEngine hist(/* one dimension */); + /// // Fill the histogram with a number of entries... + /// auto sliced = hist.Slice(hist.GetAxes()[0].GetNormalRange(1, 5)); + /// // The returned histogram will have 4 normal bins, an underflow and an overflow bin. + /// \endcode + /// + /// Slicing can also perform operations per dimension, see RSliceSpec. RSliceSpec::ROperationRebin allows to rebin + /// the histogram axis, grouping a number of normal bins into a new one: + /// \code + /// ROOT::Experimental::RHistEngine hist(/* one dimension */); + /// // Fill the histogram with a number of entries... + /// auto rebinned = hist.Slice(ROOT::Experimental::RSliceSpec::ROperationRebin(2)); + /// // The returned histogram has groups of two normal bins merged. + /// \endcode + /// + /// RSliceSpec::ROperationSum sums the bin contents along that axis, which allows to project to a lower-dimensional + /// histogram: + /// \code + /// ROOT::Experimental::RHistEngine hist({/* two dimensions */}); + /// // Fill the histogram with a number of entries... + /// auto projected = hist.Slice(ROOT::Experimental::RSliceSpec{}, ROOT::Experimental::RSliceSpec::ROperationSum{}); + /// // The returned histogram has one dimension, with bin contents summed along the second axis. + /// \endcode + /// Note that it is not allowed to sum along all histogram axes because the return value would be a scalar. + /// + /// Ranges and operations can be combined. In that case, the range is applied before the operation. + /// + /// \param[in] args the arguments for each axis + /// \return the sliced histogram + /// \par See also + /// the \ref Slice(const std::vector &sliceSpecs) const "function overload" accepting `std::vector` + template + RHistEngine Slice(const A &...args) const + { + std::vector sliceSpecs{args...}; + return Slice(sliceSpecs); + } + /// %ROOT Streamer function to throw when trying to store an object of this class. void Streamer(TBuffer &) { throw std::runtime_error("unable to store RHistEngine"); } }; -namespace Internal { -/// %Internal function to set the content of a single bin. -template -static void SetBinContent(RHistEngine &hist, const std::array &indices, const T &value) -{ - RLinearizedIndex index = hist.fAxes.ComputeGlobalIndex(indices); - if (!index.fValid) { - throw std::invalid_argument("bin not found in SetBinContent"); - } - assert(index.fIndex < hist.fBinContents.size()); - hist.fBinContents[index.fIndex] = value; -} -} // namespace Internal - } // namespace Experimental } // namespace ROOT diff --git a/hist/histv7/inc/ROOT/RHistStats.hxx b/hist/histv7/inc/ROOT/RHistStats.hxx index 462de88082aeb..fad20f9054777 100644 --- a/hist/histv7/inc/ROOT/RHistStats.hxx +++ b/hist/histv7/inc/ROOT/RHistStats.hxx @@ -23,6 +23,10 @@ class TBuffer; namespace ROOT { namespace Experimental { +// forward declaration for friend declaration +template +class RHist; + /** Histogram statistics of unbinned values. @@ -39,6 +43,9 @@ stats.Fill(1.5); Feedback is welcome! */ class RHistStats final { + template + friend class RHist; + public: /// Statistics for one dimension. struct RDimensionStats final { @@ -114,6 +121,16 @@ private: double fSumW2 = 0.0; /// The sums per dimension std::vector fDimensionStats; + /// Whether this object is tainted, in which case read access will throw. This is used if a user modifies bin + /// contents explicitly or slices histograms without preserving all entries, for example. + bool fTainted = false; + + void ThrowIfTainted() const + { + if (fTainted) { + throw std::logic_error("statistics are tainted, for example after setting bin contents or slicing"); + } + } public: /// Construct a statistics object. @@ -129,9 +146,21 @@ public: std::size_t GetNDimensions() const { return fDimensionStats.size(); } - std::uint64_t GetNEntries() const { return fNEntries; } - double GetSumW() const { return fSumW; } - double GetSumW2() const { return fSumW2; } + std::uint64_t GetNEntries() const + { + ThrowIfTainted(); + return fNEntries; + } + double GetSumW() const + { + ThrowIfTainted(); + return fSumW; + } + double GetSumW2() const + { + ThrowIfTainted(); + return fSumW2; + } /// Get the statistics object for one dimension. /// @@ -141,6 +170,8 @@ public: /// \return the statistics object const RDimensionStats &GetDimensionStats(std::size_t dim = 0) const { + ThrowIfTainted(); + const RDimensionStats &stats = fDimensionStats.at(dim); if (!stats.fEnabled) { throw std::invalid_argument("dimension is disabled"); @@ -157,6 +188,13 @@ public: bool IsEnabled(std::size_t dim) const { return fDimensionStats.at(dim).fEnabled; } + /// Taint this statistics object. + /// + /// It can still be filled, but any read access will throw until Clear() is called. + void Taint() { fTainted = true; } + + bool IsTainted() const { return fTainted; } + /// Add all entries from another statistics object. /// /// Throws an exception if the number of dimensions are not identical. @@ -164,20 +202,27 @@ public: /// \param[in] other another statistics object void Add(const RHistStats &other) { + // NB: this method does *not* call ThrowIfTainted() to allow adding RHist which may contain a tainted statistics + // object. if (fDimensionStats.size() != other.fDimensionStats.size()) { throw std::invalid_argument("number of dimensions not identical in Add"); } - fNEntries += other.fNEntries; - fSumW += other.fSumW; - fSumW2 += other.fSumW2; + // For exception safety, first check all dimensions before modifying the object. for (std::size_t i = 0; i < fDimensionStats.size(); i++) { if (fDimensionStats[i].fEnabled != other.fDimensionStats[i].fEnabled) { throw std::invalid_argument("the same dimensions must be enabled to combine statistics with Add"); } + } + + fNEntries += other.fNEntries; + fSumW += other.fSumW; + fSumW2 += other.fSumW2; + for (std::size_t i = 0; i < fDimensionStats.size(); i++) { if (fDimensionStats[i].fEnabled) { fDimensionStats[i].Add(other.fDimensionStats[i]); } } + fTainted |= other.fTainted; } /// Add all entries from another statistics object using atomic instructions. @@ -187,20 +232,27 @@ public: /// \param[in] other another statistics object that must not be modified during the operation void AddAtomic(const RHistStats &other) { + // NB: this method does *not* call ThrowIfTainted() to allow adding RHist which may contain a tainted statistics + // object. if (fDimensionStats.size() != other.fDimensionStats.size()) { - throw std::invalid_argument("number of dimensions not identical in Add"); + throw std::invalid_argument("number of dimensions not identical in AddAtomic"); } + // For exception safety, first check all dimensions before modifying the object. + for (std::size_t i = 0; i < fDimensionStats.size(); i++) { + if (fDimensionStats[i].fEnabled != other.fDimensionStats[i].fEnabled) { + throw std::invalid_argument("the same dimensions must be enabled to combine statistics with AddAtomic"); + } + } + Internal::AtomicAdd(&fNEntries, other.fNEntries); Internal::AtomicAdd(&fSumW, other.fSumW); Internal::AtomicAdd(&fSumW2, other.fSumW2); for (std::size_t i = 0; i < fDimensionStats.size(); i++) { - if (fDimensionStats[i].fEnabled != other.fDimensionStats[i].fEnabled) { - throw std::invalid_argument("the same dimensions must be enabled to combine statistics with Add"); - } if (fDimensionStats[i].fEnabled) { fDimensionStats[i].AddAtomic(other.fDimensionStats[i]); } } + fTainted |= other.fTainted; } /// Clear this statistics object. @@ -212,6 +264,7 @@ public: for (std::size_t i = 0; i < fDimensionStats.size(); i++) { fDimensionStats[i].Clear(); } + fTainted = false; } /// Compute the number of effective entries. @@ -223,6 +276,7 @@ public: /// \return the number of effective entries double ComputeNEffectiveEntries() const { + ThrowIfTainted(); if (fSumW2 == 0) { return std::numeric_limits::signaling_NaN(); } @@ -240,7 +294,7 @@ public: double ComputeMean(std::size_t dim = 0) const { // First get the statistics, which includes checking the argument. - auto &stats = fDimensionStats.at(dim); + auto &stats = GetDimensionStats(dim); if (fSumW == 0) { return std::numeric_limits::signaling_NaN(); } @@ -266,7 +320,7 @@ public: double ComputeVariance(std::size_t dim = 0) const { // First get the statistics, which includes checking the argument. - auto &stats = fDimensionStats.at(dim); + auto &stats = GetDimensionStats(dim); if (fSumW == 0) { return std::numeric_limits::signaling_NaN(); } @@ -310,7 +364,7 @@ public: double ComputeSkewness(std::size_t dim = 0) const { // First get the statistics, which includes checking the argument. - auto &stats = fDimensionStats.at(dim); + auto &stats = GetDimensionStats(dim); if (fSumW == 0) { return std::numeric_limits::signaling_NaN(); } @@ -347,7 +401,7 @@ public: double ComputeKurtosis(std::size_t dim = 0) const { // First get the statistics, which includes checking the argument. - auto &stats = fDimensionStats.at(dim); + auto &stats = GetDimensionStats(dim); if (fSumW == 0) { return std::numeric_limits::signaling_NaN(); } @@ -363,6 +417,20 @@ public: } private: + template + void CheckArguments(const std::tuple &args) const + { + using ArgumentType = std::tuple_element_t>; + if (fDimensionStats[I].fEnabled) { + if constexpr (!std::is_convertible_v) { + throw std::invalid_argument("invalid type of argument in RHistStats"); + } + } + if constexpr (I + 1 < N) { + CheckArguments(args); + } + } + template void FillImpl(const std::tuple &args) { @@ -371,7 +439,8 @@ private: if constexpr (std::is_convertible_v) { fDimensionStats[I].Add(std::get(args)); } else { - throw std::invalid_argument("invalid type of argument in RHistStats"); + // Should be checked in CheckArguments above! + assert(0); // GCOVR_EXCL_LINE } } if constexpr (I + 1 < sizeof...(A)) { @@ -389,7 +458,8 @@ private: } else { // Avoid compiler warning about unused parameter... (void)w; - throw std::invalid_argument("invalid type of argument in RHistStats"); + // Should be checked in CheckArguments above! + assert(0); // GCOVR_EXCL_LINE } } if constexpr (I + 1 < N) { @@ -418,6 +488,9 @@ public: if (sizeof...(A) != fDimensionStats.size()) { throw std::invalid_argument("invalid number of arguments to Fill"); } + // For exception safety, first check all arguments before modifying the object. + CheckArguments<0, sizeof...(A)>(args); + fNEntries++; fSumW++; fSumW2++; @@ -443,6 +516,9 @@ public: if (sizeof...(A) != fDimensionStats.size()) { throw std::invalid_argument("invalid number of arguments to Fill"); } + // For exception safety, first check all arguments before modifying the object. + CheckArguments<0, sizeof...(A)>(args); + fNEntries++; double w = weight.fValue; fSumW += w; @@ -479,6 +555,9 @@ public: if (N != fDimensionStats.size()) { throw std::invalid_argument("invalid number of arguments to Fill"); } + // For exception safety, first check all arguments before modifying the object. + CheckArguments<0, N>(t); + fNEntries++; double w = std::get(t).fValue; fSumW += w; @@ -495,6 +574,8 @@ public: /// \param[in] factor the scale factor void Scale(double factor) { + // NB: this method does *not* call ThrowIfTainted() to allow scaling RHist which may contain a tainted statistics + // object. fSumW *= factor; fSumW2 *= factor * factor; for (std::size_t i = 0; i < fDimensionStats.size(); i++) { diff --git a/hist/histv7/inc/ROOT/RRegularAxis.hxx b/hist/histv7/inc/ROOT/RRegularAxis.hxx index 67c27346063f5..449d5ff97a533 100644 --- a/hist/histv7/inc/ROOT/RRegularAxis.hxx +++ b/hist/histv7/inc/ROOT/RRegularAxis.hxx @@ -8,6 +8,7 @@ #include "RBinIndex.hxx" #include "RBinIndexRange.hxx" #include "RLinearizedIndex.hxx" +#include "RSliceSpec.hxx" #include #include @@ -83,6 +84,28 @@ public: double GetHigh() const { return fHigh; } bool HasFlowBins() const { return fEnableFlowBins; } + /// Compute the low edge of a bin. + /// + /// \param[in] bin the index, must be \f$< fNNormalBins\f$ + double ComputeLowEdge(std::uint64_t bin) const + { + if (bin >= fNNormalBins) { + throw std::invalid_argument("bin must be inside the axis"); + } + return fLow + (fHigh - fLow) * bin / fNNormalBins; + } + + /// Compute the high edge of a bin. + /// + /// \param[in] bin the index, must be \f$< fNNormalBins\f$ + double ComputeHighEdge(std::uint64_t bin) const + { + if (bin >= fNNormalBins) { + throw std::invalid_argument("bin must be inside the axis"); + } + return fLow + (fHigh - fLow) * (bin + 1) / fNNormalBins; + } + friend bool operator==(const RRegularAxis &lhs, const RRegularAxis &rhs) { return lhs.fNNormalBins == rhs.fNNormalBins && lhs.fLow == rhs.fLow && lhs.fHigh == rhs.fHigh && @@ -194,6 +217,59 @@ public: : GetNormalRange(); } + /// Slice this axis according to the specification. + /// + /// Throws an exception if the axis cannot be sliced: + /// * A sum operation makes the dimension disappear. + /// * The rebin operation must divide the number of normal bins. + /// + /// \param[in] sliceSpec the slice specification + /// \return the sliced axis, with enabled underflow and overflow bins + RRegularAxis Slice(const RSliceSpec &sliceSpec) const + { + if (sliceSpec.GetOperationSum() != nullptr) { + throw std::runtime_error("sum operation makes dimension disappear"); + } + + // Figure out the properties of the sliced axis. + std::uint64_t nNormalBins = fNNormalBins; + double low = fLow; + double high = fHigh; + + const auto &range = sliceSpec.GetRange(); + if (!range.IsInvalid()) { + std::uint64_t begin = 0; + std::uint64_t end = nNormalBins; + if (range.GetBegin().IsNormal()) { + begin = range.GetBegin().GetIndex(); + // Only compute a new lower end of the axis interval if needed. + if (begin > 0) { + low = ComputeLowEdge(begin); + } + } + if (range.GetEnd().IsNormal()) { + end = range.GetEnd().GetIndex(); + // Only compute a new upper end of the axis interval if needed, to avoid floating-point inaccuracies. + if (end < nNormalBins) { + high = ComputeHighEdge(end - 1); + } + } + nNormalBins = end - begin; + } + + if (auto *opRebin = sliceSpec.GetOperationRebin()) { + if (nNormalBins % opRebin->GetNGroup() != 0) { + throw std::runtime_error("rebin operation does not divide number of normal bins"); + } + nNormalBins /= opRebin->GetNGroup(); + } + + // The sliced axis always has flow bins enabled to preserve all entries. This is the least confusing for users, + // even if not always strictly necessary. + bool enableFlowBins = true; + return RRegularAxis(nNormalBins, {low, high}, enableFlowBins); + } + /// %ROOT Streamer function to throw when trying to store an object of this class. void Streamer(TBuffer &) { throw std::runtime_error("unable to store RRegularAxis"); } }; diff --git a/hist/histv7/inc/ROOT/RVariableBinAxis.hxx b/hist/histv7/inc/ROOT/RVariableBinAxis.hxx index 47e5f8970b2a2..58faa12ada28b 100644 --- a/hist/histv7/inc/ROOT/RVariableBinAxis.hxx +++ b/hist/histv7/inc/ROOT/RVariableBinAxis.hxx @@ -8,6 +8,7 @@ #include "RBinIndex.hxx" #include "RBinIndexRange.hxx" #include "RLinearizedIndex.hxx" +#include "RSliceSpec.hxx" #include #include @@ -196,6 +197,57 @@ public: : GetNormalRange(); } + /// Slice this axis according to the specification. + /// + /// Throws an exception if the axis cannot be sliced: + /// * A sum operation makes the dimension disappear. + /// * The rebin operation must divide the number of normal bins. + /// + /// \param[in] sliceSpec the slice specification + /// \return the sliced axis, with enabled underflow and overflow bins + RVariableBinAxis Slice(const RSliceSpec &sliceSpec) const + { + if (sliceSpec.GetOperationSum() != nullptr) { + throw std::runtime_error("sum operation makes dimension disappear"); + } + + // Figure out the properties of the sliced axis. + std::size_t nNormalBins = fBinEdges.size() - 1; + std::size_t begin = 0; + std::size_t end = nNormalBins; + + const auto &range = sliceSpec.GetRange(); + if (!range.IsInvalid()) { + if (range.GetBegin().IsNormal()) { + begin = range.GetBegin().GetIndex(); + } + if (range.GetEnd().IsNormal()) { + end = range.GetEnd().GetIndex(); + } + nNormalBins = end - begin; + } + + std::uint64_t nGroup = 1; + if (auto *opRebin = sliceSpec.GetOperationRebin()) { + nGroup = opRebin->GetNGroup(); + if (nNormalBins % nGroup != 0) { + throw std::runtime_error("rebin operation does not divide number of normal bins"); + } + } + + // Prepare the bin edges. + std::vector binEdges; + for (std::size_t bin = begin; bin < end; bin += nGroup) { + binEdges.push_back(fBinEdges[bin]); + } + binEdges.push_back(fBinEdges[end]); + + // The sliced axis always has flow bins enabled to preserve all entries. This is the least confusing for users, + // even if not always strictly necessary. + bool enableFlowBins = true; + return RVariableBinAxis(std::move(binEdges), enableFlowBins); + } + /// %ROOT Streamer function to throw when trying to store an object of this class. void Streamer(TBuffer &) { throw std::runtime_error("unable to store RVariableBinAxis"); } }; diff --git a/hist/histv7/test/hist_axis_variant.cxx b/hist/histv7/test/hist_axis_variant.cxx index 09c84923182eb..76f89accecae9 100644 --- a/hist/histv7/test/hist_axis_variant.cxx +++ b/hist/histv7/test/hist_axis_variant.cxx @@ -23,6 +23,11 @@ TEST(RAxisVariant, RegularAxis) EXPECT_EQ(std::distance(normal12.begin(), normal12.end()), 1); const auto full = axis.GetFullRange(); EXPECT_EQ(std::distance(full.begin(), full.end()), Bins + 2); + + const auto slicedAxis = axis.Slice(RSliceSpec{}); + EXPECT_EQ(slicedAxis.GetVariant().index(), 0); + EXPECT_TRUE(slicedAxis.GetRegularAxis() != nullptr); + EXPECT_EQ(slicedAxis.GetNNormalBins(), Bins); } { @@ -36,6 +41,11 @@ TEST(RAxisVariant, RegularAxis) EXPECT_EQ(std::distance(normal12.begin(), normal12.end()), 1); const auto full = axis.GetFullRange(); EXPECT_EQ(std::distance(full.begin(), full.end()), Bins); + + const auto slicedAxis = axis.Slice(RSliceSpec{}); + EXPECT_EQ(slicedAxis.GetVariant().index(), 0); + EXPECT_TRUE(slicedAxis.GetRegularAxis() != nullptr); + EXPECT_EQ(slicedAxis.GetNNormalBins(), Bins); } } @@ -64,6 +74,11 @@ TEST(RAxisVariant, VariableBinAxis) EXPECT_EQ(std::distance(normal12.begin(), normal12.end()), 1); const auto full = axis.GetFullRange(); EXPECT_EQ(std::distance(full.begin(), full.end()), Bins + 2); + + const auto slicedAxis = axis.Slice(RSliceSpec{}); + EXPECT_EQ(slicedAxis.GetVariant().index(), 1); + EXPECT_TRUE(slicedAxis.GetVariableBinAxis() != nullptr); + EXPECT_EQ(slicedAxis.GetNNormalBins(), Bins); } { @@ -77,6 +92,11 @@ TEST(RAxisVariant, VariableBinAxis) EXPECT_EQ(std::distance(normal12.begin(), normal12.end()), 1); const auto full = axis.GetFullRange(); EXPECT_EQ(std::distance(full.begin(), full.end()), Bins); + + const auto slicedAxis = axis.Slice(RSliceSpec{}); + EXPECT_EQ(slicedAxis.GetVariant().index(), 1); + EXPECT_TRUE(slicedAxis.GetVariableBinAxis() != nullptr); + EXPECT_EQ(slicedAxis.GetNNormalBins(), Bins); } } @@ -100,6 +120,11 @@ TEST(RAxisVariant, CategoricalAxis) EXPECT_EQ(std::distance(normal12.begin(), normal12.end()), 1); const auto full = axis.GetFullRange(); EXPECT_EQ(std::distance(full.begin(), full.end()), 4); + + const auto slicedAxis = axis.Slice(RSliceSpec{}); + EXPECT_EQ(slicedAxis.GetVariant().index(), 2); + EXPECT_TRUE(slicedAxis.GetCategoricalAxis() != nullptr); + EXPECT_EQ(slicedAxis.GetNNormalBins(), 3); } { @@ -113,5 +138,10 @@ TEST(RAxisVariant, CategoricalAxis) EXPECT_EQ(std::distance(normal12.begin(), normal12.end()), 1); const auto full = axis.GetFullRange(); EXPECT_EQ(std::distance(full.begin(), full.end()), 3); + + const auto slicedAxis = axis.Slice(RSliceSpec{}); + EXPECT_EQ(slicedAxis.GetVariant().index(), 2); + EXPECT_TRUE(slicedAxis.GetCategoricalAxis() != nullptr); + EXPECT_EQ(slicedAxis.GetNNormalBins(), 3); } } diff --git a/hist/histv7/test/hist_categorical.cxx b/hist/histv7/test/hist_categorical.cxx index ef9f5269932b9..a5ecd4d2646d2 100644 --- a/hist/histv7/test/hist_categorical.cxx +++ b/hist/histv7/test/hist_categorical.cxx @@ -198,3 +198,35 @@ TEST(RCategoricalAxis, GetFullRange) EXPECT_EQ(std::distance(full.begin(), full.end()), categories.size()); } } + +static void Test_RCategoricalAxis_Slice(bool enableOverflowBin) +{ + const std::vector categories = {"a", "b", "c"}; + const RCategoricalAxis origAxis(categories, enableOverflowBin); + ASSERT_EQ(origAxis.HasOverflowBin(), enableOverflowBin); + + { + // The only allowed "slicing" is to keep the entire axis. + const RSliceSpec full; + const auto axis = origAxis.Slice(full); + EXPECT_EQ(axis.GetNNormalBins(), 3); + EXPECT_EQ(axis.GetCategories(), categories); + EXPECT_TRUE(axis.HasOverflowBin()); + } + + // Slicing and other operations are not allowed / implemented. + EXPECT_THROW(origAxis.Slice(origAxis.GetFullRange()), std::runtime_error); + EXPECT_THROW(origAxis.Slice(origAxis.GetNormalRange()), std::runtime_error); + EXPECT_THROW(origAxis.Slice(RSliceSpec::ROperationRebin(2)), std::runtime_error); + EXPECT_THROW(origAxis.Slice(RSliceSpec::ROperationSum{}), std::runtime_error); +} + +TEST(RCategoricalAxis, Slice) +{ + Test_RCategoricalAxis_Slice(true); +} + +TEST(RCategoricalAxis, SliceNoOverflowBin) +{ + Test_RCategoricalAxis_Slice(false); +} diff --git a/hist/histv7/test/hist_engine.cxx b/hist/histv7/test/hist_engine.cxx index bc2ae778f3a7c..a330eb148d910 100644 --- a/hist/histv7/test/hist_engine.cxx +++ b/hist/histv7/test/hist_engine.cxx @@ -132,19 +132,51 @@ TEST(RHistEngine, GetFullMultiDimRange) TEST(RHistEngine, SetBinContent) { - using ROOT::Experimental::Internal::SetBinContent; - static constexpr std::size_t Bins = 20; const RRegularAxis axis(Bins, {0, Bins}); RHistEngine engine({axis}); - std::array indices = {7}; - SetBinContent(engine, indices, 42); - EXPECT_EQ(engine.GetBinContent(indices), 42); + const RBinIndex index(7); + engine.SetBinContent(index, 42); + EXPECT_EQ(engine.GetBinContent(index), 42); + + const std::array indices = {index}; + engine.SetBinContent(indices, 43); + EXPECT_EQ(engine.GetBinContent(indices), 43); + + // This also works if the value must be converted to the bin content type. + RHistEngine engineF({axis}); + engineF.SetBinContent(index, 42); + EXPECT_EQ(engineF.GetBinContent(index), 42); + + engineF.SetBinContent(indices, 43); + EXPECT_EQ(engineF.GetBinContent(indices), 43); +} + +TEST(RHistEngine, SetBinContentInvalidNumberOfArguments) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine1({axis}); + ASSERT_EQ(engine1.GetNDimensions(), 1); + RHistEngine engine2({axis, axis}); + ASSERT_EQ(engine2.GetNDimensions(), 2); + + EXPECT_NO_THROW(engine1.SetBinContent(1, 0)); + EXPECT_THROW(engine1.SetBinContent(1, 2, 0), std::invalid_argument); + + EXPECT_THROW(engine2.SetBinContent(1, 0), std::invalid_argument); + EXPECT_NO_THROW(engine2.SetBinContent(1, 2, 0)); + EXPECT_THROW(engine2.SetBinContent(1, 2, 3, 0), std::invalid_argument); +} + +TEST(RHistEngine, SetBinContentNotFound) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine({axis}); - // "bin not found" - indices = {Bins}; - EXPECT_THROW(SetBinContent(engine, indices, 43), std::invalid_argument); + EXPECT_THROW(engine.SetBinContent(Bins, 0), std::invalid_argument); } TEST(RHistEngine, Add) diff --git a/hist/histv7/test/hist_hist.cxx b/hist/histv7/test/hist_hist.cxx index 2b03b4f7fa4db..2cc6d2190f2cf 100644 --- a/hist/histv7/test/hist_hist.cxx +++ b/hist/histv7/test/hist_hist.cxx @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -83,6 +84,31 @@ TEST(RHist, GetFullMultiDimRange) EXPECT_FLOAT_EQ(hist.GetStats().GetSumW(), sumW); } +TEST(RHist, SetBinContent) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + + { + RHist hist(axis); + ASSERT_FALSE(hist.GetStats().IsTainted()); + hist.SetBinContent(RBinIndex(1), 42); + EXPECT_EQ(hist.GetBinContent(RBinIndex(1)), 42); + EXPECT_TRUE(hist.GetStats().IsTainted()); + EXPECT_THROW(hist.GetNEntries(), std::logic_error); + } + + { + RHist hist(axis); + ASSERT_FALSE(hist.GetStats().IsTainted()); + const std::array indices = {2}; + hist.SetBinContent(indices, 42); + EXPECT_EQ(hist.GetBinContent(indices), 42); + EXPECT_TRUE(hist.GetStats().IsTainted()); + EXPECT_THROW(hist.GetNEntries(), std::logic_error); + } +} + TEST(RHist, Add) { static constexpr std::size_t Bins = 20; @@ -139,6 +165,33 @@ TEST(RHist, StressAddAtomic) EXPECT_EQ(histA.GetBinContent(0), NAdds); } +TEST(RHist, AddExceptionSafety) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis regularAxis(Bins, {0, Bins}); + const std::vector categories = {"a", "b", "c"}; + const RCategoricalAxis categoricalAxis(categories); + + RHist histA({regularAxis, regularAxis}); + RHist histB({regularAxis, categoricalAxis}); + + histA.Fill(1.5, 2.5); + ASSERT_EQ(histA.GetNEntries(), 1); + ASSERT_EQ(histA.GetBinContent(RBinIndex(1), RBinIndex(2)), 1); + histB.Fill(1.5, "b"); + + EXPECT_THROW(histA.Add(histB), std::invalid_argument); + EXPECT_THROW(histA.AddAtomic(histB), std::invalid_argument); + + // Verify exception safety. Only the original entry should be there. + EXPECT_EQ(histA.GetNEntries(), 1); + EXPECT_EQ(histA.GetBinContent(RBinIndex(1), RBinIndex(2)), 1); + EXPECT_EQ(histA.GetStats().GetSumW(), 1); + EXPECT_EQ(histA.GetStats().GetSumW2(), 1); + EXPECT_EQ(histA.GetStats().GetDimensionStats(0).fSumWX, 1.5); + EXPECT_EQ(histA.GetStats().GetDimensionStats(1).fSumWX, 2.5); +} + TEST(RHist, Clear) { static constexpr std::size_t Bins = 20; @@ -273,6 +326,30 @@ TEST(RHist, FillCategoricalWeight) EXPECT_FLOAT_EQ(hist.ComputeNEffectiveEntries(), 1.9931034); } +TEST(RHist, FillExceptionSafety) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHist hist({axis, axis}); + + hist.Fill(1.5, 2.5); + ASSERT_EQ(hist.GetNEntries(), 1); + ASSERT_EQ(hist.GetBinContent(RBinIndex(1), RBinIndex(2)), 1); + + EXPECT_THROW(hist.Fill(1.5, "b"), std::invalid_argument); + EXPECT_THROW(hist.Fill(std::make_tuple(1.5, "b")), std::invalid_argument); + EXPECT_THROW(hist.Fill(1.5, "b", RWeight(1)), std::invalid_argument); + EXPECT_THROW(hist.Fill(std::make_tuple(1.5, "b"), RWeight(1)), std::invalid_argument); + + // Verify exception safety. Only the first entry should be there. + EXPECT_EQ(hist.GetNEntries(), 1); + EXPECT_EQ(hist.GetBinContent(RBinIndex(1), RBinIndex(2)), 1); + EXPECT_EQ(hist.GetStats().GetSumW(), 1); + EXPECT_EQ(hist.GetStats().GetSumW2(), 1); + EXPECT_EQ(hist.GetStats().GetDimensionStats(0).fSumWX, 1.5); + EXPECT_EQ(hist.GetStats().GetDimensionStats(1).fSumWX, 2.5); +} + TEST(RHist, Scale) { static constexpr std::size_t Bins = 20; diff --git a/hist/histv7/test/hist_regular.cxx b/hist/histv7/test/hist_regular.cxx index 8b4793af912a9..58917956796ec 100644 --- a/hist/histv7/test/hist_regular.cxx +++ b/hist/histv7/test/hist_regular.cxx @@ -32,6 +32,20 @@ TEST(RRegularAxis, Constructor) EXPECT_THROW(RRegularAxis(Bins, {0, NaN}), std::invalid_argument); } +TEST(RRegularAxis, ComputeLowHighEdge) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + + for (std::size_t i = 0; i < Bins; i++) { + EXPECT_EQ(axis.ComputeLowEdge(i), i); + EXPECT_EQ(axis.ComputeHighEdge(i), i + 1); + } + + EXPECT_THROW(axis.ComputeLowEdge(Bins), std::invalid_argument); + EXPECT_THROW(axis.ComputeHighEdge(Bins), std::invalid_argument); +} + TEST(RRegularAxis, Equality) { static constexpr std::size_t Bins = 20; @@ -258,3 +272,62 @@ TEST(RRegularAxis, GetFullRange) EXPECT_EQ(std::distance(full.begin(), full.end()), Bins); } } + +static void Test_RegularAxis_Slice(bool enableFlowBins) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis origAxis(Bins, {0, Bins}, enableFlowBins); + ASSERT_EQ(origAxis.HasFlowBins(), enableFlowBins); + + // Three different ways of "slicing" which will keep the entire axis. + for (auto sliceSpec : {RSliceSpec{}, RSliceSpec(origAxis.GetFullRange()), RSliceSpec(origAxis.GetNormalRange())}) { + const auto axis = origAxis.Slice(sliceSpec); + EXPECT_EQ(axis.GetNNormalBins(), Bins); + EXPECT_EQ(axis.GetLow(), 0); + EXPECT_EQ(axis.GetHigh(), Bins); + EXPECT_TRUE(axis.HasFlowBins()); + } + + { + const RSliceSpec slice(origAxis.GetNormalRange(1, Bins - 1)); + const auto axis = origAxis.Slice(slice); + EXPECT_EQ(axis.GetNNormalBins(), Bins - 2); + EXPECT_EQ(axis.GetLow(), 1); + EXPECT_EQ(axis.GetHigh(), Bins - 1); + EXPECT_TRUE(axis.HasFlowBins()); + } + + { + const RSliceSpec rebin(RSliceSpec::ROperationRebin(2)); + const auto axis = origAxis.Slice(rebin); + EXPECT_EQ(axis.GetNNormalBins(), Bins / 2); + EXPECT_EQ(axis.GetLow(), 0); + EXPECT_EQ(axis.GetHigh(), Bins); + EXPECT_TRUE(axis.HasFlowBins()); + } + + // Rebin grouping must divide the number of normal bins. + EXPECT_THROW(origAxis.Slice(RSliceSpec::ROperationRebin(3)), std::runtime_error); + + // Sum operation makes dimension disappear. + EXPECT_THROW(origAxis.Slice(RSliceSpec::ROperationSum{}), std::runtime_error); + + { + const RSliceSpec sliceRebin(origAxis.GetNormalRange(1, 5), RSliceSpec::ROperationRebin(2)); + const auto axis = origAxis.Slice(sliceRebin); + EXPECT_EQ(axis.GetNNormalBins(), 2); + EXPECT_EQ(axis.GetLow(), 1); + EXPECT_EQ(axis.GetHigh(), 5); + EXPECT_TRUE(axis.HasFlowBins()); + } +} + +TEST(RRegularAxis, Slice) +{ + Test_RegularAxis_Slice(true); +} + +TEST(RRegularAxis, SliceNoFlowBins) +{ + Test_RegularAxis_Slice(false); +} diff --git a/hist/histv7/test/hist_slice.cxx b/hist/histv7/test/hist_slice.cxx index 3df462cbb9433..5805e49d201bc 100644 --- a/hist/histv7/test/hist_slice.cxx +++ b/hist/histv7/test/hist_slice.cxx @@ -285,3 +285,344 @@ TEST(RSliceBinIndexMapper, MapSliceFullSum) EXPECT_TRUE(success); } } + +TEST(RHistEngine, SliceInvalidNumberOfArguments) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + const RHistEngine engine1(axis); + ASSERT_EQ(engine1.GetNDimensions(), 1); + const RHistEngine engine2(axis, axis); + ASSERT_EQ(engine2.GetNDimensions(), 2); + + EXPECT_NO_THROW(engine1.Slice(RSliceSpec{})); + EXPECT_THROW(engine1.Slice(RSliceSpec{}, RSliceSpec{}), std::invalid_argument); + + EXPECT_THROW(engine2.Slice(RSliceSpec{}), std::invalid_argument); + EXPECT_NO_THROW(engine2.Slice(RSliceSpec{}, RSliceSpec{})); + EXPECT_THROW(engine2.Slice(RSliceSpec{}, RSliceSpec{}, RSliceSpec{}), std::invalid_argument); +} + +TEST(RHistEngine, SliceSumAll) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + const RHistEngine engine1(axis); + ASSERT_EQ(engine1.GetNDimensions(), 1); + const RHistEngine engine2(axis, axis); + ASSERT_EQ(engine2.GetNDimensions(), 2); + + const RSliceSpec sum(RSliceSpec::ROperationSum{}); + EXPECT_THROW(engine1.Slice(sum), std::invalid_argument); + EXPECT_THROW(engine2.Slice(sum, sum), std::invalid_argument); +} + +TEST(RHistEngine, SliceFull) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine(axis); + + engine.SetBinContent(RBinIndex::Underflow(), 100); + for (std::size_t i = 0; i < Bins; i++) { + engine.SetBinContent(i, i + 1); + } + engine.SetBinContent(RBinIndex::Overflow(), 200); + + // Three different ways of "slicing" which will keep the entire axis. + for (auto sliceSpec : {RSliceSpec{}, RSliceSpec(axis.GetFullRange()), RSliceSpec(axis.GetNormalRange())}) { + const auto sliced = engine.Slice(sliceSpec); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_TRUE(sliced.GetAxes()[0].GetRegularAxis() != nullptr); + EXPECT_EQ(sliced.GetAxes()[0].GetNNormalBins(), Bins); + EXPECT_EQ(sliced.GetTotalNBins(), Bins + 2); + + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Underflow()), 100); + for (std::size_t i = 0; i < Bins; i++) { + EXPECT_EQ(sliced.GetBinContent(i), i + 1); + } + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Overflow()), 200); + } +} + +TEST(RHistEngine, SliceNormal) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine(axis); + + engine.SetBinContent(RBinIndex::Underflow(), 100); + for (std::size_t i = 0; i < Bins; i++) { + engine.SetBinContent(i, i + 1); + } + engine.SetBinContent(RBinIndex::Overflow(), 200); + + const auto sliced = engine.Slice(axis.GetNormalRange(1, 5)); + ASSERT_EQ(sliced.GetNDimensions(), 1); + const auto *regular = sliced.GetAxes()[0].GetRegularAxis(); + ASSERT_TRUE(regular != nullptr); + EXPECT_EQ(regular->GetNNormalBins(), 4); + EXPECT_EQ(regular->GetLow(), 1); + EXPECT_EQ(regular->GetHigh(), 5); + EXPECT_EQ(sliced.GetTotalNBins(), 6); + + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Underflow()), 101); + for (std::size_t i = 0; i < 4; i++) { + EXPECT_EQ(sliced.GetBinContent(i), i + 2); + } + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Overflow()), 395); +} + +TEST(RHistEngine, SliceRebin) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine(axis); + + engine.SetBinContent(RBinIndex::Underflow(), 100); + for (std::size_t i = 0; i < Bins; i++) { + engine.SetBinContent(i, i + 1); + } + engine.SetBinContent(RBinIndex::Overflow(), 200); + + const auto sliced = engine.Slice(RSliceSpec::ROperationRebin(2)); + ASSERT_EQ(sliced.GetNDimensions(), 1); + const auto *regular = sliced.GetAxes()[0].GetRegularAxis(); + ASSERT_TRUE(regular != nullptr); + EXPECT_EQ(regular->GetNNormalBins(), Bins / 2); + EXPECT_EQ(regular->GetLow(), 0); + EXPECT_EQ(regular->GetHigh(), Bins); + EXPECT_EQ(sliced.GetTotalNBins(), Bins / 2 + 2); + + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Underflow()), 100); + for (std::size_t i = 0; i < Bins / 2; i++) { + EXPECT_EQ(sliced.GetBinContent(i), 4 * i + 3); + } + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Overflow()), 200); +} + +TEST(RHistEngine, SliceRangeRebin) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine(axis); + + engine.SetBinContent(RBinIndex::Underflow(), 100); + for (std::size_t i = 0; i < Bins; i++) { + engine.SetBinContent(i, i + 1); + } + engine.SetBinContent(RBinIndex::Overflow(), 200); + + const RSliceSpec spec(axis.GetNormalRange(1, 5), RSliceSpec::ROperationRebin(2)); + const auto sliced = engine.Slice(spec); + ASSERT_EQ(sliced.GetNDimensions(), 1); + const auto *regular = sliced.GetAxes()[0].GetRegularAxis(); + ASSERT_TRUE(regular != nullptr); + EXPECT_EQ(regular->GetNNormalBins(), 2); + EXPECT_EQ(regular->GetLow(), 1); + EXPECT_EQ(regular->GetHigh(), 5); + EXPECT_EQ(sliced.GetTotalNBins(), 4); + + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Underflow()), 101); + for (std::size_t i = 0; i < 2; i++) { + EXPECT_EQ(sliced.GetBinContent(i), 4 * i + 5); + } + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Overflow()), 395); +} + +TEST(RHistEngine, SliceSum) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine(axis, axis); + + engine.SetBinContent(RBinIndex::Underflow(), 0, 1000); + engine.SetBinContent(RBinIndex::Underflow(), 2, 2000); + for (std::size_t i = 0; i < Bins; i++) { + for (std::size_t j = 0; j < Bins; j++) { + engine.SetBinContent(i, RBinIndex::Underflow(), 100 * i); + engine.SetBinContent(i, RBinIndex(j), i * Bins + j); + engine.SetBinContent(i, RBinIndex::Overflow(), 200 * i); + } + } + engine.SetBinContent(RBinIndex::Overflow(), 3, 3000); + engine.SetBinContent(RBinIndex::Overflow(), 6, 4000); + + const auto sliced = engine.Slice(RSliceSpec{}, RSliceSpec::ROperationSum{}); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_TRUE(sliced.GetAxes()[0].GetRegularAxis() != nullptr); + EXPECT_EQ(sliced.GetAxes()[0].GetNNormalBins(), Bins); + EXPECT_EQ(sliced.GetTotalNBins(), Bins + 2); + + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Underflow()), 3000); + for (std::size_t i = 0; i < Bins; i++) { + EXPECT_EQ(sliced.GetBinContent(i), i * (100 + Bins * Bins + 200) + Bins * (Bins - 1) / 2); + } + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Overflow()), 7000); +} + +TEST(RHistEngine, SliceRangeSum) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine(axis, axis); + + engine.SetBinContent(RBinIndex::Underflow(), 0, 1000); + engine.SetBinContent(RBinIndex::Underflow(), 2, 2000); + for (std::size_t i = 0; i < Bins; i++) { + for (std::size_t j = 0; j < Bins; j++) { + engine.SetBinContent(i, RBinIndex::Underflow(), 100 * i); + engine.SetBinContent(i, RBinIndex(j), i * Bins + j); + engine.SetBinContent(i, RBinIndex::Overflow(), 200 * i); + } + } + engine.SetBinContent(RBinIndex::Overflow(), 3, 3000); + engine.SetBinContent(RBinIndex::Overflow(), 6, 4000); + + const RSliceSpec rangeSum(axis.GetNormalRange(1, 5), RSliceSpec::ROperationSum{}); + const auto sliced = engine.Slice(RSliceSpec{}, rangeSum); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_TRUE(sliced.GetAxes()[0].GetRegularAxis() != nullptr); + EXPECT_EQ(sliced.GetAxes()[0].GetNNormalBins(), Bins); + EXPECT_EQ(sliced.GetTotalNBins(), Bins + 2); + + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Underflow()), 2000); + for (std::size_t i = 0; i < Bins; i++) { + EXPECT_EQ(sliced.GetBinContent(i), 4 * i * Bins + 10); + } + EXPECT_EQ(sliced.GetBinContent(RBinIndex::Overflow()), 3000); +} + +TEST(RHist, SliceTainted) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHist hist(axis); + + hist.SetBinContent(RBinIndex(1), 2); + ASSERT_TRUE(hist.GetStats().IsTainted()); + + const auto sliced = hist.Slice(RSliceSpec{}); + EXPECT_TRUE(sliced.GetStats().IsTainted()); +} + +TEST(RHist, SliceFull) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHist hist(axis); + + hist.Fill(1.5); + ASSERT_EQ(hist.GetNEntries(), 1); + + // Three different ways of "slicing" which will keep the entire axis. + for (auto sliceSpec : {RSliceSpec{}, RSliceSpec(axis.GetFullRange()), RSliceSpec(axis.GetNormalRange())}) { + const auto sliced = hist.Slice(sliceSpec); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_EQ(sliced.GetTotalNBins(), Bins + 2); + + EXPECT_FALSE(sliced.GetStats().IsTainted()); + EXPECT_EQ(sliced.GetBinContent(1), 1); + } +} + +TEST(RHist, SliceNormal) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHist hist(axis); + + hist.Fill(1.5); + ASSERT_EQ(hist.GetNEntries(), 1); + + const auto sliced = hist.Slice(axis.GetNormalRange(1, 5)); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_EQ(sliced.GetTotalNBins(), 6); + EXPECT_EQ(sliced.GetBinContent(0), 1); + + // The sliced histogram still has the same statistics. + EXPECT_FALSE(sliced.GetStats().IsTainted()); + EXPECT_EQ(sliced.GetNEntries(), 1); + EXPECT_EQ(sliced.ComputeMean(), 1.5); +} + +TEST(RHist, SliceRebin) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHist hist(axis); + + hist.Fill(1.5); + ASSERT_EQ(hist.GetNEntries(), 1); + + const auto sliced = hist.Slice(RSliceSpec::ROperationRebin(2)); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_EQ(sliced.GetTotalNBins(), Bins / 2 + 2); + EXPECT_EQ(sliced.GetBinContent(0), 1); + + // The sliced histogram still has the same statistics. + EXPECT_FALSE(sliced.GetStats().IsTainted()); + EXPECT_EQ(sliced.GetNEntries(), 1); + EXPECT_EQ(sliced.ComputeMean(), 1.5); +} + +TEST(RHist, SliceRangeRebin) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHist hist(axis); + + hist.Fill(1.5); + ASSERT_EQ(hist.GetNEntries(), 1); + + const RSliceSpec spec(axis.GetNormalRange(1, 5), RSliceSpec::ROperationRebin(2)); + const auto sliced = hist.Slice(spec); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_EQ(sliced.GetTotalNBins(), 4); + EXPECT_EQ(sliced.GetBinContent(0), 1); + + // The sliced histogram still has the same statistics. + EXPECT_FALSE(sliced.GetStats().IsTainted()); + EXPECT_EQ(sliced.GetNEntries(), 1); + EXPECT_EQ(sliced.ComputeMean(), 1.5); +} + +TEST(RHist, SliceSum) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHist hist(axis, axis); + + hist.Fill(1.5, 2.5); + ASSERT_EQ(hist.GetNEntries(), 1); + + const auto sliced = hist.Slice(RSliceSpec{}, RSliceSpec::ROperationSum{}); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_EQ(sliced.GetTotalNBins(), Bins + 2); + EXPECT_EQ(sliced.GetBinContent(1), 1); + + // The first dimension still has the same statistics. + EXPECT_FALSE(sliced.GetStats().IsTainted()); + EXPECT_EQ(sliced.GetNEntries(), 1); + EXPECT_EQ(sliced.ComputeMean(), 1.5); +} + +TEST(RHist, SliceRangeSum) +{ + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHist hist(axis, axis); + + hist.Fill(1.5, 2.5); + ASSERT_EQ(hist.GetNEntries(), 1); + + const RSliceSpec rangeSum(axis.GetNormalRange(1, 5), RSliceSpec::ROperationSum{}); + const auto sliced = hist.Slice(RSliceSpec{}, rangeSum); + ASSERT_EQ(sliced.GetNDimensions(), 1); + EXPECT_EQ(sliced.GetTotalNBins(), Bins + 2); + EXPECT_EQ(sliced.GetBinContent(1), 1); + + // The slicing could have dropped entries (even if it didn't in this case), the statistics are tainted. + EXPECT_TRUE(sliced.GetStats().IsTainted()); + EXPECT_THROW(sliced.GetNEntries(), std::logic_error); +} diff --git a/hist/histv7/test/hist_stats.cxx b/hist/histv7/test/hist_stats.cxx index 1b1f9542fb92a..f8abeb6d1bf02 100644 --- a/hist/histv7/test/hist_stats.cxx +++ b/hist/histv7/test/hist_stats.cxx @@ -98,6 +98,36 @@ TEST(RHistStats, DisableDimension) stats.Fill(4, 5, 6, RWeight(1)); EXPECT_EQ(stats.GetNEntries(), 4); + EXPECT_THROW(stats.ComputeMean(1), std::invalid_argument); + EXPECT_THROW(stats.ComputeVariance(1), std::invalid_argument); + EXPECT_THROW(stats.ComputeSkewness(1), std::invalid_argument); + EXPECT_THROW(stats.ComputeKurtosis(1), std::invalid_argument); +} + +TEST(RHistStats, Taint) +{ + RHistStats stats(1); + stats.Taint(); + EXPECT_TRUE(stats.IsTainted()); + + // Modifications are still possible. + stats.Add(stats); + stats.Fill(1); + stats.Scale(2.0); + + // Any read access will throw. + EXPECT_THROW(stats.GetNEntries(), std::logic_error); + EXPECT_THROW(stats.GetSumW(), std::logic_error); + EXPECT_THROW(stats.GetSumW2(), std::logic_error); + EXPECT_THROW(stats.GetDimensionStats(), std::logic_error); + EXPECT_THROW(stats.ComputeMean(), std::logic_error); + EXPECT_THROW(stats.ComputeVariance(), std::logic_error); + EXPECT_THROW(stats.ComputeSkewness(), std::logic_error); + EXPECT_THROW(stats.ComputeKurtosis(), std::logic_error); + + // Clear resets the object, including its taint status. + stats.Clear(); + EXPECT_NO_THROW(stats.GetNEntries()); } TEST(RHistStats, Add) @@ -224,6 +254,27 @@ TEST(RHistStats, AddAtomicDifferent) EXPECT_THROW(statsC.AddAtomic(statsB), std::invalid_argument); } +TEST(RHistStats, AddExceptionSafety) +{ + RHistStats statsA(2); + RHistStats statsB(2); + statsB.DisableDimension(1); + + statsA.Fill(1, 2); + ASSERT_EQ(statsA.GetNEntries(), 1); + statsB.Fill(1, 2); + + EXPECT_THROW(statsA.Add(statsB), std::invalid_argument); + EXPECT_THROW(statsA.AddAtomic(statsB), std::invalid_argument); + + // Verify exception safety. Only the original entry should be there. + EXPECT_EQ(statsA.GetNEntries(), 1); + EXPECT_EQ(statsA.GetSumW(), 1); + EXPECT_EQ(statsA.GetSumW2(), 1); + EXPECT_EQ(statsA.GetDimensionStats(0).fSumWX, 1); + EXPECT_EQ(statsA.GetDimensionStats(1).fSumWX, 2); +} + TEST(RHistStats, Clear) { RHistStats stats(2); @@ -573,6 +624,26 @@ TEST(RHistStats, FillTupleWeightInvalidNumberOfArguments) EXPECT_THROW(stats2.Fill(std::make_tuple(1, 2, 3), RWeight(1)), std::invalid_argument); } +TEST(RHistStats, FillExceptionSafety) +{ + RHistStats stats(2); + + stats.Fill(1, 2); + ASSERT_EQ(stats.GetNEntries(), 1); + + EXPECT_THROW(stats.Fill(1, "b"), std::invalid_argument); + EXPECT_THROW(stats.Fill(std::make_tuple(1, "b")), std::invalid_argument); + EXPECT_THROW(stats.Fill(1, "b", RWeight(1)), std::invalid_argument); + EXPECT_THROW(stats.Fill(std::make_tuple(1, "b"), RWeight(1)), std::invalid_argument); + + // Verify exception safety. Only the first entry should be there. + EXPECT_EQ(stats.GetNEntries(), 1); + EXPECT_EQ(stats.GetSumW(), 1); + EXPECT_EQ(stats.GetSumW2(), 1); + EXPECT_EQ(stats.GetDimensionStats(0).fSumWX, 1); + EXPECT_EQ(stats.GetDimensionStats(1).fSumWX, 2); +} + TEST(RHistStats, Scale) { RHistStats stats(3); diff --git a/hist/histv7/test/hist_user.cxx b/hist/histv7/test/hist_user.cxx index c7791fa770da3..b5d348da0cdd9 100644 --- a/hist/histv7/test/hist_user.cxx +++ b/hist/histv7/test/hist_user.cxx @@ -11,6 +11,12 @@ struct UserWeight { struct User { double fValue = 0; + User &operator=(double value) + { + fValue = value; + return *this; + } + User &operator++() { fValue++; @@ -252,3 +258,19 @@ TEST(RHistEngineUser, Scale) EXPECT_EQ(engine.GetBinContent(8).fValue, Factor * 0.8); EXPECT_EQ(engine.GetBinContent(9).fValue, Factor * 0.9); } + +TEST(RHistEngineUser, SetBinContent) +{ + // Setting uses operator= + static constexpr std::size_t Bins = 20; + const RRegularAxis axis(Bins, {0, Bins}); + RHistEngine engine({axis}); + + const RBinIndex index(7); + engine.SetBinContent(index, 42); + EXPECT_EQ(engine.GetBinContent(index).fValue, 42); + + const std::array indices = {index}; + engine.SetBinContent(indices, 43); + EXPECT_EQ(engine.GetBinContent(indices).fValue, 43); +} diff --git a/hist/histv7/test/hist_variable.cxx b/hist/histv7/test/hist_variable.cxx index 284958d703100..ed277e38586ea 100644 --- a/hist/histv7/test/hist_variable.cxx +++ b/hist/histv7/test/hist_variable.cxx @@ -332,3 +332,69 @@ TEST(RVariableBinAxis, GetFullRange) EXPECT_EQ(std::distance(full.begin(), full.end()), Bins); } } + +static void Test_RVariableBinAxis_Slice(bool enableFlowBins) +{ + static constexpr std::size_t Bins = 20; + std::vector bins; + for (std::size_t i = 0; i < Bins; i++) { + bins.push_back(i); + } + bins.push_back(Bins); + const RVariableBinAxis origAxis(bins, enableFlowBins); + ASSERT_EQ(origAxis.HasFlowBins(), enableFlowBins); + + // Three different ways of "slicing" which will keep the entire axis. + for (auto sliceSpec : {RSliceSpec{}, RSliceSpec(origAxis.GetFullRange()), RSliceSpec(origAxis.GetNormalRange())}) { + const auto axis = origAxis.Slice(sliceSpec); + EXPECT_EQ(axis.GetNNormalBins(), Bins); + EXPECT_EQ(axis.GetBinEdges(), bins); + EXPECT_TRUE(axis.HasFlowBins()); + } + + { + const RSliceSpec slice(origAxis.GetNormalRange(1, Bins - 1)); + const auto axis = origAxis.Slice(slice); + EXPECT_EQ(axis.GetNNormalBins(), Bins - 2); + EXPECT_EQ(axis.GetBinEdges().front(), 1); + EXPECT_EQ(axis.GetBinEdges().back(), Bins - 1); + EXPECT_TRUE(axis.HasFlowBins()); + } + + { + const RSliceSpec rebin(RSliceSpec::ROperationRebin(2)); + const auto axis = origAxis.Slice(rebin); + EXPECT_EQ(axis.GetNNormalBins(), Bins / 2); + EXPECT_EQ(axis.GetBinEdges().front(), 0); + EXPECT_EQ(axis.GetBinEdges().back(), Bins); + EXPECT_TRUE(axis.HasFlowBins()); + } + + // Rebin grouping must divide the number of normal bins. + EXPECT_THROW(origAxis.Slice(RSliceSpec::ROperationRebin(3)), std::runtime_error); + + // Sum operation makes dimension disappear. + EXPECT_THROW(origAxis.Slice(RSliceSpec::ROperationSum{}), std::runtime_error); + + { + const RSliceSpec sliceRebin(origAxis.GetNormalRange(1, 5), RSliceSpec::ROperationRebin(2)); + const auto axis = origAxis.Slice(sliceRebin); + EXPECT_EQ(axis.GetNNormalBins(), 2); + const auto &binEdges = axis.GetBinEdges(); + ASSERT_EQ(binEdges.size(), 3); + EXPECT_EQ(binEdges[0], 1); + EXPECT_EQ(binEdges[1], 3); + EXPECT_EQ(binEdges[2], 5); + EXPECT_TRUE(axis.HasFlowBins()); + } +} + +TEST(RVariableBinAxis, Slice) +{ + Test_RVariableBinAxis_Slice(true); +} + +TEST(RVariableBinAxis, SliceNoFlowBins) +{ + Test_RVariableBinAxis_Slice(false); +} diff --git a/hist/histv7util/inc/ROOT/Hist/ConvertToTH1.hxx b/hist/histv7util/inc/ROOT/Hist/ConvertToTH1.hxx index 197f9e62cd1a7..c52de19ad7c30 100644 --- a/hist/histv7util/inc/ROOT/Hist/ConvertToTH1.hxx +++ b/hist/histv7util/inc/ROOT/Hist/ConvertToTH1.hxx @@ -79,6 +79,9 @@ std::unique_ptr ConvertToTH1S(const RHist &hist); /// Convert a one-dimensional histogram to TH1I. /// +/// If the RHistStats are tainted, for example after setting bin contents, the number of entries and the total sum of +/// weights will be unset. +/// /// Throws an exception if the histogram has more than one dimension. /// /// \param[in] hist the RHist to convert diff --git a/hist/histv7util/src/ConvertToTH1.cxx b/hist/histv7util/src/ConvertToTH1.cxx index 86f7ee2cd6e8f..76bad7d8f06bd 100644 --- a/hist/histv7util/src/ConvertToTH1.cxx +++ b/hist/histv7util/src/ConvertToTH1.cxx @@ -69,6 +69,10 @@ std::unique_ptr ConvertToTH1Impl(const RHistEngine &engine) template void ConvertGlobalStatistics(Hist &h, const RHistStats &stats) { + if (stats.IsTainted()) { + return; + } + h.SetEntries(stats.GetNEntries()); Double_t hStats[4] = { diff --git a/hist/histv7util/test/hist_convert_TH1.cxx b/hist/histv7util/test/hist_convert_TH1.cxx index 1ab428ec1e5a0..fc4d87bf4e4f7 100644 --- a/hist/histv7util/test/hist_convert_TH1.cxx +++ b/hist/histv7util/test/hist_convert_TH1.cxx @@ -100,6 +100,27 @@ TEST(ConvertToTH1I, RHist) EXPECT_EQ(stats[3], 2470); } +TEST(ConvertToTH1I, RHistSetBinContentTainted) +{ + static constexpr std::size_t Bins = 20; + RHist hist(Bins, {0, Bins}); + hist.SetBinContent(RBinIndex(1), 42); + ASSERT_TRUE(hist.GetStats().IsTainted()); + + auto th1i = ConvertToTH1I(hist); + ASSERT_TRUE(th1i); + + EXPECT_EQ(th1i->GetBinContent(2), 42); + + EXPECT_EQ(th1i->GetEntries(), 0); + Double_t stats[4]; + th1i->GetStats(stats); + EXPECT_EQ(stats[0], 0); + EXPECT_EQ(stats[1], 0); + EXPECT_EQ(stats[2], 0); + EXPECT_EQ(stats[3], 0); +} + TEST(ConvertToTH1C, RHistEngine) { static constexpr std::size_t Bins = 20; @@ -149,7 +170,7 @@ TEST(ConvertToTH1L, RHistEngine) // Set one 64-bit long long value larger than what double can exactly represent. static constexpr long long Large = (1LL << 60) - 1; const std::array indices = {1}; - ROOT::Experimental::Internal::SetBinContent(engineLL, indices, Large); + engineLL.SetBinContent(indices, Large); th1l = ConvertToTH1L(engineLL); ASSERT_TRUE(th1l); diff --git a/io/io/inc/ROOT/RFile.hxx b/io/io/inc/ROOT/RFile.hxx index 445cfba08e6c3..27871c85aae0e 100644 --- a/io/io/inc/ROOT/RFile.hxx +++ b/io/io/inc/ROOT/RFile.hxx @@ -2,7 +2,7 @@ /// \ingroup Base ROOT7 /// \author Giacomo Parolini /// \date 2025-03-19 -/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback +/// \warning This is part of the ROOT 7 prototype! It will change without notice. Feedback /// is welcome! #ifndef ROOT7_RFile @@ -189,6 +189,9 @@ public: \ingroup RFile \brief An interface to read from, or write to, a ROOT file, as well as performing other common operations. +Please refer to the documentation of TFile for the details related to how data and executable code can be stored +in ROOT files. + ## When and why should you use RFile RFile is a modern and minimalistic interface to ROOT files, both local and remote, that can be used instead of TFile diff --git a/io/io/inc/ROOT/RRawFile.hxx b/io/io/inc/ROOT/RRawFile.hxx index 4aa54a787a031..1e1b83aca697d 100644 --- a/io/io/inc/ROOT/RRawFile.hxx +++ b/io/io/inc/ROOT/RRawFile.hxx @@ -140,6 +140,9 @@ protected: /// By default implemented as a loop of ReadAt calls but can be overwritten, e.g. XRootD or DAVIX implementations virtual void ReadVImpl(RIOVec *ioVec, unsigned int nReq); + // Default implementation: no-op + virtual void SetDiscourageReadAheadImpl(bool /* value */) {} + /// Open the file if not already open. Otherwise noop. void EnsureOpen(); @@ -178,8 +181,9 @@ public: /// Returns the limits regarding the ioVec input to ReadV for this specific file; may open the file as a side-effect. virtual RIOVecLimits GetReadVLimits() { return RIOVecLimits(); } - /// Turn off buffered reads; all scalar read requests go directly to the implementation. Buffering can be turned - /// back on. + /// Turn on/off buffered reads; if off, all scalar read requests go directly to the implementation. Buffering also + /// turns on and off OS read-ahead where supported: if buffering is switched, SetDiscourageReadAheadImpl() will be + /// called accordingly. void SetBuffering(bool value); bool IsBuffering() const { return fIsBuffering; } diff --git a/io/io/inc/ROOT/RRawFileUnix.hxx b/io/io/inc/ROOT/RRawFileUnix.hxx index 822ef986cadf7..bb8ebb32fe2aa 100644 --- a/io/io/inc/ROOT/RRawFileUnix.hxx +++ b/io/io/inc/ROOT/RRawFileUnix.hxx @@ -37,6 +37,7 @@ protected: size_t ReadAtImpl(void *buffer, size_t nbytes, std::uint64_t offset) final; void ReadVImpl(RIOVec *ioVec, unsigned int nReq) final; std::uint64_t GetSizeImpl() final; + void SetDiscourageReadAheadImpl(bool value) final; public: RRawFileUnix(std::string_view url, RRawFile::ROptions options); diff --git a/io/io/inc/TEmulatedCollectionProxy.h b/io/io/inc/TEmulatedCollectionProxy.h index 3f1a1cd75dff4..151f7a23bd900 100644 --- a/io/io/inc/TEmulatedCollectionProxy.h +++ b/io/io/inc/TEmulatedCollectionProxy.h @@ -13,6 +13,7 @@ #include "TGenCollectionProxy.h" +#include #include class TEmulatedCollectionProxy : public TGenCollectionProxy { @@ -21,10 +22,84 @@ class TEmulatedCollectionProxy : public TGenCollectionProxy { friend class TCollectionProxy; public: - // Container type definition - typedef std::vector Cont_t; - // Pointer to container type - typedef Cont_t *PCont_t; + /// Storage type whose alignment matches \a Align bytes. + /// Used to instantiate std::vector specializations with guaranteed buffer alignment. + template + struct alignas(Align) AlignedStorage { + char data[Align] = {}; + }; + + // Convenience vector aliases for each supported alignment. + using Cont1_t = std::vector>; + using Cont2_t = std::vector>; + using Cont4_t = std::vector>; + using Cont8_t = std::vector>; + using Cont16_t = std::vector>; + using Cont32_t = std::vector>; + using Cont64_t = std::vector>; + using Cont128_t = std::vector>; + using Cont256_t = std::vector>; + using Cont512_t = std::vector>; + using Cont1024_t = std::vector>; + using Cont2048_t = std::vector>; + using Cont4096_t = std::vector>; + + // Canonical container type (used for sizeof/typeid; actual alignment is + // selected at runtime via the alignment switch in each method). + using Cont_t = std::vector; + using PCont_t = Cont_t *; + + /// Invoke \a fn(typed_ptr, elemSize) where typed_ptr is the container + /// pointer cast to the correct AlignedStorage* for the value class + /// alignment. \a fn receives the element size (N) as a second argument + /// so it can convert byte counts to element counts. + template + void WithCont(void *obj, F &&fn) const + { + auto *vcl = GetValueClass(); + std::size_t align = alignof(std::max_align_t); + if (!fKey && (fVal->fCase & kIsPointer)) { + // If the collection contains pointers, we need to use the alignment of a pointer, not of the value class. + align = alignof(void*); + } else if (vcl) { + align = vcl->GetClassAlignment(); + } else { + switch( int(fVal->fKind) ) { + case kChar_t: + case kUChar_t: align = alignof(char); break; + case kShort_t: + case kUShort_t: align = alignof(short); break; + case kInt_t: + case kUInt_t: align = alignof(int); break; + case kLong_t: + case kULong_t: align = alignof(long); break; + case kLong64_t: + case kULong64_t:align = alignof(long long); break; + case kFloat_t: align = alignof(float); break; + case kDouble_t: align = alignof(double); break; + } + } + switch (align) { + // When adding new cases here, also update the static_assert in TClingUtils.cxx + // to explicitly allow the new alignment and to update the error message accordingly. + case 4096: fn(reinterpret_cast(obj), std::size_t(4096)); break; + case 2048: fn(reinterpret_cast(obj), std::size_t(2048)); break; + case 1024: fn(reinterpret_cast(obj), std::size_t(1024)); break; + case 512: fn(reinterpret_cast(obj), std::size_t( 512)); break; + case 256: fn(reinterpret_cast(obj), std::size_t( 256)); break; + case 128: fn(reinterpret_cast(obj), std::size_t( 128)); break; + case 64: fn(reinterpret_cast(obj), std::size_t( 64)); break; + case 32: fn(reinterpret_cast(obj), std::size_t( 32)); break; + case 16: fn(reinterpret_cast(obj), std::size_t( 16)); break; + case 8: fn(reinterpret_cast(obj), std::size_t( 8)); break; + case 4: fn(reinterpret_cast(obj), std::size_t( 4)); break; + case 2: fn(reinterpret_cast(obj), std::size_t( 2)); break; + case 1: fn(reinterpret_cast(obj), std::size_t( 1)); break; + default: + Fatal("TEmulatedCollectionProxy::WithCont", "Unsupported alignment %zu for value class %s", + align, vcl ? vcl->GetName() : ""); + } + } protected: // Some hack to avoid const-ness @@ -59,28 +134,62 @@ class TEmulatedCollectionProxy : public TGenCollectionProxy { ~TEmulatedCollectionProxy() override; // Virtual constructor - void* New() const override { return new Cont_t; } + void* New() const override + { + void *mem = ::operator new(sizeof(Cont_t)); + WithCont(mem, [](auto *c, std::size_t) { new (c) std::decay_t(); }); + return mem; + } // Virtual in-place constructor - void* New(void* memory) const override { return new(memory) Cont_t; } + void* New(void* memory) const override + { + WithCont(memory, [](auto *c, std::size_t) { new (c) std::decay_t(); }); + return memory; + } // Virtual constructor - TClass::ObjectPtr NewObject() const override { return {new Cont_t, nullptr}; } + TClass::ObjectPtr NewObject() const override + { + return {New(), nullptr}; + } // Virtual in-place constructor - TClass::ObjectPtr NewObject(void* memory) const override { return {new(memory) Cont_t, nullptr}; } + TClass::ObjectPtr NewObject(void* memory) const override + { + return {New(memory), nullptr}; + } // Virtual array constructor - void* NewArray(Int_t nElements) const override { return new Cont_t[nElements]; } + void* NewArray(Int_t nElements) const override + { + void *arr = ::operator new(nElements * sizeof(Cont_t)); + for (Int_t i = 0; i < nElements; ++i) + WithCont(static_cast(arr) + i * sizeof(Cont_t), + [](auto *c, std::size_t) { new (c) std::decay_t(); }); + return arr; + } - // Virtual in-place constructor - void* NewArray(Int_t nElements, void* memory) const override { return new(memory) Cont_t[nElements]; } + // Virtual in-place array constructor + void* NewArray(Int_t nElements, void* memory) const override + { + for (Int_t i = 0; i < nElements; ++i) + WithCont(static_cast(memory) + i * sizeof(Cont_t), + [](auto *c, std::size_t) { new (c) std::decay_t(); }); + return memory; + } // Virtual array constructor - TClass::ObjectPtr NewObjectArray(Int_t nElements) const override { return {new Cont_t[nElements], nullptr}; } + TClass::ObjectPtr NewObjectArray(Int_t nElements) const override + { + return {NewArray(nElements), nullptr}; + } - // Virtual in-place constructor - TClass::ObjectPtr NewObjectArray(Int_t nElements, void* memory) const override { return {new(memory) Cont_t[nElements], nullptr}; } + // Virtual in-place array constructor + TClass::ObjectPtr NewObjectArray(Int_t nElements, void* memory) const override + { + return {NewArray(nElements, memory), nullptr}; + } // Virtual destructor void Destructor(void* p, Bool_t dtorOnly = kFALSE) const override; @@ -130,4 +239,22 @@ class TEmulatedCollectionProxy : public TGenCollectionProxy { Bool_t IsValid() const; }; + +namespace ROOT::Internal::TEmulatedProxyHelpers { +inline void PrintWriteStlWithoutProxyMsg(const char *where, const char *clName, const char *BranchName) +{ + const char *writeStlWithoutProxyMsg = + "The class requested (%s) for the branch \"%s\" " + "is an instance of an stl collection and does not have a compiled CollectionProxy. " + "Please generate the dictionary for this collection (%s) to avoid writing corrupted data."; + // This error message is repeated several times in the code. We write it once. + Error(where, writeStlWithoutProxyMsg, clName, BranchName, clName); +} + +inline bool HasEmulatedProxy(TClass *cl){ + return cl && cl->GetCollectionProxy() && dynamic_cast(cl->GetCollectionProxy()); +} +} // namespace ROOT::Internal::TEmulatedProxyHelpers + + #endif diff --git a/io/io/inc/TGenCollectionProxy.h b/io/io/inc/TGenCollectionProxy.h index 310f8a1020bb0..a494da2e0eebb 100644 --- a/io/io/inc/TGenCollectionProxy.h +++ b/io/io/inc/TGenCollectionProxy.h @@ -21,7 +21,9 @@ #include #include #include +#include #include +#include class TObjArray; class TCollectionProxyFactory; @@ -283,6 +285,46 @@ class TGenCollectionProxy } }; + /// Allocator that allocates memory aligned to at least \a Align bytes. + /// The alignment is chosen at construction time and must be a power of two. + template + struct AlignedAllocator { + using value_type = T; + + std::size_t alignment; + + explicit AlignedAllocator(std::size_t align = alignof(std::max_align_t)) : alignment(align) {} + + template + AlignedAllocator(const AlignedAllocator &other) noexcept : alignment(other.alignment) {} + + T *allocate(std::size_t n) + { + std::size_t effectiveAlign = alignment < alignof(T) ? alignof(T) : alignment; + std::size_t bytes = AlignUp(n * sizeof(T), effectiveAlign); + return static_cast(::operator new(bytes, std::align_val_t{effectiveAlign})); + } + + void deallocate(T *p, std::size_t n) noexcept + { + std::size_t effectiveAlign = alignment < alignof(T) ? alignof(T) : alignment; + std::size_t bytes = AlignUp(n * sizeof(T), effectiveAlign); + ::operator delete(p, bytes, std::align_val_t{effectiveAlign}); + } + + template + bool operator==(const AlignedAllocator &other) const noexcept { return alignment == other.alignment; } + template + bool operator!=(const AlignedAllocator &other) const noexcept { return !(*this == other); } + + private: + /// Round \p value up to the next multiple of \p align. \p align must be a power of two. + static std::size_t AlignUp(std::size_t value, std::size_t align) + { + return (value + align - 1) & ~(align - 1); + } + }; + protected: typedef ROOT::Detail::TCollectionProxyInfo::Environ Env_t; typedef ROOT::Detail::TCollectionProxyInfo::EnvironBase EnvironBase_t; diff --git a/io/io/inc/TStreamerInfo.h b/io/io/inc/TStreamerInfo.h index a604a3cc196ea..ffe008fd308b0 100644 --- a/io/io/inc/TStreamerInfo.h +++ b/io/io/inc/TStreamerInfo.h @@ -90,6 +90,7 @@ class TStreamerInfo : public TVirtualStreamerInfo { Int_t fOnFileClassVersion;///), sizeof(std::vector::iterator)) + : TGenCollectionProxy(typeid(Cont_t), sizeof(Cont_t::iterator)) { // Build a Streamer for a collection whose type is described by 'collectionClass'. @@ -88,9 +88,9 @@ void TEmulatedCollectionProxy::Destructor(void* p, Bool_t dtorOnly) const const_cast(this)->Clear("force"); } if (dtorOnly) { - ((Cont_t*)p)->~Cont_t(); + WithCont(p, [](auto *c, std::size_t) { using Vec_t = std::decay_t; c->~Vec_t(); }); } else { - delete (Cont_t*) p; + WithCont(p, [](auto *c, std::size_t) { delete c; }); } } @@ -102,7 +102,7 @@ void TEmulatedCollectionProxy::DeleteArray(void* p, Bool_t dtorOnly) const // how many elements are in the array. Warning("DeleteArray", "Cannot properly delete emulated array of %s at %p, I don't know how many elements it has!", fClass->GetName(), p); if (!dtorOnly) { - delete[] (Cont_t*) p; + ::operator delete(p); } } @@ -133,13 +133,11 @@ TGenCollectionProxy *TEmulatedCollectionProxy::InitializeEx(Bool_t silent) // Note: an emulated collection proxy is never really associative // since under-neath is actually an array. - // std::cout << "Initialized " << typeid(*this).name() << ":" << fName << std::endl; - auto alignedSize = [](size_t in) { - constexpr size_t kSizeOfPtr = sizeof(void*); - return in + (kSizeOfPtr - in%kSizeOfPtr)%kSizeOfPtr; + auto alignedSize = [](size_t in, TClass *align_cl) { + size_t align = align_cl ? align_cl->GetClassAlignment() : alignof(std::max_align_t); + return in + (align - in % align) % align; }; - struct GenerateTemporaryTEnum - { + struct GenerateTemporaryTEnum { TEnum *fTemporaryTEnum = nullptr; GenerateTemporaryTEnum(UInt_t typecase, const std::string &enumname) @@ -195,10 +193,10 @@ TGenCollectionProxy *TEmulatedCollectionProxy::InitializeEx(Bool_t silent) fProperties |= kNeedDelete; } if ( 0 == fValOffset ) { - fValOffset = alignedSize(fKey->fSize); + fValOffset = alignedSize(fKey->fSize, (*fValue).fType.GetClass()); } if ( 0 == fValDiff ) { - fValDiff = alignedSize(fValOffset + fVal->fSize); + fValDiff = alignedSize(fValOffset + fVal->fSize, (*fValue).fType.GetClass()); } if (num > 3 && !inside[3].empty()) { if (! TClassEdit::IsDefAlloc(inside[3].c_str(),inside[0].c_str())) { @@ -268,7 +266,6 @@ void TEmulatedCollectionProxy::Shrink(UInt_t nCurr, UInt_t left, Bool_t force ) // Shrink the container typedef std::string String_t; - PCont_t c = PCont_t(fEnv->fObject); char* addr = ((char*)fEnv->fStart) + fValDiff*left; size_t i; @@ -363,8 +360,10 @@ void TEmulatedCollectionProxy::Shrink(UInt_t nCurr, UInt_t left, Bool_t force ) break; } } - c->resize(left*fValDiff,0); - fEnv->fStart = left > 0 ? c->data() : 0; + WithCont(fEnv->fObject, [&](auto *c, std::size_t elemSize) { + c->resize(left * fValDiff / elemSize); + fEnv->fStart = left > 0 ? c->data() : nullptr; + }); return; } @@ -372,10 +371,11 @@ void TEmulatedCollectionProxy::Expand(UInt_t nCurr, UInt_t left) { // Expand the container size_t i; - PCont_t c = PCont_t(fEnv->fObject); - c->resize(left*fValDiff,0); void *oldstart = fEnv->fStart; - fEnv->fStart = left > 0 ? c->data() : 0; + WithCont(fEnv->fObject, [&](auto *c, std::size_t elemSize) { + c->resize(left * fValDiff / elemSize); + fEnv->fStart = left > 0 ? c->data() : nullptr; + }); char* addr = ((char*)fEnv->fStart) + fValDiff*nCurr; switch ( fSTL_type ) { diff --git a/io/io/src/TFile.cxx b/io/io/src/TFile.cxx index 44f2c8a912bdb..459919ca42541 100644 --- a/io/io/src/TFile.cxx +++ b/io/io/src/TFile.cxx @@ -13,15 +13,35 @@ \file TFile.cxx \class TFile \ingroup IO -\brief A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-like logical structure, possibly including subdirectory hierarchies. +\brief A file, usually with extension .root, that stores data and code in the form of serialized objects in a +file-system-like logical structure, possibly including subdirectory hierarchies. +\note ROOT files contain data, and executable code, for example through TExec, TMacro, and TFormula instances. As for all files, **do not open ROOT files from an unknown origin!** \note See also \ref IO \note See also \ref rootio (or `io/doc/TFile` folder in your codebase) +ROOT files a are an efficient mean to store C++ class instances, e.g. data, +both as individual objects, in a so called *row-wise fashion*, and in a +*so-called columnar fashion*. Also executable code can be stored in ROOT files, +for example in the form of TMacro, TExec or TFormula instances, and the +related federation of classes. + +For example, a TCanvas or TPad instance may rely on TExec instances stored in +their *list of executables* to obtain certain graphics effects: in this case, +code will be executed upon drawing. A TH1 or a TGraph instance, as well as +their multidimensional counterparts and derived classes, may also execute code +upon drawing through TExec instances stored in their *list of functions*. +Another example of code which is executable is represented by TFormula +instances, that are the "computational workhorse" of function classes such as +TF1, its multidimensional counterparts, and related classes. There, jitted C++ +code is executed for example upon evaluation, for example during fits or +drawing operations, to obtain maximum runtime performance. + +
ROOT file data format specification A ROOT file is composed of a header, followed by consecutive data records -(`TKey` instances) with a well defined format. +(TKey instances) with a well defined format. The first data record starts at byte fBEGIN (currently set to kBEGIN). Bytes 1->kBEGIN contain the file description, when fVersion >= 1000000 @@ -225,18 +245,18 @@ TFile::TFile() : TDirectoryFile(), fCompress(ROOT::RCompressionSetting::EAlgorit /// /// Option | Description /// -------|------------ -/// NEW or CREATE | Create a new file and open it for writing, if the file already exists the file is not opened. -/// RECREATE | Create a new file, if the file already exists it will be overwritten. -/// UPDATE | Open an existing file for writing. If no file exists, it is created. -/// READ | Open an existing file for reading (default). -/// NET | Used by derived remote file access classes, not a user callable option. -/// WEB | Used by derived remote http access class, not a user callable option. -/// READ_WITHOUT_GLOBALREGISTRATION | Used by TTreeProcessorMT, not a user callable option. +/// NEW or CREATE | Create a new file and open it for writing, if the file already exists the file +/// is not opened. RECREATE | Create a new file, if the file already exists it will be +/// overwritten. UPDATE | Open an existing file for writing. If no file exists, it is +/// created. READ | Open an existing file for reading (default). NET | Used by derived +/// remote file access classes, not a user callable option. WEB | Used by derived remote +/// http access class, not a user callable option. READ_WITHOUT_GLOBALREGISTRATION | Used by TTreeProcessorMT, not a +/// user callable option. /// /// If option = "" (default), READ is assumed. -/// \note Even in READ mode, if the file is the current directory `cd()`, and you create e.g. a new histogram in your code, -/// the histogram will be appended (but not written) to this directory, and automatically deleted when closing the file. -/// To avoid this behavior, call hist->SetDirectory(nullptr); after creating it. +/// \note Even in READ mode, if the file is the current directory `cd()`, and you create e.g. a new histogram in your +/// code, the histogram will be appended (but not written) to this directory, and automatically deleted when closing the +/// file. To avoid this behavior, call hist->SetDirectory(nullptr); after creating it. /// /// The file can be specified as a URL of the form: /// @@ -255,12 +275,12 @@ TFile::TFile() : TDirectoryFile(), fCompress(ROOT::RCompressionSetting::EAlgorit /// /// file.tar?filetype=raw /// -/// This is convenient because the many remote file access plugins allow -/// easy access to/from the many different mass storage systems. +/// This can be convenient because the many file access plugins allow +/// easy access to remote endpoints, e.g. mass storage pools. /// The title of the file (ftitle) will be shown by the ROOT browsers. /// A ROOT file (like a Unix file system) may contain objects and -/// directories. There are no restrictions for the number of levels -/// of directories. +/// directories, as well as executable code. There are no restrictions +/// for the number of levels of directories. /// A ROOT file is designed such that one can write in the file in pure /// sequential mode (case of BATCH jobs). In this case, the file may be /// read sequentially again without using the file index written @@ -288,7 +308,9 @@ TFile::TFile() : TDirectoryFile(), fCompress(ROOT::RCompressionSetting::EAlgorit /// The enumeration ROOT::RCompressionSetting::EAlgorithm associates each /// algorithm with a number. There is a utility function to help /// to set the value of compress. For example, +/// /// ROOT::CompressionSettings(ROOT::kLZMA, 1) +/// /// will build an integer which will set the compression to use /// the LZMA algorithm and compression level 1. These are defined /// in the header file Compression.h. @@ -315,7 +337,7 @@ TFile::TFile() : TDirectoryFile(), fCompress(ROOT::RCompressionSetting::EAlgorit /// } /// ~~~ /// When opening the file, the system checks the validity of this directory. -/// If something wrong is detected, an automatic Recovery is performed. In +/// If something wrong is detected, an automatic recovery is performed. In /// this case, the file is scanned sequentially reading all logical blocks /// and attempting to rebuild a correct directory (see TFile::Recover). /// One can disable the automatic recovery procedure when reading one diff --git a/io/io/src/TGenCollectionProxy.cxx b/io/io/src/TGenCollectionProxy.cxx index 3defdece6e7fd..6a512b67d989f 100644 --- a/io/io/src/TGenCollectionProxy.cxx +++ b/io/io/src/TGenCollectionProxy.cxx @@ -1520,7 +1520,6 @@ void TGenCollectionProxy__VectorCreateIterators(void *obj, void **begin_arena, v } *begin_arena = vec->data(); *end_arena = vec->data() + vec->size(); - } //////////////////////////////////////////////////////////////////////////////// @@ -1624,7 +1623,9 @@ TVirtualCollectionProxy::CreateIterators_t TGenCollectionProxy::GetFunctionCreat // fprintf(stderr,"a generic iterator\n"); // TODO could we do better than SlowCreateIterators for RVec? - if (fSTL_type==ROOT::kSTLvector || (fProperties & kIsEmulated)) + if (fProperties & kIsEmulated) + return fFunctionCreateIterators = TGenCollectionProxy__VectorCreateIterators; + else if (fSTL_type==ROOT::kSTLvector) return fFunctionCreateIterators = TGenCollectionProxy__VectorCreateIterators; else if ( (fProperties & kIsAssociative) && read) return TGenCollectionProxy__StagingCreateIterators; diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 3900b54b37cf8..801d65ab8ff3e 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -76,7 +76,9 @@ element type. #include "TStreamerInfoActions.h" #include +#include #include +#include std::atomic TStreamerInfo::fgCount{0}; @@ -154,6 +156,7 @@ TStreamerInfo::TStreamerInfo() fNfulldata= 0; fNslots = 0; fSize = 0; + fAlignment= 0; fClassVersion = 0; fOnFileClassVersion = 0; fOldVersion = Class()->GetClassVersion(); @@ -188,6 +191,7 @@ TStreamerInfo::TStreamerInfo(TClass *cl) fNfulldata= 0; fNslots = 0; fSize = 0; + fAlignment= 0; fClassVersion = fClass->GetClassVersion(); fOnFileClassVersion = 0; fOldVersion = Class()->GetClassVersion(); @@ -240,6 +244,15 @@ TStreamerInfo::~TStreamerInfo() /// Makes sure kBuildRunning reset once Build finishes. namespace { + /// Round \p value up to the next multiple of \p align. + /// \p align must be a power of two. + template + inline T AlignUp(T value, T align) + { + assert((align & (align - 1)) == 0); // must be a power of two + return (value + align - 1) & ~(align - 1); + } + struct TPreventRecursiveBuildGuard { TPreventRecursiveBuildGuard(TStreamerInfo* info): fInfo(info) { fInfo->SetBit(TStreamerInfo::kBuildRunning); @@ -1912,8 +1925,6 @@ void TStreamerInfo::BuildOld() Int_t offset = 0; TMemberStreamer* streamer = 0; - constexpr size_t kSizeOfPtr = sizeof(void*); - int nBaze = 0; if ((fElements->GetEntriesFast() == 1) && !strcmp(fElements->At(0)->GetName(), "This")) { @@ -2098,6 +2109,7 @@ void TStreamerInfo::BuildOld() } element->SetOffset(baseOffset); offset += baseclass->Size(); + fAlignment = std::max(fAlignment, baseclass->GetClassAlignment()); continue; } else { @@ -2195,6 +2207,7 @@ void TStreamerInfo::BuildOld() fVirtualInfoLoc = new ULong_t[1]; // To allow for a single delete statement. fVirtualInfoLoc[0] = offset; offset += sizeof(TStreamerInfo*); + fAlignment = std::max(fAlignment, sizeof(TStreamerInfo*)); } TDataMember* dm = 0; @@ -2640,12 +2653,20 @@ void TStreamerInfo::BuildOld() // Regular case asize = element->GetSize(); } - // align the non-basic data types (required on alpha and IRIX!!) - if ((offset % kSizeOfPtr) != 0) { - offset = offset - (offset % kSizeOfPtr) + kSizeOfPtr; - } + // Use the precise alignment of the element type, falling back to + // max_align_t for types whose alignment is not known. + std::size_t align = alignof(std::max_align_t); + if (element->GetClass() && element->GetClass()->GetClassAlignment()) + align = element->GetClass()->GetClassAlignment(); + else if (auto *eldt = TDataType::GetDataType((EDataType)element->GetType()); eldt && eldt->GetAlignOf()) + align = eldt->GetAlignOf(); + offset = (Int_t)AlignUp((std::size_t)offset, align); element->SetOffset(offset); offset += asize; + if (element->GetClass()) + fAlignment = std::max(fAlignment, element->GetClass()->GetClassAlignment()); + else if (auto *eldt = TDataType::GetDataType((EDataType)element->GetType()); eldt && eldt->GetAlignOf()) + fAlignment = std::max(fAlignment, eldt->GetAlignOf()); } if (!wasCompiled && rules) { @@ -3326,12 +3347,12 @@ void TStreamerInfo::ComputeSize() fSize = fVirtualInfoLoc[0] + sizeof(TStreamerInfo*); } - // On some platform and in some case of layout non-basic data types needs - // to be aligned. So let's be on the safe side and align on the size of - // the pointers. (Question: is that the right thing on x32 ABI ?) - constexpr size_t kSizeOfPtr = sizeof(void*); - if ((fSize % kSizeOfPtr) != 0 && !fClass->IsSyntheticPair()) { - fSize = fSize - (fSize % kSizeOfPtr) + kSizeOfPtr; + // If we have no information use the default alignment. + if (!fAlignment) { + fAlignment = alignof(std::max_align_t); + } + if ((fSize % fAlignment) != 0) { + fSize = (Int_t)AlignUp((size_t)fSize, fAlignment); } } @@ -4991,8 +5012,12 @@ void* TStreamerInfo::New(void *obj) TIter next(fElements); if (!p) { - // Allocate and initialize the memory block. - p = new char[fSize]; + // Allocate and initialize the memory block. Ensure the returned + // storage is aligned to the class alignment requirement. + auto align = fClass->GetClassAlignment(); + // Use aligned new (C++17). This will return memory aligned to + // 'align' and can be freed with the matching delete[]. + p = static_cast(::operator new[](fSize, std::align_val_t(align))); memset(p, 0, fSize); } @@ -5146,22 +5171,39 @@ void* TStreamerInfo::NewArray(Long_t nElements, void *ary) char* p = (char*) ary; if (!p) { - Long_t len = nElements * size + sizeof(Long_t)*2; - p = new char[len]; + // Determine the alignment requirement for the class. + const std::size_t align = fClass->GetClassAlignment(); + // The header holds two Long_t cookie values (size and nElements). + // Round the header size up to the next multiple of 'align' so that + // dataBegin (= p + headerSize) is itself aligned to 'align'. + const std::size_t cookieSize = sizeof(Long_t) * 2; + const std::size_t headerSize = AlignUp(cookieSize, align); + + Long_t len = nElements * size + headerSize; + + // Allocate and initialize the memory block. Request alignment so + // that the raw block starts on an 'align'-boundary; combined with + // the rounded-up header this guarantees dataBegin is also aligned. + p = static_cast(::operator new[](len, std::align_val_t(align))); memset(p, 0, len); } - // Store the array cookie - Long_t* r = (Long_t*) p; + // Store the array cookie in the two Long_t slots immediately before dataBegin. + // Recompute headerSize from the class alignment so the layout matches DeleteArray. + const std::size_t align = fClass->GetClassAlignment(); + const std::size_t cookieSize = sizeof(Long_t) * 2; + const std::size_t headerSize = AlignUp(cookieSize, align); + + Long_t* r = (Long_t*)(p + headerSize - cookieSize); r[0] = size; r[1] = nElements; - char* dataBegin = (char*) &r[2]; + char* dataBegin = p + headerSize; // Do a placement new for each element. - p = dataBegin; + char* q = dataBegin; for (Long_t cnt = 0; cnt < nElements; ++cnt) { - New(p); - p += size; + New(q); + q += size; } // for nElements return dataBegin; @@ -5306,7 +5348,7 @@ void TStreamerInfo::DestructorImpl(void* obj, Bool_t dtorOnly) } // iter over elements if (!dtorOnly) { - delete[] p; + ::operator delete[](p, std::align_val_t(fClass->GetClassAlignment())); } } @@ -5350,10 +5392,17 @@ void TStreamerInfo::DeleteArray(void* ary, Bool_t dtorOnly) //???FIX ME: What about varying length arrays? - Long_t* r = (Long_t*) ary; - Long_t arrayLen = r[-1]; - Long_t size = r[-2]; - char* memBegin = (char*) &r[-2]; + // Recover the cookie layout: the two Long_t values sit in the header + // block immediately before dataBegin, with the same alignment-based + // headerSize that NewArray used. + const std::size_t align = fClass->GetClassAlignment(); + const std::size_t cookieSize = sizeof(Long_t) * 2; + const std::size_t headerSize = ((cookieSize + align - 1) / align) * align; + + Long_t* r = (Long_t*)((char*)ary - cookieSize); + Long_t arrayLen = r[1]; + Long_t size = r[0]; + char* memBegin = (char*)ary - headerSize; char* p = ((char*) ary) + ((arrayLen - 1) * size); for (Long_t cnt = 0; cnt < arrayLen; ++cnt, p -= size) { @@ -5362,7 +5411,7 @@ void TStreamerInfo::DeleteArray(void* ary, Bool_t dtorOnly) } // for arrayItemSize if (!dtorOnly) { - delete[] memBegin; + ::operator delete[](memBegin, std::align_val_t(align)); } } @@ -5910,7 +5959,7 @@ TStreamerInfo::GenExplicitClassStreamer( const ::ROOT::TCollectionProxyInfo &inf // // Utility functions // -static TStreamerElement* R__CreateEmulatedElement(const char *dmName, const std::string &dmFull, Int_t offset, bool silent) +static TStreamerElement* R__CreateEmulatedElement(const char *dmName, const std::string &dmFull, Int_t offset, bool silent, bool needAlign) { // Create a TStreamerElement for the type 'dmFull' and whose data member name is 'dmName'. @@ -5918,12 +5967,22 @@ static TStreamerElement* R__CreateEmulatedElement(const char *dmName, const std: TString dmType( TClassEdit::ShortType(dmFull.c_str(),1) ); Bool_t dmIsPtr = (s1 != dmType); const char *dmTitle = "Emulation"; + // Over align the basic data types. + size_t align = alignof(std::max_align_t); + if (needAlign && offset % align != 0) + offset = (Int_t)AlignUp((size_t)offset, align); TDataType *dt = gROOT->GetType(dmType); if (dt && dt->GetType() > 0 ) { // found a basic type Int_t dsize,dtype; dtype = dt->GetType(); dsize = dt->Size(); + // Use the precise alignment for the basic type if available. + if (needAlign && dt->GetAlignOf()) { + align = dt->GetAlignOf(); + if (offset % align != 0) + offset = (Int_t)AlignUp((size_t)offset, align); + } if (dmIsPtr && dtype != kCharStar) { if (!silent) Error("Pair Emulation Building","%s is not yet supported in pair emulation", @@ -5969,6 +6028,9 @@ static TStreamerElement* R__CreateEmulatedElement(const char *dmName, const std: } } // a class + align = std::max(align, clm->GetClassAlignment()); + if (needAlign && align != alignof(std::max_align_t) && ((offset % align) != 0)) + offset = (Int_t)AlignUp((size_t)offset, align); if (clm->IsTObject()) { return new TStreamerObject(dmName,dmTitle,offset,dmFull.c_str()); } else if(clm == TString::Class() && !dmIsPtr) { @@ -6012,24 +6074,30 @@ TVirtualStreamerInfo *TStreamerInfo::GenerateInfoForPair(const std::string &firs i->SetName(pname.c_str()); i->SetClass(nullptr); i->GetElements()->Delete(); - TStreamerElement *fel = R__CreateEmulatedElement("first", firstname, 0, silent); + TStreamerElement *fel = R__CreateEmulatedElement("first", firstname, 0, silent, /*needAlign=*/false); Int_t size = 0; + size_t align = alignof(std::max_align_t); if (fel) { - i->GetElements()->Add( fel ); + i->GetElements()->Add(fel); - size = fel->GetSize(); - Int_t sp = sizeof(void *); - //align the non-basic data types (required on alpha and IRIX!!) - if (size%sp != 0) size = size - size%sp + sp; + if (fel->GetClass() && fel->GetClass()->GetClassAlignment()) + i->fAlignment = std::max(align, fel->GetClass()->GetClassAlignment()); + else if (auto *dt = TDataType::GetDataType((EDataType)fel->GetType()); dt && dt->GetAlignOf()) + i->fAlignment = std::max(align, dt->GetAlignOf()); } else { delete i; return 0; } if (hint_pair_offset) size = hint_pair_offset; - TStreamerElement *second = R__CreateEmulatedElement("second", secondname, size, silent); + TStreamerElement *second = + R__CreateEmulatedElement("second", secondname, size, silent, /*needAlign=*/!hint_pair_offset); if (second) { - i->GetElements()->Add( second ); + i->GetElements()->Add(second); + if (second->GetClass() && second->GetClass()->GetClassAlignment()) + i->fAlignment = std::max(align, second->GetClass()->GetClassAlignment()); + else if (auto *dt = TDataType::GetDataType((EDataType)second->GetType()); dt && dt->GetAlignOf()) + i->fAlignment = std::max(align, dt->GetAlignOf()); } else { delete i; return 0; diff --git a/io/io/test/RRawFile.cxx b/io/io/test/RRawFile.cxx index 1857698d43c5a..3a861e1618856 100644 --- a/io/io/test/RRawFile.cxx +++ b/io/io/test/RRawFile.cxx @@ -15,6 +15,7 @@ class RRawFileMock : public RRawFile { public: std::string fContent; unsigned fNumReadAt; + bool fDiscourageReadAhead = false; RRawFileMock(const std::string &content, RRawFile::ROptions options) : RRawFile("", options), fContent(content), fNumReadAt(0) { } @@ -39,6 +40,8 @@ class RRawFileMock : public RRawFile { } std::uint64_t GetSizeImpl() final { return fContent.size(); } + + void SetDiscourageReadAheadImpl(bool val) final { fDiscourageReadAhead = val; } }; } // anonymous namespace @@ -272,3 +275,22 @@ TEST(RRawFileTFile, TFile) EXPECT_EQ(seek[2], 0); EXPECT_EQ(seek[3], 100); } + +TEST(RRawFile, Readahead) +{ + { + RRawFileMock mock("", ROOT::Internal::RRawFile::ROptions()); + (void)mock.GetSize(); + EXPECT_FALSE(mock.fDiscourageReadAhead); + } + + { + RRawFileMock mock("", ROOT::Internal::RRawFile::ROptions()); + mock.SetBuffering(false); + EXPECT_FALSE(mock.fDiscourageReadAhead); + (void)mock.GetSize(); + EXPECT_TRUE(mock.fDiscourageReadAhead); + mock.SetBuffering(true); + EXPECT_FALSE(mock.fDiscourageReadAhead); + } +} diff --git a/js/LICENSE b/js/LICENSE index b9aea0154d89e..a6d9f24717fe9 100644 --- a/js/LICENSE +++ b/js/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright � 2013-2025 JavaScript ROOT authors +Copyright � 2013-2026 JavaScript ROOT authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/js/build/jsroot.js b/js/build/jsroot.js index 293dfc20b5d68..86bd090c6c117 100644 --- a/js/build/jsroot.js +++ b/js/build/jsroot.js @@ -14,7 +14,7 @@ const version_id = 'dev', /** @summary version date * @desc Release date in format day/month/year like '14/04/2022' */ -version_date = '7/11/2025', +version_date = '10/03/2026', /** @summary version id and date * @desc Produced by concatenation of {@link version_id} and {@link version_date} @@ -257,6 +257,8 @@ settings = { Render3DBatch: constants$1.Render3D.Default, /** @summary Way to embed 3D drawing in SVG, see {@link constants.Embed3D} for possible values */ Embed3D: constants$1.Embed3D.Default, + /** @summary Use `resvg-js` backend for converting SVGs in node.js */ + UseResvgJs: true, /** @summary Default canvas width */ CanvasWidth: 1200, /** @summary Default canvas height */ @@ -363,6 +365,10 @@ settings = { * @desc Allows to retry files reading if original URL fails * @private */ FilesRemap: { 'https://root.cern/': 'https://root-eos.web.cern.ch/' }, + /** @summary THttpServer read timeout in ms + * @desc Configures timeout for requests to THttpServer + * @default 0 */ + ServerTimeout: 0, /** @summary Configure xhr.withCredentials = true when submitting http requests from JSROOT */ WithCredentials: false, /** @summary Skip streamer infos from the GUI */ @@ -965,13 +971,22 @@ function findFunction(name) { /** @summary Method to create http request, without promise can be used only in browser environment * @private */ -function createHttpRequest(url, kind, user_accept_callback, user_reject_callback, use_promise) { +function createHttpRequest(url, kind, user_accept_callback, user_reject_callback, use_promise, tmout) { + function handle_error(xhr, message, code, abort_reason) { + if (!xhr.did_abort) { + xhr.did_abort = abort_reason || true; + xhr.abort(); + } + if (!xhr.did_error || abort_reason) + console.warn(message); + if (!xhr.did_error) { + xhr.did_error = true; + xhr.error_callback(Error(message), code); + } + } function configureXhr(xhr) { xhr.http_callback = isFunc(user_accept_callback) ? user_accept_callback.bind(xhr) : () => {}; - xhr.error_callback = isFunc(user_reject_callback) ? user_reject_callback.bind(xhr) : function(err) { - console.warn(err.message); - this.http_callback(null); - }.bind(xhr); + xhr.error_callback = isFunc(user_reject_callback) ? user_reject_callback.bind(xhr) : function() { this.http_callback(null); }; if (!kind) kind = 'buf'; @@ -1007,11 +1022,8 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback if (settings.HandleWrongHttpResponse && (method === 'GET') && isFunc(xhr.addEventListener)) { xhr.addEventListener('progress', function(oEvent) { - if (oEvent.lengthComputable && this.expected_size && (oEvent.loaded > this.expected_size)) { - this.did_abort = true; - this.abort(); - this.error_callback(Error(`Server sends more bytes ${oEvent.loaded} than expected ${this.expected_size}. Abort I/O operation`), 598); - } + if (oEvent.lengthComputable && this.expected_size && (oEvent.loaded > this.expected_size)) + handle_error(this, `Server sends more bytes ${oEvent.loaded} than expected ${this.expected_size}. Abort I/O operation`, 598); }.bind(xhr)); } @@ -1021,11 +1033,8 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback if ((this.readyState === 2) && this.expected_size) { const len = parseInt(this.getResponseHeader('Content-Length')); - if (Number.isInteger(len) && (len > this.expected_size) && !settings.HandleWrongHttpResponse) { - this.did_abort = 'large'; - this.abort(); - return this.error_callback(Error(`Server response size ${len} larger than expected ${this.expected_size}. Abort I/O operation`), 599); - } + if (Number.isInteger(len) && (len > this.expected_size) && !settings.HandleWrongHttpResponse) + return handle_error(this, `Server response size ${len} larger than expected ${this.expected_size}. Abort I/O operation`, 599, 'large'); } if (this.readyState !== 4) @@ -1034,11 +1043,11 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback if ((this.status !== 200) && (this.status !== 206) && !browser.qt6 && // in these special cases browsers not always set status !((this.status === 0) && ((url.indexOf('file://') === 0) || (url.indexOf('blob:') === 0)))) - return this.error_callback(Error(`Fail to load url ${url}`), this.status); + return handle_error(this, `Fail to load url ${url}`, this.status); if (this.nodejs_checkzip && (this.getResponseHeader('content-encoding') === 'gzip')) { // special handling of gzip JSON objects in Node.js - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(handle => { + return import('zlib').then(handle => { const res = handle.unzipSync(Buffer.from(this.response)), obj = JSON.parse(res); // zlib returns Buffer, use JSON to parse it return this.http_callback(parse$1(obj)); @@ -1079,13 +1088,18 @@ function createHttpRequest(url, kind, user_accept_callback, user_reject_callback xhr.responseType = 'arraybuffer'; } + if (tmout && Number.isFinite(tmout)) { + xhr.timeout = tmout; + xhr.ontimeout = function() { handle_error(this, `Request ${url} timeout set ${tmout} ms`, 600, 'timeout'); }; + } + return xhr; } if (isNodeJs()) { if (!use_promise) throw Error('Not allowed to create http requests in node.js without promise'); - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(h => configureXhr(new h.default())); + return import('xhr2').then(h => configureXhr(new h.default())); } const xhr = configureXhr(new XMLHttpRequest()); @@ -1125,9 +1139,9 @@ async function httpRequest(url, kind, post_data) { async function injectCode(code) { if (nodejs) { let name, fs; - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(tmp => { + return import('tmp').then(tmp => { name = tmp.tmpNameSync() + '.js'; - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }); + return import('fs'); }).then(_fs => { fs = _fs; fs.writeFileSync(name, code); @@ -1210,7 +1224,7 @@ async function loadScript(url) { // local files, read and use it if (url.indexOf('./') === 0) - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(fs => injectCode(fs.readFileSync(url))); + return import('fs').then(fs => injectCode(fs.readFileSync(url))); return import(/* webpackIgnore: true */ url); } @@ -2911,11 +2925,11 @@ function compareValue(compare) { }; } -function chord() { - return chord$1(false); +function chord_default() { + return chord(false); } -function chord$1(directed, transpose) { +function chord(directed, transpose) { var padAngle = 0, sortGroups = null, sortSubgroups = null, @@ -3266,7 +3280,7 @@ function ribbon(headRadius) { return ribbon; } -function ribbon$1() { +function ribbon_default() { return ribbon(); } @@ -5174,8 +5188,8 @@ function formatDecimal(x) { // significant digits p, where x is positive and p is in [1, 21] or undefined. // For example, formatDecimalParts(1.23) returns ["123", 0]. function formatDecimalParts(x, p) { - if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity - var i, coefficient = x.slice(0, i); + if (!isFinite(x) || x === 0) return null; // NaN, ±Infinity, ±0 + var i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e"), coefficient = x.slice(0, i); // The string returned by toExponential either has the form \d\.\d+e[-+]\d+ // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3). @@ -5280,7 +5294,7 @@ var prefixExponent; function formatPrefixAuto(x, p) { var d = formatDecimalParts(x, p); - if (!d) return x + ""; + if (!d) return prefixExponent = undefined, x.toPrecision(p); var coefficient = d[0], exponent = d[1], i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1, @@ -5334,7 +5348,7 @@ function formatLocale$1(locale) { minus = locale.minus === undefined ? "−" : locale.minus + "", nan = locale.nan === undefined ? "NaN" : locale.nan + ""; - function newFormat(specifier) { + function newFormat(specifier, options) { specifier = formatSpecifier(specifier); var fill = specifier.fill, @@ -5359,8 +5373,8 @@ function formatLocale$1(locale) { // Compute the prefix and suffix. // For SI-prefix, the suffix is lazily computed. - var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "", - suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : ""; + var prefix = (options && options.prefix !== undefined ? options.prefix : "") + (symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : ""), + suffix = (symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "") + (options && options.suffix !== undefined ? options.suffix : ""); // What format function should we use? // Is this an integer type? @@ -5401,7 +5415,7 @@ function formatLocale$1(locale) { // Compute the prefix and suffix. valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix; - valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); + valueSuffix = (type === "s" && !isNaN(value) && prefixExponent !== undefined ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : ""); // Break the formatted value into the integer “value” part that can be // grouped, and fractional or exponential “suffix” part that is not. @@ -5446,12 +5460,11 @@ function formatLocale$1(locale) { } function formatPrefix(specifier, value) { - var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)), - e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, + var e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3, k = Math.pow(10, -e), - prefix = prefixes[8 + e / 3]; + f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier), {suffix: prefixes[8 + e / 3]}); return function(value) { - return f(k * value) + prefix; + return f(k * value); }; } @@ -5731,9 +5744,9 @@ function loggish(transform) { return scale; } -function log() { +function log$1() { const scale = loggish(transformer$2()).domain([1, 10]); - scale.copy = () => copy$1(scale, log()).base(scale.base()); + scale.copy = () => copy$1(scale, log$1()).base(scale.base()); initRange.apply(scale, arguments); return scale; } @@ -7262,14 +7275,14 @@ function clearNow() { clockNow = 0; } -function Timer() { +function Timer$1() { this._call = this._time = this._next = null; } -Timer.prototype = timer.prototype = { - constructor: Timer, +Timer$1.prototype = timer.prototype = { + constructor: Timer$1, restart: function(callback, delay, time) { if (typeof callback !== "function") throw new TypeError("callback is not a function"); time = (time == null ? now() : +time) + (delay == null ? 0 : +delay); @@ -7292,7 +7305,7 @@ Timer.prototype = timer.prototype = { }; function timer(callback, delay, time) { - var t = new Timer; + var t = new Timer$1; t.restart(callback, delay, time); return t; } @@ -7354,7 +7367,7 @@ function sleep(time) { } function timeout(callback, delay, time) { - var t = new Timer; + var t = new Timer$1; delay = delay == null ? 0 : +delay; t.restart(elapsed => { t.stop(); @@ -8510,10 +8523,28 @@ function createGrayPalette() { return new ColorPalette(palette); } +/** @summary Set list of colors for specified color palette + * @desc One also can redefine existing palette + * Array should contain several colors in RGB format like `rgb(10,10,10)` or `#ff00ff` + * @private */ + +const customPalettes = {}; + +function setColorPalette(id, colors) { + if (!Number.isInteger(id) || (id < 0) || !colors?.length) + return false; + + customPalettes[id] = colors; + return true; +} + /** @summary Create color palette * @private */ function getColorPalette(id, grayscale) { id = id || settings.Palette; + if (customPalettes[id]) + return new ColorPalette(customPalettes[id], grayscale); + if ((id > 0) && (id < 10)) return createGrayPalette(); if (id < 51) @@ -9536,7 +9567,7 @@ class BasePainter { * @return {Promise} with d3 selection for d3_body * @private */ async function _loadJSDOM() { - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(handle => { + return import('jsdom').then(handle => { if (!internals.nodejs_window) { internals.nodejs_window = (new handle.JSDOM('hello')).window; internals.nodejs_document = internals.nodejs_window.document; // used with three.js @@ -9601,9 +9632,14 @@ async function svgToImage(svg, image_format, args) { return internals.makePDF ? internals.makePDF(svg, args) : null; // required with df104.py/df105.py example with RCanvas or any special symbols in TLatex - const doctype = ''; + const doctype = '', + is_rgba = image_format === 'rgba'; if (isNodeJs()) { + if (image_format === 'jpeg') { + console.log('JPEG image format not supported in node.js, use PNG'); + return null; + } svg = encodeURIComponent(doctype + svg); svg = svg.replace(/%([0-9A-F]{2})/g, (match, p1) => { const c = String.fromCharCode('0x' + p1); @@ -9612,7 +9648,32 @@ async function svgToImage(svg, image_format, args) { const img_src = 'data:image/svg+xml;base64,' + btoa_func(decodeURIComponent(svg)); - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(async handle => { + // Use the newer and stabler `resvg-js` backend for converting SVG to PNG + if (settings.UseResvgJs) { + return import('@resvg/resvg-js').then(({ Resvg }) => { + const rawSvg = decodeURIComponent(svg), // raw SVG XML + resvg = new Resvg(rawSvg), // Initialize Resvg and create the PNG buffer + renderData = resvg.render(), + pngBuffer = renderData.asPng(); + + // Return raw RGBA pixels if caller requested it + if (is_rgba) { + return { + width: renderData.width, + height: renderData.height, + data: renderData.pixels + }; + } + + if (args?.as_buffer) + return pngBuffer; + + return 'data:image/png;base64,' + pngBuffer.toString('base64'); + }); + } + + // Fallback to `node-canvas` + return import('canvas').then(async handle => { return handle.default.loadImage(img_src).then(img => { const canvas = handle.default.createCanvas(img.width, img.height); @@ -9621,7 +9682,7 @@ async function svgToImage(svg, image_format, args) { if (args?.as_buffer) return canvas.toBuffer('image/' + image_format); - return image_format ? canvas.toDataURL('image/' + image_format) : canvas; + return image_format && !is_rgba ? canvas.toDataURL('image/' + image_format) : canvas; }); }); } @@ -9643,7 +9704,7 @@ async function svgToImage(svg, image_format, args) { if (args?.as_buffer && image_format) canvas.toBlob(blob => blob.arrayBuffer().then(resolveFunc), 'image/' + image_format); else - resolveFunc(image_format ? canvas.toDataURL('image/' + image_format) : canvas); + resolveFunc(image_format && !is_rgba ? canvas.toDataURL('image/' + image_format) : canvas); }; image.onerror = function(arg) { URL.revokeObjectURL(img_src); @@ -9759,7 +9820,7 @@ async function loadFontFile(fname) { } let path = locations.shift() + fname; console.log('loading font', path); - const pr = isNodeJs() ? Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(fs => { + const pr = isNodeJs() ? import('fs').then(fs => { const prefix = 'file://' + (process?.platform === 'win32' ? '/' : ''); if (path.indexOf(prefix) === 0) path = path.slice(prefix.length); @@ -11124,7 +11185,7 @@ async function loadMathjax() { return _loadJSDOM().then(handle => { JSDOM = handle.JSDOM; - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }); + return import('mathjax'); }).then(mj0 => { // return Promise with mathjax loading mj0.init({ @@ -14301,9 +14362,8 @@ class ObjectPainter extends BasePainter { } /** @summary Configure user-defined tooltip handler - * @desc Hook for the users to get tooltip information when mouse cursor moves over frame area + * @desc Hook for the users to get tooltip information when mouse cursor moves over then object * Handler function will be called every time when new data is selected - * when mouse leave frame area, handler(null) will be called * @param {function} handler - function called when tooltip is produced * @param {number} [tmout = 100] - delay in ms before tooltip delivered */ configureUserTooltipHandler(handler, tmout = 100) { @@ -14591,7 +14651,7 @@ Object.assign(internals.jsroot, { ObjectPainter, cleanup, resize }); * Copyright 2010-2025 Three.js Authors * SPDX-License-Identifier: MIT */ -const REVISION = '180'; +const REVISION = '183'; /** * Represents mouse buttons and interaction types in context of controls. @@ -15455,6 +15515,38 @@ const RGB_ETC2_Format = 37492; */ const RGBA_ETC2_EAC_Format = 37496; +/** + * EAC R11 UNORM format. + * + * @type {number} + * @constant + */ +const R11_EAC_Format = 37488; // 0x9270 + +/** + * EAC R11 SNORM format. + * + * @type {number} + * @constant + */ +const SIGNED_R11_EAC_Format = 37489; // 0x9271 + +/** + * EAC RG11 UNORM format. + * + * @type {number} + * @constant + */ +const RG11_EAC_Format = 37490; // 0x9272 + +/** + * EAC RG11 SNORM format. + * + * @type {number} + * @constant + */ +const SIGNED_RG11_EAC_Format = 37491; // 0x9273 + /** * ASTC RGBA 4x4 format. * @@ -15624,21 +15716,13 @@ const RED_GREEN_RGTC2_Format = 36285; const SIGNED_RED_GREEN_RGTC2_Format = 36286; /** - * Basic depth packing. + * The depth value is inverted (1.0 - z) for visualization purposes. * * @type {number} * @constant */ const BasicDepthPacking = 3200; -/** - * A depth value is packed into 32 bit RGBA. - * - * @type {number} - * @constant - */ -const RGBADepthPacking = 3201; - /** * Normal information is relative to the underlying surface. * @@ -15863,7 +15947,7 @@ const WebGPUCoordinateSystem = 2001; /** * This modules allows to dispatch event objects on custom JavaScript objects. * - * Main repository: [eventdispatcher.js]{@link https://github.com/mrdoob/eventdispatcher.js/} + * Main repository: [eventdispatcher.js](https://github.com/mrdoob/eventdispatcher.js/) * * Code Example: * ```js @@ -15989,6 +16073,276 @@ class EventDispatcher { } +/** + * Checks if an array contains values that require Uint32 representation. + * + * This function determines whether the array contains any values >= 65535, + * which would require a Uint32Array rather than a Uint16Array for proper storage. + * The function iterates from the end of the array, assuming larger values are + * typically located at the end. + * + * @private + * @param {Array} array - The array to check. + * @return {boolean} True if the array contains values >= 65535, false otherwise. + */ +function arrayNeedsUint32( array ) { + + // assumes larger values usually on last + + for ( let i = array.length - 1; i >= 0; -- i ) { + + if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 + + } + + return false; + +} + +/** + * Creates an XHTML element with the specified tag name. + * + * This function uses the XHTML namespace to create DOM elements, + * ensuring proper element creation in XML-based contexts. + * + * @private + * @param {string} name - The tag name of the element to create (e.g., 'canvas', 'div'). + * @return {HTMLElement} The created XHTML element. + */ +function createElementNS( name ) { + + return document.createElementNS( 'http://www.w3.org/1999/xhtml', name ); + +} + +/** + * Creates a canvas element configured for block display. + * + * This is a convenience function that creates a canvas element with + * display style set to 'block', which is commonly used in three.js + * rendering contexts to avoid inline element spacing issues. + * + * @return {HTMLCanvasElement} A canvas element with display set to 'block'. + */ +function createCanvasElement() { + + const canvas = createElementNS( 'canvas' ); + canvas.style.display = 'block'; + return canvas; + +} + +/** + * Internal cache for tracking warning messages to prevent duplicate warnings. + * + * @private + * @type {Object} + */ +const _cache = {}; + +/** + * Logs an informational message with the 'THREE.' prefix. + * + * If a custom console function is set via setConsoleFunction(), it will be used + * instead of the native console.log. The first parameter is treated as the + * method name and is automatically prefixed with 'THREE.'. + * + * @param {...any} params - The message components. The first param is used as + * the method name and prefixed with 'THREE.'. + */ +function log( ...params ) { + + const message = 'THREE.' + params.shift(); + + { + + console.log( message, ...params ); + + } + +} + +/** + * Enhances log/warn/error messages related to TSL. + * + * @param {Array} params - The original message parameters. + * @returns {Array} The filtered and enhanced message parameters. + */ +function enhanceLogMessage( params ) { + + const message = params[ 0 ]; + + if ( typeof message === 'string' && message.startsWith( 'TSL:' ) ) { + + const stackTrace = params[ 1 ]; + + if ( stackTrace && stackTrace.isStackTrace ) { + + params[ 0 ] += ' ' + stackTrace.getLocation(); + + } else { + + params[ 1 ] = 'Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.'; + + } + + } + + return params; + +} + +/** + * Logs a warning message with the 'THREE.' prefix. + * + * If a custom console function is set via setConsoleFunction(), it will be used + * instead of the native console.warn. The first parameter is treated as the + * method name and is automatically prefixed with 'THREE.'. + * + * @param {...any} params - The message components. The first param is used as + * the method name and prefixed with 'THREE.'. + */ +function warn( ...params ) { + + params = enhanceLogMessage( params ); + + const message = 'THREE.' + params.shift(); + + { + + const stackTrace = params[ 0 ]; + + if ( stackTrace && stackTrace.isStackTrace ) { + + console.warn( stackTrace.getError( message ) ); + + } else { + + console.warn( message, ...params ); + + } + + } + +} + +/** + * Logs an error message with the 'THREE.' prefix. + * + * If a custom console function is set via setConsoleFunction(), it will be used + * instead of the native console.error. The first parameter is treated as the + * method name and is automatically prefixed with 'THREE.'. + * + * @param {...any} params - The message components. The first param is used as + * the method name and prefixed with 'THREE.'. + */ +function error( ...params ) { + + params = enhanceLogMessage( params ); + + const message = 'THREE.' + params.shift(); + + { + + const stackTrace = params[ 0 ]; + + if ( stackTrace && stackTrace.isStackTrace ) { + + console.error( stackTrace.getError( message ) ); + + } else { + + console.error( message, ...params ); + + } + + } + +} + +/** + * Logs a warning message only once, preventing duplicate warnings. + * + * This function maintains an internal cache of warning messages and will only + * output each unique warning message once. Useful for warnings that may be + * triggered repeatedly but should only be shown to the user once. + * + * @param {...any} params - The warning message components. + */ +function warnOnce( ...params ) { + + const message = params.join( ' ' ); + + if ( message in _cache ) return; + + _cache[ message ] = true; + + warn( ...params ); + +} + +/** + * Asynchronously probes for WebGL sync object completion. + * + * This function creates a promise that resolves when the WebGL sync object + * signals completion or rejects if the sync operation fails. It uses polling + * at the specified interval to check the sync status without blocking the + * main thread. This is useful for GPU-CPU synchronization in WebGL contexts. + * + * @private + * @param {WebGL2RenderingContext} gl - The WebGL rendering context. + * @param {WebGLSync} sync - The WebGL sync object to wait for. + * @param {number} interval - The polling interval in milliseconds. + * @return {Promise} A promise that resolves when the sync completes or rejects if it fails. + */ +function probeAsync( gl, sync, interval ) { + + return new Promise( function ( resolve, reject ) { + + function probe() { + + switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) { + + case gl.WAIT_FAILED: + reject(); + break; + + case gl.TIMEOUT_EXPIRED: + setTimeout( probe, interval ); + break; + + default: + resolve(); + + } + + } + + setTimeout( probe, interval ); + + } ); + +} + +/** + * Used to select the correct depth functions + * when reversed depth buffer is used. + * + * @private + * @type {Object} + */ +const ReversedDepthFuncs = { + [ NeverDepth ]: AlwaysDepth, + [ LessDepth ]: GreaterDepth, + [ EqualDepth ]: NotEqualDepth, + [ LessEqualDepth ]: GreaterEqualDepth, + + [ AlwaysDepth ]: NeverDepth, + [ GreaterDepth ]: LessDepth, + [ NotEqualDepth ]: EqualDepth, + [ GreaterEqualDepth ]: LessEqualDepth, +}; + const _lut = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff' ]; let _seed = 1234567; @@ -15998,7 +16352,7 @@ const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; /** - * Generate a [UUID]{@link https://en.wikipedia.org/wiki/Universally_unique_identifier} + * Generate a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) * (universally unique identifier). * * @return {string} The UUID. @@ -16053,7 +16407,7 @@ function euclideanModulo( n, m ) { /** * Performs a linear mapping from range `` to range `` - * for the given value. + * for the given value. `a2` must be greater than `a1`. * * @param {number} x - The value to be mapped. * @param {number} a1 - Minimum value for range A. @@ -16111,7 +16465,7 @@ function lerp( x, y, t ) { /** * Smoothly interpolate a number from `x` to `y` in a spring-like manner using a delta * time to maintain frame rate independent movement. For details, see - * [Frame rate independent damping using lerp]{@link http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/}. + * [Frame rate independent damping using lerp](http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/). * * @param {number} x - The current point. * @param {number} y - The target point. @@ -16146,11 +16500,11 @@ function pingpong( x, length = 1 ) { * moved between `min` and `max`, but smoothed or slowed down the closer `x` is to * the `min` and `max`. * - * See [Smoothstep]{@link http://en.wikipedia.org/wiki/Smoothstep} for more details. + * See [Smoothstep](http://en.wikipedia.org/wiki/Smoothstep) for more details. * - * @param {number} x - The value to evaluate based on its position between min and max. - * @param {number} min - The min value. Any x value below min will be `0`. - * @param {number} max - The max value. Any x value above max will be `1`. + * @param {number} x - The value to evaluate based on its position between `min` and `max`. + * @param {number} min - The min value. Any `x` value below `min` will be `0`. `min` must be lower than `max`. + * @param {number} max - The max value. Any `x` value above `max` will be `1`. `max` must be greater than `min`. * @return {number} The alternated value. */ function smoothstep( x, min, max ) { @@ -16165,12 +16519,12 @@ function smoothstep( x, min, max ) { } /** - * A [variation on smoothstep]{@link https://en.wikipedia.org/wiki/Smoothstep#Variations} - * that has zero 1st and 2nd order derivatives at x=0 and x=1. + * A [variation on smoothstep](https://en.wikipedia.org/wiki/Smoothstep#Variations) + * that has zero 1st and 2nd order derivatives at `x=0` and `x=1`. * - * @param {number} x - The value to evaluate based on its position between min and max. - * @param {number} min - The min value. Any x value below min will be `0`. - * @param {number} max - The max value. Any x value above max will be `1`. + * @param {number} x - The value to evaluate based on its position between `min` and `max`. + * @param {number} min - The min value. Any `x` value below `min` will be `0`. `min` must be lower than `max`. + * @param {number} max - The max value. Any `x` value above `max` will be `1`. `max` must be greater than `min`. * @return {number} The alternated value. */ function smootherstep( x, min, max ) { @@ -16283,7 +16637,7 @@ function isPowerOfTwo( value ) { /** * Returns the smallest power of two that is greater than or equal to the given number. * - * @param {number} value - The value to find a POT for. + * @param {number} value - The value to find a POT for. Must be greater than `0`. * @return {number} The smallest power of two that is greater than or equal to the given number. */ function ceilPowerOfTwo( value ) { @@ -16295,7 +16649,7 @@ function ceilPowerOfTwo( value ) { /** * Returns the largest power of two that is less than or equal to the given number. * - * @param {number} value - The value to find a POT for. + * @param {number} value - The value to find a POT for. Must be greater than `0`. * @return {number} The largest power of two that is less than or equal to the given number. */ function floorPowerOfTwo( value ) { @@ -16305,7 +16659,7 @@ function floorPowerOfTwo( value ) { } /** - * Sets the given quaternion from the [Intrinsic Proper Euler Angles]{@link https://en.wikipedia.org/wiki/Euler_angles} + * Sets the given quaternion from the [Intrinsic Proper Euler Angles](https://en.wikipedia.org/wiki/Euler_angles) * defined by the given angles and order. * * Rotations are applied to the axes in the order specified by order: @@ -16361,7 +16715,7 @@ function setQuaternionFromProperEuler( q, a, b, c, order ) { break; default: - console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); + warn( 'MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); } @@ -16470,7 +16824,7 @@ const MathUtils = { DEG2RAD: DEG2RAD, RAD2DEG: RAD2DEG, /** - * Generate a [UUID]{@link https://en.wikipedia.org/wiki/Universally_unique_identifier} + * Generate a [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) * (universally unique identifier). * * @static @@ -16541,7 +16895,7 @@ const MathUtils = { /** * Smoothly interpolate a number from `x` to `y` in a spring-like manner using a delta * time to maintain frame rate independent movement. For details, see - * [Frame rate independent damping using lerp]{@link http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/}. + * [Frame rate independent damping using lerp](http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/). * * @static * @method @@ -16568,7 +16922,7 @@ const MathUtils = { * moved between `min` and `max`, but smoothed or slowed down the closer `x` is to * the `min` and `max`. * - * See [Smoothstep]{@link http://en.wikipedia.org/wiki/Smoothstep} for more details. + * See [Smoothstep](http://en.wikipedia.org/wiki/Smoothstep) for more details. * * @static * @method @@ -16579,7 +16933,7 @@ const MathUtils = { */ smoothstep: smoothstep, /** - * A [variation on smoothstep]{@link https://en.wikipedia.org/wiki/Smoothstep#Variations} + * A [variation on smoothstep](https://en.wikipedia.org/wiki/Smoothstep#Variations) * that has zero 1st and 2nd order derivatives at x=0 and x=1. * * @static @@ -16674,7 +17028,7 @@ const MathUtils = { */ floorPowerOfTwo: floorPowerOfTwo, /** - * Sets the given quaternion from the [Intrinsic Proper Euler Angles]{@link https://en.wikipedia.org/wiki/Euler_angles} + * Sets the given quaternion from the [Intrinsic Proper Euler Angles](https://en.wikipedia.org/wiki/Euler_angles) * defined by the given angles and order. * * Rotations are applied to the axes in the order specified by order: @@ -17648,7 +18002,7 @@ class Quaternion { /** * Interpolates between two quaternions via SLERP. This implementation assumes the - * quaternion data are managed in flat arrays. + * quaternion data are managed in flat arrays. * * @param {Array} dst - The destination array. * @param {number} dstOffset - An offset into the destination array. @@ -17656,70 +18010,61 @@ class Quaternion { * @param {number} srcOffset0 - An offset into the first source array. * @param {Array} src1 - The source array of the second quaternion. * @param {number} srcOffset1 - An offset into the second source array. - * @param {number} t - The interpolation factor in the range `[0,1]`. + * @param {number} t - The interpolation factor. A value in the range `[0,1]` will interpolate. A value outside the range `[0,1]` will extrapolate. * @see {@link Quaternion#slerp} */ static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - // fuzz-free, array-based Quaternion SLERP operation - let x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ]; - const x1 = src1[ srcOffset1 + 0 ], + let x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; - if ( t === 0 ) { - - dst[ dstOffset + 0 ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; - return; + if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - } + let dot = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1; - if ( t === 1 ) { + if ( dot < 0 ) { - dst[ dstOffset + 0 ] = x1; - dst[ dstOffset + 1 ] = y1; - dst[ dstOffset + 2 ] = z1; - dst[ dstOffset + 3 ] = w1; - return; + x1 = - x1; + y1 = - y1; + z1 = - z1; + w1 = - w1; - } + dot = - dot; - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { + } let s = 1 - t; - const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, - dir = ( cos >= 0 ? 1 : -1 ), - sqrSin = 1 - cos * cos; - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { + if ( dot < 0.9995 ) { - const sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); + // slerp - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; + const theta = Math.acos( dot ); + const sin = Math.sin( theta ); - } + s = Math.sin( s * theta ) / sin; + t = Math.sin( t * theta ) / sin; - const tDir = t * dir; + x0 = x0 * s + x1 * t; + y0 = y0 * s + y1 * t; + z0 = z0 * s + z1 * t; + w0 = w0 * s + w1 * t; - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; + } else { + + // for small angles, lerp then normalize - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { + x0 = x0 * s + x1 * t; + y0 = y0 * s + y1 * t; + z0 = z0 * s + z1 * t; + w0 = w0 * s + w1 * t; const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); @@ -17973,7 +18318,7 @@ class Quaternion { break; default: - console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); + warn( 'Quaternion: .setFromEuler() encountered an unknown order: ' + order ); } @@ -18321,77 +18666,61 @@ class Quaternion { } /** - * Performs a spherical linear interpolation between quaternions. + * Performs a spherical linear interpolation between this quaternion and the target quaternion. * * @param {Quaternion} qb - The target quaternion. - * @param {number} t - The interpolation factor in the closed interval `[0, 1]`. + * @param {number} t - The interpolation factor. A value in the range `[0,1]` will interpolate. A value outside the range `[0,1]` will extrapolate. * @return {Quaternion} A reference to this quaternion. */ slerp( qb, t ) { - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); - - const x = this._x, y = this._y, z = this._z, w = this._w; + let x = qb._x, y = qb._y, z = qb._z, w = qb._w; - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + let dot = this.dot( qb ); - let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + if ( dot < 0 ) { - if ( cosHalfTheta < 0 ) { + x = - x; + y = - y; + z = - z; + w = - w; - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; + dot = - dot; - cosHalfTheta = - cosHalfTheta; + } - } else { + let s = 1 - t; - this.copy( qb ); + if ( dot < 0.9995 ) { - } + // slerp - if ( cosHalfTheta >= 1.0 ) { + const theta = Math.acos( dot ); + const sin = Math.sin( theta ); - this._w = w; - this._x = x; - this._y = y; - this._z = z; + s = Math.sin( s * theta ) / sin; + t = Math.sin( t * theta ) / sin; - return this; + this._x = this._x * s + x * t; + this._y = this._y * s + y * t; + this._z = this._z * s + z * t; + this._w = this._w * s + w * t; - } + this._onChangeCallback(); - const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; + } else { - if ( sqrSinHalfTheta <= Number.EPSILON ) { + // for small angles, lerp then normalize - const s = 1 - t; - this._w = s * w + t * this._w; - this._x = s * x + t * this._x; - this._y = s * y + t * this._y; - this._z = s * z + t * this._z; + this._x = this._x * s + x * t; + this._y = this._y * s + y * t; + this._z = this._z * s + z * t; + this._w = this._w * s + w * t; this.normalize(); // normalize calls _onChangeCallback() - return this; - } - const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); - const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - - this._w = ( w * ratioA + this._w * ratioB ); - this._x = ( x * ratioA + this._x * ratioB ); - this._y = ( y * ratioA + this._y * ratioB ); - this._z = ( z * ratioA + this._z * ratioB ); - - this._onChangeCallback(); - return this; } @@ -18647,7 +18976,7 @@ class Vector3 { } /** - * Sets the vector's x component to the given value + * Sets the vector's x component to the given value. * * @param {number} x - The value to set. * @return {Vector3} A reference to this vector. @@ -18661,7 +18990,7 @@ class Vector3 { } /** - * Sets the vector's y component to the given value + * Sets the vector's y component to the given value. * * @param {number} y - The value to set. * @return {Vector3} A reference to this vector. @@ -18675,7 +19004,7 @@ class Vector3 { } /** - * Sets the vector's z component to the given value + * Sets the vector's z component to the given value. * * @param {number} z - The value to set. * @return {Vector3} A reference to this vector. @@ -18928,7 +19257,7 @@ class Vector3 { */ applyEuler( euler ) { - return this.applyQuaternion( _quaternion$2.setFromEuler( euler ) ); + return this.applyQuaternion( _quaternion$3.setFromEuler( euler ) ); } @@ -18941,7 +19270,7 @@ class Vector3 { */ applyAxisAngle( axis, angle ) { - return this.applyQuaternion( _quaternion$2.setFromAxisAngle( axis, angle ) ); + return this.applyQuaternion( _quaternion$3.setFromAxisAngle( axis, angle ) ); } @@ -19285,8 +19614,6 @@ class Vector3 { } - // TODO lengthSquared? - /** * Computes the square of the Euclidean length (straight-line length) from * (0, 0, 0) to (x, y, z). If you are comparing the lengths of vectors, you should @@ -19797,7 +20124,7 @@ class Vector3 { } const _vector$8 = /*@__PURE__*/ new Vector3(); -const _quaternion$2 = /*@__PURE__*/ new Quaternion(); +const _quaternion$3 = /*@__PURE__*/ new Quaternion(); /** * Represents a 3x3 matrix. @@ -19805,7 +20132,7 @@ const _quaternion$2 = /*@__PURE__*/ new Quaternion(); * A Note on Row-Major and Column-Major Ordering: * * The constructor and {@link Matrix3#set} method take arguments in - * [row-major]{@link https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order} + * [row-major](https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order) * order, while internally they are stored in the {@link Matrix3#elements} array in column-major order. * This means that calling: * ```js @@ -20079,7 +20406,7 @@ class Matrix3 { } /** - * Inverts this matrix, using the [analytic method]{@link https://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution}. + * Inverts this matrix, using the [analytic method](https://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution). * You can not invert with a determinant of zero. If you attempt this, the method produces * a zero matrix instead. * @@ -20411,75 +20738,6 @@ class Matrix3 { const _m3 = /*@__PURE__*/ new Matrix3(); -function arrayNeedsUint32( array ) { - - // assumes larger values usually on last - - for ( let i = array.length - 1; i >= 0; -- i ) { - - if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 - - } - - return false; - -} - -function createElementNS( name ) { - - return document.createElementNS( 'http://www.w3.org/1999/xhtml', name ); - -} - -function createCanvasElement() { - - const canvas = createElementNS( 'canvas' ); - canvas.style.display = 'block'; - return canvas; - -} - -const _cache = {}; - -function warnOnce( message ) { - - if ( message in _cache ) return; - - _cache[ message ] = true; - - console.warn( message ); - -} - -function probeAsync( gl, sync, interval ) { - - return new Promise( function ( resolve, reject ) { - - function probe() { - - switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) { - - case gl.WAIT_FAILED: - reject(); - break; - - case gl.TIMEOUT_EXPIRED: - setTimeout( probe, interval ); - break; - - default: - resolve(); - - } - - } - - setTimeout( probe, interval ); - - } ); - -} - const LINEAR_REC709_TO_XYZ = /*@__PURE__*/ new Matrix3().set( 0.4123908, 0.3575843, 0.1804808, 0.2126390, 0.7151687, 0.0721923, @@ -20625,7 +20883,7 @@ function createColorManagement() { fromWorkingColorSpace: function ( color, targetColorSpace ) { - warnOnce( 'THREE.ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace().' ); // @deprecated, r177 + warnOnce( 'ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace().' ); // @deprecated, r177 return ColorManagement.workingToColorSpace( color, targetColorSpace ); @@ -20633,7 +20891,7 @@ function createColorManagement() { toWorkingColorSpace: function ( color, sourceColorSpace ) { - warnOnce( 'THREE.ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking().' ); // @deprecated, r177 + warnOnce( 'ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking().' ); // @deprecated, r177 return ColorManagement.colorSpaceToWorking( color, sourceColorSpace ); @@ -20816,7 +21074,7 @@ class ImageUtils { } else { - console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' ); + warn( 'ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' ); return image; } @@ -20911,7 +21169,7 @@ class Source { target.set( data.videoWidth, data.videoHeight, 0 ); - } else if ( data instanceof VideoFrame ) { + } else if ( ( typeof VideoFrame !== 'undefined' ) && ( data instanceof VideoFrame ) ) { target.set( data.displayHeight, data.displayWidth, 0 ); @@ -21041,7 +21299,7 @@ function serializeImage( image ) { } else { - console.warn( 'THREE.Texture: Unable to serialize Texture.' ); + warn( 'Texture: Unable to serialize Texture.' ); return {}; } @@ -21101,7 +21359,7 @@ class Texture extends EventDispatcher { Object.defineProperty( this, 'id', { value: _textureId ++ } ); /** - * The UUID of the material. + * The UUID of the texture. * * @type {string} * @readonly @@ -21109,7 +21367,7 @@ class Texture extends EventDispatcher { this.uuid = generateUUID(); /** - * The name of the material. + * The name of the texture. * * @type {string} */ @@ -21191,7 +21449,7 @@ class Texture extends EventDispatcher { * texture samples being used. * * @type {number} - * @default 0 + * @default Texture.DEFAULT_ANISOTROPY */ this.anisotropy = anisotropy; @@ -21555,7 +21813,7 @@ class Texture extends EventDispatcher { if ( newValue === undefined ) { - console.warn( `THREE.Texture.setValues(): parameter '${ key }' has value of undefined.` ); + warn( `Texture.setValues(): parameter '${ key }' has value of undefined.` ); continue; } @@ -21564,7 +21822,7 @@ class Texture extends EventDispatcher { if ( currentValue === undefined ) { - console.warn( `THREE.Texture.setValues(): property '${ key }' does not exist.` ); + warn( `Texture.setValues(): property '${ key }' does not exist.` ); continue; } @@ -23011,10 +23269,6 @@ class RenderTarget extends EventDispatcher { */ this.viewport = new Vector4( 0, 0, width, height ); - const image = { width: width, height: height, depth: options.depth }; - - const texture = new Texture( image ); - /** * An array of textures. Each color attachment is represented as a separate texture. * Has at least a single entry for the default color attachment. @@ -23023,6 +23277,9 @@ class RenderTarget extends EventDispatcher { */ this.textures = []; + const image = { width: width, height: height, depth: options.depth }; + const texture = new Texture( image ); + const count = options.count; for ( let i = 0; i < count; i ++ ) { @@ -23181,7 +23438,16 @@ class RenderTarget extends EventDispatcher { this.textures[ i ].image.width = width; this.textures[ i ].image.height = height; this.textures[ i ].image.depth = depth; - this.textures[ i ].isArrayTexture = this.textures[ i ].image.depth > 1; + + if ( this.textures[ i ].isData3DTexture !== true ) { // Fix for #31693 + + // TODO: Reconsider setting isArrayTexture flag here and in the ctor of Texture. + // Maybe a method `isArrayTexture()` or just a getter could replace a flag since + // both are evaluated on each call? + + this.textures[ i ].isArrayTexture = this.textures[ i ].image.depth > 1; + + } } @@ -23571,7 +23837,7 @@ class Color { /** * Sets this color from a CSS-style string. For example, `rgb(250, 0,0)`, * `rgb(100%, 0%, 0%)`, `hsl(0, 100%, 50%)`, `#ff0000`, `#f00`, or `red` ( or - * any [X11 color name]{@link https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart} - + * any [X11 color name](https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart) - * all 140 color names are supported). * * @param {string} style - Color as a CSS-style string. @@ -23586,7 +23852,7 @@ class Color { if ( parseFloat( string ) < 1 ) { - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); + warn( 'Color: Alpha component of ' + style + ' will be ignored.' ); } @@ -23662,7 +23928,7 @@ class Color { default: - console.warn( 'THREE.Color: Unknown color model ' + style ); + warn( 'Color: Unknown color model ' + style ); } @@ -23690,7 +23956,7 @@ class Color { } else { - console.warn( 'THREE.Color: Invalid hex color ' + style ); + warn( 'Color: Invalid hex color ' + style ); } @@ -23730,7 +23996,7 @@ class Color { } else { // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); + warn( 'Color: Unknown color ' + style ); } @@ -25465,7 +25731,7 @@ class Sphere { * Returns a serialized structure of the bounding sphere. * * @param {Object} json - The serialized json to set the sphere from. - * @return {Box3} A reference to this bounding sphere. + * @return {Sphere} A reference to this bounding sphere. */ fromJSON( json ) { @@ -25483,7 +25749,7 @@ const _normalMatrix = /*@__PURE__*/ new Matrix3(); /** * A two dimensional surface that extends infinitely in 3D space, represented - * in [Hessian normal form]{@link http://mathworld.wolfram.com/HessianNormalForm.html} + * in [Hessian normal form](http://mathworld.wolfram.com/HessianNormalForm.html) * by a unit length normal vector and a constant. */ class Plane { @@ -26121,7 +26387,7 @@ class Frustum { * Represents a 4x4 matrix. * * The most common use of a 4x4 matrix in 3D computer graphics is as a transformation matrix. - * For an introduction to transformation matrices as used in WebGL, check out [this tutorial]{@link https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices} + * For an introduction to transformation matrices as used in WebGL, check out [this tutorial](https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices) * * This allows a 3D vector representing a point in 3D space to undergo * transformations such as translation, rotation, shear, scale, reflection, @@ -26131,7 +26397,7 @@ class Frustum { * A Note on Row-Major and Column-Major Ordering: * * The constructor and {@link Matrix3#set} method take arguments in - * [row-major]{@link https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order} + * [row-major](https://en.wikipedia.org/wiki/Row-_and_column-major_order#Column-major_order) * order, while internally they are stored in the {@link Matrix3#elements} array in column-major order. * This means that calling: * ```js @@ -26350,6 +26616,16 @@ class Matrix4 { */ extractBasis( xAxis, yAxis, zAxis ) { + if ( this.determinant() === 0 ) { + + xAxis.set( 1, 0, 0 ); + yAxis.set( 0, 1, 0 ); + zAxis.set( 0, 0, 1 ); + + return this; + + } + xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); @@ -26390,6 +26666,12 @@ class Matrix4 { */ extractRotation( m ) { + if ( m.determinant() === 0 ) { + + return this.identity(); + + } + const te = this.elements; const me = m.elements; @@ -26425,7 +26707,7 @@ class Matrix4 { * Sets the rotation component (the upper left 3x3 matrix) of this matrix to * the rotation specified by the given Euler angles. The rest of * the matrix is set to the identity. Depending on the {@link Euler#order}, - * there are six possible outcomes. See [this page]{@link https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix} + * there are six possible outcomes. See [this page](https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix) * for a complete list. * * @param {Euler} euler - The Euler angles. @@ -26555,7 +26837,7 @@ class Matrix4 { /** * Sets the rotation component of this matrix to the rotation specified by - * the given Quaternion as outlined [here]{@link https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion} + * the given Quaternion as outlined [here](https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion) * The rest of the matrix is set to the identity. * * @param {Quaternion} q - The Quaternion. @@ -26717,7 +26999,7 @@ class Matrix4 { /** * Computes and returns the determinant of this matrix. * - * Based on the method outlined [here]{@link http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.html}. + * Based on the method outlined [here](http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.html). * * @return {number} The determinant. */ @@ -26730,43 +27012,18 @@ class Matrix4 { const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - //TODO: make this more efficient - - return ( - n41 * ( - + n14 * n23 * n32 - - n13 * n24 * n32 - - n14 * n22 * n33 - + n12 * n24 * n33 - + n13 * n22 * n34 - - n12 * n23 * n34 - ) + - n42 * ( - + n11 * n23 * n34 - - n11 * n24 * n33 - + n14 * n21 * n33 - - n13 * n21 * n34 - + n13 * n24 * n31 - - n14 * n23 * n31 - ) + - n43 * ( - + n11 * n24 * n32 - - n11 * n22 * n34 - - n14 * n21 * n32 - + n12 * n21 * n34 - + n14 * n22 * n31 - - n12 * n24 * n31 - ) + - n44 * ( - - n13 * n22 * n31 - - n11 * n23 * n32 - + n11 * n22 * n33 - + n13 * n21 * n32 - - n12 * n21 * n33 - + n12 * n23 * n31 - ) + const t11 = n23 * n34 - n24 * n33; + const t12 = n22 * n34 - n24 * n32; + const t13 = n22 * n33 - n23 * n32; - ); + const t21 = n21 * n34 - n24 * n31; + const t22 = n21 * n33 - n23 * n31; + const t23 = n21 * n32 - n22 * n31; + + return n11 * ( n42 * t11 - n43 * t12 + n44 * t13 ) - + n12 * ( n41 * t11 - n43 * t21 + n44 * t22 ) + + n13 * ( n41 * t12 - n42 * t21 + n44 * t23 ) - + n14 * ( n41 * t13 - n42 * t22 + n43 * t23 ); } @@ -26824,7 +27081,7 @@ class Matrix4 { } /** - * Inverts this matrix, using the [analytic method]{@link https://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution}. + * Inverts this matrix, using the [analytic method](https://en.wikipedia.org/wiki/Invertible_matrix#Analytic_solution). * You can not invert with a determinant of zero. If you attempt this, the method produces * a zero matrix instead. * @@ -26832,7 +27089,7 @@ class Matrix4 { */ invert() { - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + // based on https://github.com/toji/gl-matrix const te = this.elements, n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], @@ -26840,36 +27097,44 @@ class Matrix4 { n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], - t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, - t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, - t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, - t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; - - const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + t1 = n11 * n22 - n21 * n12, + t2 = n11 * n32 - n31 * n12, + t3 = n11 * n42 - n41 * n12, + t4 = n21 * n32 - n31 * n22, + t5 = n21 * n42 - n41 * n22, + t6 = n31 * n42 - n41 * n32, + t7 = n13 * n24 - n23 * n14, + t8 = n13 * n34 - n33 * n14, + t9 = n13 * n44 - n43 * n14, + t10 = n23 * n34 - n33 * n24, + t11 = n23 * n44 - n43 * n24, + t12 = n33 * n44 - n43 * n34; + + const det = t1 * t12 - t2 * t11 + t3 * t10 + t4 * t9 - t5 * t8 + t6 * t7; if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); const detInv = 1 / det; - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; - te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; - te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; + te[ 0 ] = ( n22 * t12 - n32 * t11 + n42 * t10 ) * detInv; + te[ 1 ] = ( n31 * t11 - n21 * t12 - n41 * t10 ) * detInv; + te[ 2 ] = ( n24 * t6 - n34 * t5 + n44 * t4 ) * detInv; + te[ 3 ] = ( n33 * t5 - n23 * t6 - n43 * t4 ) * detInv; - te[ 4 ] = t12 * detInv; - te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; - te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; - te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; + te[ 4 ] = ( n32 * t9 - n12 * t12 - n42 * t8 ) * detInv; + te[ 5 ] = ( n11 * t12 - n31 * t9 + n41 * t8 ) * detInv; + te[ 6 ] = ( n34 * t3 - n14 * t6 - n44 * t2 ) * detInv; + te[ 7 ] = ( n13 * t6 - n33 * t3 + n43 * t2 ) * detInv; - te[ 8 ] = t13 * detInv; - te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; - te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; - te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; + te[ 8 ] = ( n12 * t11 - n22 * t9 + n42 * t7 ) * detInv; + te[ 9 ] = ( n21 * t9 - n11 * t11 - n41 * t7 ) * detInv; + te[ 10 ] = ( n14 * t5 - n24 * t3 + n44 * t1 ) * detInv; + te[ 11 ] = ( n23 * t3 - n13 * t5 - n43 * t1 ) * detInv; - te[ 12 ] = t14 * detInv; - te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; - te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; - te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; + te[ 12 ] = ( n22 * t8 - n12 * t10 - n32 * t7 ) * detInv; + te[ 13 ] = ( n11 * t10 - n21 * t8 + n31 * t7 ) * detInv; + te[ 14 ] = ( n24 * t2 - n14 * t4 - n34 * t1 ) * detInv; + te[ 15 ] = ( n13 * t4 - n23 * t2 + n33 * t1 ) * detInv; return this; @@ -27027,7 +27292,7 @@ class Matrix4 { * the given angle. * * This is a somewhat controversial but mathematically sound alternative to - * rotating via Quaternions. See the discussion [here]{@link https://www.gamedev.net/articles/programming/math-and-physics/do-we-really-need-quaternions-r1199}. + * rotating via Quaternions. See the discussion [here](https://www.gamedev.net/articles/programming/math-and-physics/do-we-really-need-quaternions-r1199). * * @param {Vector3} axis - The normalized rotation axis. * @param {number} angle - The rotation in radians. @@ -27167,18 +27432,28 @@ class Matrix4 { const te = this.elements; + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; + + const det = this.determinant(); + + if ( det === 0 ) { + + scale.set( 1, 1, 1 ); + quaternion.identity(); + + return this; + + } + let sx = _v1$4.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); const sy = _v1$4.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); const sz = _v1$4.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - // if determine is negative, we need to invert one scale - const det = this.determinant(); + // if determinant is negative, we need to invert one scale if ( det < 0 ) sx = - sx; - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; - // scale the rotation part _m1$4.copy( this ); @@ -28443,7 +28718,7 @@ class Float32BufferAttribute extends BufferAttribute { } const _matrix$2 = /*@__PURE__*/ new Matrix4(); -const _quaternion$1 = /*@__PURE__*/ new Quaternion(); +const _quaternion$2 = /*@__PURE__*/ new Quaternion(); /** * A class representing Euler angles. @@ -28744,7 +29019,7 @@ class Euler { default: - console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); + warn( 'Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); } @@ -28797,9 +29072,9 @@ class Euler { */ reorder( newOrder ) { - _quaternion$1.setFromEuler( this ); + _quaternion$2.setFromEuler( this ); - return this.setFromQuaternion( _quaternion$1, newOrder ); + return this.setFromQuaternion( _quaternion$2, newOrder ); } @@ -29011,9 +29286,9 @@ const _q1 = /*@__PURE__*/ new Quaternion(); const _m1$3 = /*@__PURE__*/ new Matrix4(); const _target = /*@__PURE__*/ new Vector3(); -const _position$1 = /*@__PURE__*/ new Vector3(); -const _scale = /*@__PURE__*/ new Vector3(); -const _quaternion = /*@__PURE__*/ new Quaternion(); +const _position$2 = /*@__PURE__*/ new Vector3(); +const _scale$1 = /*@__PURE__*/ new Vector3(); +const _quaternion$1 = /*@__PURE__*/ new Quaternion(); const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); @@ -29238,7 +29513,8 @@ class Object3D extends EventDispatcher { /** * When set to `true`, the engine automatically computes the local matrix from position, - * rotation and scale every frame. + * rotation and scale every frame. If set to `false`, the app is responsible for recomputing + * the local matrix by calling `updateMatrix()`. * * The default values for all 3D objects is defined by `Object3D.DEFAULT_MATRIX_AUTO_UPDATE`. * @@ -29249,7 +29525,8 @@ class Object3D extends EventDispatcher { /** * When set to `true`, the engine automatically computes the world matrix from the current local - * matrix and the object's transformation hierarchy. + * matrix and the object's transformation hierarchy. If set to `false`, the app is responsible for + * recomputing the world matrix by directly updating the `matrixWorld` property. * * The default values for all 3D objects is defined by `Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE`. * @@ -29350,6 +29627,19 @@ class Object3D extends EventDispatcher { */ this.customDistanceMaterial = undefined; + /** + * Whether the 3D object is supposed to be static or not. If set to `true`, it means + * the 3D object is not going to be changed after the initial renderer. This includes + * geometry and material settings. A static 3D object can be processed by the renderer + * slightly faster since certain state checks can be bypassed. + * + * Only relevant in context of {@link WebGPURenderer}. + * + * @type {boolean} + * @default false + */ + this.static = false; + /** * An object that can be used to store custom data about the 3D object. It * should not hold references to functions as these will not be cloned. @@ -29358,6 +29648,16 @@ class Object3D extends EventDispatcher { */ this.userData = {}; + /** + * The pivot point for rotation and scale transformations. + * When set, rotation and scale are applied around this point + * instead of the object's origin. + * + * @type {?Vector3} + * @default null + */ + this.pivot = null; + } /** @@ -29640,7 +29940,7 @@ class Object3D extends EventDispatcher { } /** - * Converts the given vector from this 3D object's word space to local space. + * Converts the given vector from this 3D object's world space to local space. * * @param {Vector3} vector - The vector to convert. * @return {Vector3} The converted vector. @@ -29680,15 +29980,15 @@ class Object3D extends EventDispatcher { this.updateWorldMatrix( true, false ); - _position$1.setFromMatrixPosition( this.matrixWorld ); + _position$2.setFromMatrixPosition( this.matrixWorld ); if ( this.isCamera || this.isLight ) { - _m1$3.lookAt( _position$1, _target, this.up ); + _m1$3.lookAt( _position$2, _target, this.up ); } else { - _m1$3.lookAt( _target, _position$1, this.up ); + _m1$3.lookAt( _target, _position$2, this.up ); } @@ -29730,7 +30030,7 @@ class Object3D extends EventDispatcher { if ( object === this ) { - console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object ); + error( 'Object3D.add: object can\'t be added as a child of itself.', object ); return this; } @@ -29749,7 +30049,7 @@ class Object3D extends EventDispatcher { } else { - console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object ); + error( 'Object3D.add: object not an instance of THREE.Object3D.', object ); } @@ -29982,7 +30282,7 @@ class Object3D extends EventDispatcher { this.updateWorldMatrix( true, false ); - this.matrixWorld.decompose( _position$1, target, _scale ); + this.matrixWorld.decompose( _position$2, target, _scale$1 ); return target; @@ -29998,7 +30298,7 @@ class Object3D extends EventDispatcher { this.updateWorldMatrix( true, false ); - this.matrixWorld.decompose( _position$1, _quaternion, target ); + this.matrixWorld.decompose( _position$2, _quaternion$1, target ); return target; @@ -30105,6 +30405,19 @@ class Object3D extends EventDispatcher { this.matrix.compose( this.position, this.quaternion, this.scale ); + const pivot = this.pivot; + + if ( pivot !== null ) { + + const px = pivot.x, py = pivot.y, pz = pivot.z; + const te = this.matrix.elements; + + te[ 12 ] += px - te[ 0 ] * px - te[ 4 ] * py - te[ 8 ] * pz; + te[ 13 ] += py - te[ 1 ] * px - te[ 5 ] * py - te[ 9 ] * pz; + te[ 14 ] += pz - te[ 2 ] * px - te[ 6 ] * py - te[ 10 ] * pz; + + } + this.matrixWorldNeedsUpdate = true; } @@ -30118,7 +30431,7 @@ class Object3D extends EventDispatcher { * `true` by default. Set these flags to `false` if you need more control over the update matrix process. * * @param {boolean} [force=false] - When set to `true`, a recomputation of world matrices is forced even - * when {@link Object3D#matrixWorldAutoUpdate} is set to `false`. + * when {@link Object3D#matrixWorldNeedsUpdate} is `false`. */ updateMatrixWorld( force ) { @@ -30263,14 +30576,20 @@ class Object3D extends EventDispatcher { if ( this.visible === false ) object.visible = false; if ( this.frustumCulled === false ) object.frustumCulled = false; if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; + if ( this.static !== false ) object.static = this.static; if ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData; object.layers = this.layers.mask; object.matrix = this.matrix.toArray(); object.up = this.up.toArray(); + if ( this.pivot !== null ) object.pivot = this.pivot.toArray(); + if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; + if ( this.morphTargetDictionary !== undefined ) object.morphTargetDictionary = Object.assign( {}, this.morphTargetDictionary ); + if ( this.morphTargetInfluences !== undefined ) object.morphTargetInfluences = this.morphTargetInfluences.slice(); + // object specific properties if ( this.isInstancedMesh ) { @@ -30544,6 +30863,12 @@ class Object3D extends EventDispatcher { this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); + if ( source.pivot !== null ) { + + this.pivot = source.pivot.clone(); + + } + this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); @@ -30561,6 +30886,8 @@ class Object3D extends EventDispatcher { this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; + this.static = source.static; + this.animations = source.animations.slice(); this.userData = JSON.parse( JSON.stringify( source.userData ) ); @@ -30713,6 +31040,16 @@ class BufferGeometry extends EventDispatcher { */ this.indirect = null; + /** + * The offset, in bytes, into the indirect drawing buffer where the value data begins. If an array is provided, multiple indirect draw calls will be made for each offset. + * + * Can only be used with {@link WebGPURenderer} and a WebGPU backend. + * + * @type {number|Array} + * @default 0 + */ + this.indirectOffset = 0; + /** * This dictionary has as id the name of the attribute to be set and as value * the buffer attribute to set it to. Rather than accessing this property directly, @@ -30726,7 +31063,7 @@ class BufferGeometry extends EventDispatcher { * This dictionary holds the morph targets of the geometry. * * Note: Once the geometry has been rendered, the morph attribute data cannot - * be changed. You will have to call `dispose()?, and create a new geometry instance. + * be changed. You will have to call `dispose()`, and create a new geometry instance. * * @type {Object} */ @@ -30826,11 +31163,13 @@ class BufferGeometry extends EventDispatcher { * Sets the given indirect attribute to this geometry. * * @param {BufferAttribute} indirect - The attribute holding indirect draw calls. + * @param {number|Array} [indirectOffset=0] - The offset, in bytes, into the indirect drawing buffer where the value data begins. If an array is provided, multiple indirect draw calls will be made for each offset. * @return {BufferGeometry} A reference to this instance. */ - setIndirect( indirect ) { + setIndirect( indirect, indirectOffset = 0 ) { this.indirect = indirect; + this.indirectOffset = indirectOffset; return this; @@ -31198,7 +31537,7 @@ class BufferGeometry extends EventDispatcher { if ( points.length > positionAttribute.count ) { - console.warn( 'THREE.BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry.' ); + warn( 'BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry.' ); } @@ -31228,7 +31567,7 @@ class BufferGeometry extends EventDispatcher { if ( position && position.isGLBufferAttribute ) { - console.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.', this ); + error( 'BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.', this ); this.boundingBox.set( new Vector3( - Infinity, - Infinity, - Infinity ), @@ -31279,7 +31618,7 @@ class BufferGeometry extends EventDispatcher { if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - console.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + error( 'BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); } @@ -31303,7 +31642,7 @@ class BufferGeometry extends EventDispatcher { if ( position && position.isGLBufferAttribute ) { - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere.', this ); + error( 'BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere.', this ); this.boundingSphere.set( new Vector3(), Infinity ); @@ -31394,7 +31733,7 @@ class BufferGeometry extends EventDispatcher { if ( isNaN( this.boundingSphere.radius ) ) { - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); + error( 'BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); } @@ -31422,7 +31761,7 @@ class BufferGeometry extends EventDispatcher { attributes.normal === undefined || attributes.uv === undefined ) { - console.error( 'THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)' ); + error( 'BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)' ); return; } @@ -31732,7 +32071,7 @@ class BufferGeometry extends EventDispatcher { if ( this.index === null ) { - console.warn( 'THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.' ); + warn( 'BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.' ); return this; } @@ -32058,6 +32397,7 @@ class BufferGeometry extends EventDispatcher { * ``` * * @augments BufferGeometry + * @demo scenes/geometry-browser.html#BoxGeometry */ class BoxGeometry extends BufferGeometry { @@ -32269,6 +32609,7 @@ class BoxGeometry extends BufferGeometry { * ``` * * @augments BufferGeometry + * @demo scenes/geometry-browser.html#PlaneGeometry */ class PlaneGeometry extends BufferGeometry { @@ -32905,7 +33246,7 @@ class Material extends EventDispatcher { * * This method can only be used when rendering with {@link WebGLRenderer}. The * recommended approach when customizing materials is to use `WebGPURenderer` with the new - * Node Material system and [TSL]{@link https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language}. + * Node Material system and [TSL](https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language). * * @param {{vertexShader:string,fragmentShader:string,uniforms:Object}} shaderobject - The object holds the uniforms and the vertex and fragment shader source. * @param {WebGLRenderer} renderer - A reference to the renderer. @@ -32944,7 +33285,7 @@ class Material extends EventDispatcher { if ( newValue === undefined ) { - console.warn( `THREE.Material: parameter '${ key }' has value of undefined.` ); + warn( `Material: parameter '${ key }' has value of undefined.` ); continue; } @@ -32953,7 +33294,7 @@ class Material extends EventDispatcher { if ( currentValue === undefined ) { - console.warn( `THREE.Material: '${ key }' is not a property of THREE.${ this.type }.` ); + warn( `Material: '${ key }' is not a property of THREE.${ this.type }.` ); continue; } @@ -33214,6 +33555,7 @@ class Material extends EventDispatcher { if ( this.alphaToCoverage === true ) data.alphaToCoverage = true; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = true; if ( this.forceSinglePass === true ) data.forceSinglePass = true; + if ( this.allowOverride === false ) data.allowOverride = false; if ( this.wireframe === true ) data.wireframe = true; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; @@ -33349,6 +33691,7 @@ class Material extends EventDispatcher { this.alphaToCoverage = source.alphaToCoverage; this.premultipliedAlpha = source.premultipliedAlpha; this.forceSinglePass = source.forceSinglePass; + this.allowOverride = source.allowOverride; this.visible = source.visible; @@ -33394,8 +33737,20 @@ class Material extends EventDispatcher { } -// Uniform Utilities +/** + * Provides utility functions for managing uniforms. + * + * @module UniformsUtils + */ +/** + * Clones the given uniform definitions by performing a deep-copy. That means + * if the value of a uniform refers to an object like a Vector3 or Texture, + * the cloned uniform will refer to a new object reference. + * + * @param {Object} src - An object representing uniform definitions. + * @return {Object} The cloned uniforms. + */ function cloneUniforms( src ) { const dst = {}; @@ -33415,7 +33770,7 @@ function cloneUniforms( src ) { if ( property.isRenderTargetTexture ) { - console.warn( 'UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().' ); + warn( 'UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().' ); dst[ u ][ p ] = null; } else { @@ -33442,6 +33797,14 @@ function cloneUniforms( src ) { } +/** + * Merges the given uniform definitions into a single object. Since the + * method internally uses cloneUniforms(), it performs a deep-copy when + * producing the merged uniform definitions. + * + * @param {Array} uniforms - An array of objects containing uniform definitions. + * @return {Object} The merged uniforms. + */ function mergeUniforms( uniforms ) { const merged = {}; @@ -33519,7 +33882,7 @@ var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 * - You can use the directive `#pragma unroll_loop_start` and `#pragma unroll_loop_end` * in order to unroll a `for` loop in GLSL by the shader preprocessor. The directive has * to be placed right above the loop. The loop formatting has to correspond to a defined standard. - * - The loop has to be [normalized]{@link https://en.wikipedia.org/wiki/Normalized_loop}. + * - The loop has to be [normalized](https://en.wikipedia.org/wiki/Normalized_loop). * - The loop variable has to be *i*. * - The value `UNROLLED_LOOP_INDEX` will be replaced with the explicitly * value of *i* for the given iteration and can be used in preprocessor @@ -33660,9 +34023,22 @@ class ShaderMaterial extends Material { this.wireframeLinewidth = 1; /** - * Define whether the material color is affected by global fog settings; `true` + * Defines whether the material color is affected by global fog settings; `true` * to pass fog uniforms to the shader. * + * Setting this property to `true` requires the definition of fog uniforms. It is + * recommended to use `UniformsUtils.merge()` to combine the custom shader uniforms + * with predefined fog uniforms. + * + * ```js + * const material = new ShaderMaterial( { + * uniforms: UniformsUtils.merge( [ UniformsLib[ 'fog' ], shaderUniforms ] ); + * vertexShader: vertexShader, + * fragmentShader: fragmentShader, + * fog: true + * } ); + * ``` + * * @type {boolean} * @default false */ @@ -33725,7 +34101,7 @@ class ShaderMaterial extends Material { }; /** - * If set, this calls [gl.bindAttribLocation]{@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bindAttribLocation} + * If set, this calls [gl.bindAttribLocation](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bindAttribLocation) * to bind a generic vertex index to an attribute variable. * * @type {string|undefined} @@ -33781,6 +34157,12 @@ class ShaderMaterial extends Material { this.glslVersion = source.glslVersion; + this.defaultAttributeValues = Object.assign( {}, source.defaultAttributeValues ); + + this.index0AttributeName = source.index0AttributeName; + + this.uniformsNeedUpdate = source.uniformsNeedUpdate; + return this; } @@ -35075,6 +35457,7 @@ class Triangle { * This material is not affected by lights. * * @augments Material + * @demo scenes/material-browser.html#MeshBasicMaterial */ class MeshBasicMaterial extends Material { @@ -35382,7 +35765,7 @@ class Mesh extends Object3D { * morph targets name, the value its attribute index. This member is `undefined` * by default and only set when morph targets are detected in the geometry. * - * @type {Object|undefined} + * @type {Object|undefined} * @default undefined */ this.morphTargetDictionary = undefined; @@ -35812,7 +36195,7 @@ var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; -var batching_pars_vertex = "#ifdef USE_BATCHING\n\t#if ! defined( GL_ANGLE_multi_draw )\n\t#define gl_DrawID _gl_DrawID\n\tuniform int _gl_DrawID;\n\t#endif\n\tuniform highp sampler2D batchingTexture;\n\tuniform highp usampler2D batchingIdTexture;\n\tmat4 getBatchingMatrix( const in float i ) {\n\t\tint size = textureSize( batchingTexture, 0 ).x;\n\t\tint j = int( i ) * 4;\n\t\tint x = j % size;\n\t\tint y = j / size;\n\t\tvec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 );\n\t\tvec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 );\n\t\tvec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 );\n\t\tvec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 );\n\t\treturn mat4( v1, v2, v3, v4 );\n\t}\n\tfloat getIndirectIndex( const in int i ) {\n\t\tint size = textureSize( batchingIdTexture, 0 ).x;\n\t\tint x = i % size;\n\t\tint y = i / size;\n\t\treturn float( texelFetch( batchingIdTexture, ivec2( x, y ), 0 ).r );\n\t}\n#endif\n#ifdef USE_BATCHING_COLOR\n\tuniform sampler2D batchingColorTexture;\n\tvec3 getBatchingColor( const in float i ) {\n\t\tint size = textureSize( batchingColorTexture, 0 ).x;\n\t\tint j = int( i );\n\t\tint x = j % size;\n\t\tint y = j / size;\n\t\treturn texelFetch( batchingColorTexture, ivec2( x, y ), 0 ).rgb;\n\t}\n#endif"; +var batching_pars_vertex = "#ifdef USE_BATCHING\n\t#if ! defined( GL_ANGLE_multi_draw )\n\t#define gl_DrawID _gl_DrawID\n\tuniform int _gl_DrawID;\n\t#endif\n\tuniform highp sampler2D batchingTexture;\n\tuniform highp usampler2D batchingIdTexture;\n\tmat4 getBatchingMatrix( const in float i ) {\n\t\tint size = textureSize( batchingTexture, 0 ).x;\n\t\tint j = int( i ) * 4;\n\t\tint x = j % size;\n\t\tint y = j / size;\n\t\tvec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 );\n\t\tvec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 );\n\t\tvec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 );\n\t\tvec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 );\n\t\treturn mat4( v1, v2, v3, v4 );\n\t}\n\tfloat getIndirectIndex( const in int i ) {\n\t\tint size = textureSize( batchingIdTexture, 0 ).x;\n\t\tint x = i % size;\n\t\tint y = i / size;\n\t\treturn float( texelFetch( batchingIdTexture, ivec2( x, y ), 0 ).r );\n\t}\n#endif\n#ifdef USE_BATCHING_COLOR\n\tuniform sampler2D batchingColorTexture;\n\tvec4 getBatchingColor( const in float i ) {\n\t\tint size = textureSize( batchingColorTexture, 0 ).x;\n\t\tint j = int( i );\n\t\tint x = j % size;\n\t\tint y = j / size;\n\t\treturn texelFetch( batchingColorTexture, ivec2( x, y ), 0 );\n\t}\n#endif"; var batching_vertex = "#ifdef USE_BATCHING\n\tmat4 batchingMatrix = getBatchingMatrix( getIndirectIndex( gl_DrawID ) );\n#endif"; @@ -35834,15 +36217,15 @@ var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 v var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; -var color_fragment = "#if defined( USE_COLOR_ALPHA )\n\tdiffuseColor *= vColor;\n#elif defined( USE_COLOR )\n\tdiffuseColor.rgb *= vColor;\n#endif"; +var color_fragment = "#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA )\n\tdiffuseColor *= vColor;\n#endif"; -var color_pars_fragment = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR )\n\tvarying vec3 vColor;\n#endif"; +var color_pars_fragment = "#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#endif"; -var color_pars_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR )\n\tvarying vec3 vColor;\n#endif"; +var color_pars_vertex = "#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR )\n\tvarying vec4 vColor;\n#endif"; -var color_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvColor = vec4( 1.0 );\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif\n#ifdef USE_BATCHING_COLOR\n\tvec3 batchingColor = getBatchingColor( getIndirectIndex( gl_DrawID ) );\n\tvColor.xyz *= batchingColor.xyz;\n#endif"; +var color_vertex = "#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR )\n\tvColor = vec4( 1.0 );\n#endif\n#ifdef USE_COLOR_ALPHA\n\tvColor *= color;\n#elif defined( USE_COLOR )\n\tvColor.rgb *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.rgb *= instanceColor.rgb;\n#endif\n#ifdef USE_BATCHING_COLOR\n\tvColor *= getBatchingColor( getIndirectIndex( gl_DrawID ) );\n#endif"; -var common$1 = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( a ) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nvec3 pow2( const in vec3 x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\nfloat average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract( sin( sn ) * c );\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\n#ifdef USE_ALPHAHASH\n\tvarying vec3 vPosition;\n#endif\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}\nvec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n}\nfloat F_Schlick( const in float f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n} // validated"; +var common$1 = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( a ) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nvec3 pow2( const in vec3 x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\nfloat average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract( sin( sn ) * c );\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\n#ifdef USE_ALPHAHASH\n\tvarying vec3 vPosition;\n#endif\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}\nvec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n}\nfloat F_Schlick( const in float f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n} // validated"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\thighp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tuv.x += filterInt * 3.0 * cubeUV_minTileSize;\n\t\tuv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize );\n\t\tuv.x *= CUBEUV_TEXEL_WIDTH;\n\t\tuv.y *= CUBEUV_TEXEL_HEIGHT;\n\t\t#ifdef texture2DGradEXT\n\t\t\treturn texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb;\n\t\t#else\n\t\t\treturn texture2D( envMap, uv ).rgb;\n\t\t#endif\n\t}\n\t#define cubeUV_r0 1.0\n\t#define cubeUV_m0 - 2.0\n\t#define cubeUV_r1 0.8\n\t#define cubeUV_m1 - 1.0\n\t#define cubeUV_r4 0.4\n\t#define cubeUV_m4 2.0\n\t#define cubeUV_r5 0.305\n\t#define cubeUV_m5 3.0\n\t#define cubeUV_r6 0.21\n\t#define cubeUV_m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= cubeUV_r1 ) {\n\t\t\tmip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0;\n\t\t} else if ( roughness >= cubeUV_r4 ) {\n\t\t\tmip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1;\n\t\t} else if ( roughness >= cubeUV_r5 ) {\n\t\t\tmip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4;\n\t\t} else if ( roughness >= cubeUV_r6 ) {\n\t\t\tmip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; @@ -35860,9 +36243,9 @@ var colorspace_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; var colorspace_pars_fragment = "vec4 LinearTransferOETF( in vec4 value ) {\n\treturn value;\n}\nvec4 sRGBTransferEOTF( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 sRGBTransferOETF( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}"; -var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, envMapRotation * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; +var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, envMapRotation * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t\t#endif\n\t#endif\n#endif"; -var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform mat3 envMapRotation;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; +var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform mat3 envMapRotation;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n#endif"; var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; @@ -35888,7 +36271,7 @@ var lights_lambert_pars_fragment = "varying vec3 vViewPosition;\nstruct LambertM var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\n#if defined( USE_LIGHT_PROBES )\n\tuniform vec3 lightProbe[ 9 ];\n#endif\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) {\n\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\treturn irradiance;\n}\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif ( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n}\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\n\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) {\n\t\tlight.color = directionalLight.color;\n\t\tlight.direction = directionalLight.direction;\n\t\tlight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) {\n\t\tvec3 lVector = pointLight.position - geometryPosition;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tlight.color = pointLight.color;\n\t\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) {\n\t\tvec3 lVector = spotLight.position - geometryPosition;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat angleCos = dot( light.direction, spotLight.direction );\n\t\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\tif ( spotAttenuation > 0.0 ) {\n\t\t\tfloat lightDistance = length( lVector );\n\t\t\tlight.color = spotLight.color * spotAttenuation;\n\t\t\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t\t} else {\n\t\t\tlight.color = vec3( 0.0 );\n\t\t\tlight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) {\n\t\tfloat dotNL = dot( normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\treturn irradiance;\n\t}\n#endif"; -var envmap_physical_pars_fragment = "#ifdef USE_ENVMAP\n\tvec3 getIBLIrradiance( const in vec3 normal ) {\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 );\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\tvec3 reflectVec = reflect( - viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness );\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\t#ifdef USE_ANISOTROPY\n\t\tvec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) {\n\t\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\t\tvec3 bentNormal = cross( bitangent, viewDir );\n\t\t\t\tbentNormal = normalize( cross( bentNormal, bitangent ) );\n\t\t\t\tbentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) );\n\t\t\t\treturn getIBLRadiance( viewDir, bentNormal, roughness );\n\t\t\t#else\n\t\t\t\treturn vec3( 0.0 );\n\t\t\t#endif\n\t\t}\n\t#endif\n#endif"; +var envmap_physical_pars_fragment = "#ifdef USE_ENVMAP\n\tvec3 getIBLIrradiance( const in vec3 normal ) {\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 );\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\tvec3 reflectVec = reflect( - viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, pow4( roughness ) ) );\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness );\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\t#ifdef USE_ANISOTROPY\n\t\tvec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) {\n\t\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\t\tvec3 bentNormal = cross( bitangent, viewDir );\n\t\t\t\tbentNormal = normalize( cross( bentNormal, bitangent ) );\n\t\t\t\tbentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) );\n\t\t\t\treturn getIBLRadiance( viewDir, bentNormal, roughness );\n\t\t\t#else\n\t\t\t\treturn vec3( 0.0 );\n\t\t\t#endif\n\t\t}\n\t#endif\n#endif"; var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; @@ -35898,15 +36281,15 @@ var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor var lights_phong_pars_fragment = "varying vec3 vViewPosition;\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong"; -var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n#ifdef IOR\n\tmaterial.ior = ior;\n\t#ifdef USE_SPECULAR\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularColorFactor = specularColor;\n\t\t#ifdef USE_SPECULAR_COLORMAP\n\t\t\tspecularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb;\n\t\t#endif\n\t\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a;\n\t\t#endif\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\t#else\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularColorFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\t#endif\n\tmaterial.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n#endif\n#ifdef USE_CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_DISPERSION\n\tmaterial.dispersion = dispersion;\n#endif\n#ifdef USE_IRIDESCENCE\n\tmaterial.iridescence = iridescence;\n\tmaterial.iridescenceIOR = iridescenceIOR;\n\t#ifdef USE_IRIDESCENCEMAP\n\t\tmaterial.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r;\n\t#endif\n\t#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\t\tmaterial.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum;\n\t#else\n\t\tmaterial.iridescenceThickness = iridescenceThicknessMaximum;\n\t#endif\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheenColor;\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tmaterial.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb;\n\t#endif\n\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a;\n\t#endif\n#endif\n#ifdef USE_ANISOTROPY\n\t#ifdef USE_ANISOTROPYMAP\n\t\tmat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x );\n\t\tvec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb;\n\t\tvec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b;\n\t#else\n\t\tvec2 anisotropyV = anisotropyVector;\n\t#endif\n\tmaterial.anisotropy = length( anisotropyV );\n\tif( material.anisotropy == 0.0 ) {\n\t\tanisotropyV = vec2( 1.0, 0.0 );\n\t} else {\n\t\tanisotropyV /= material.anisotropy;\n\t\tmaterial.anisotropy = saturate( material.anisotropy );\n\t}\n\tmaterial.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) );\n\tmaterial.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y;\n\tmaterial.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y;\n#endif"; +var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.diffuseContribution = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.metalness = metalnessFactor;\nvec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n#ifdef IOR\n\tmaterial.ior = ior;\n\t#ifdef USE_SPECULAR\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularColorFactor = specularColor;\n\t\t#ifdef USE_SPECULAR_COLORMAP\n\t\t\tspecularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb;\n\t\t#endif\n\t\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a;\n\t\t#endif\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\t#else\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularColorFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\t#endif\n\tmaterial.specularColor = min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor;\n\tmaterial.specularColorBlended = mix( material.specularColor, diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = vec3( 0.04 );\n\tmaterial.specularColorBlended = mix( material.specularColor, diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n#endif\n#ifdef USE_CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_DISPERSION\n\tmaterial.dispersion = dispersion;\n#endif\n#ifdef USE_IRIDESCENCE\n\tmaterial.iridescence = iridescence;\n\tmaterial.iridescenceIOR = iridescenceIOR;\n\t#ifdef USE_IRIDESCENCEMAP\n\t\tmaterial.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r;\n\t#endif\n\t#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\t\tmaterial.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum;\n\t#else\n\t\tmaterial.iridescenceThickness = iridescenceThicknessMaximum;\n\t#endif\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheenColor;\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tmaterial.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb;\n\t#endif\n\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.0001, 1.0 );\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a;\n\t#endif\n#endif\n#ifdef USE_ANISOTROPY\n\t#ifdef USE_ANISOTROPYMAP\n\t\tmat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x );\n\t\tvec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb;\n\t\tvec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b;\n\t#else\n\t\tvec2 anisotropyV = anisotropyVector;\n\t#endif\n\tmaterial.anisotropy = length( anisotropyV );\n\tif( material.anisotropy == 0.0 ) {\n\t\tanisotropyV = vec2( 1.0, 0.0 );\n\t} else {\n\t\tanisotropyV /= material.anisotropy;\n\t\tmaterial.anisotropy = saturate( material.anisotropy );\n\t}\n\tmaterial.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) );\n\tmaterial.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y;\n\tmaterial.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y;\n#endif"; -var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat roughness;\n\tvec3 specularColor;\n\tfloat specularF90;\n\tfloat dispersion;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\t#ifdef USE_IRIDESCENCE\n\t\tfloat iridescence;\n\t\tfloat iridescenceIOR;\n\t\tfloat iridescenceThickness;\n\t\tvec3 iridescenceFresnel;\n\t\tvec3 iridescenceF0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenColor;\n\t\tfloat sheenRoughness;\n\t#endif\n\t#ifdef IOR\n\t\tfloat ior;\n\t#endif\n\t#ifdef USE_TRANSMISSION\n\t\tfloat transmission;\n\t\tfloat transmissionAlpha;\n\t\tfloat thickness;\n\t\tfloat attenuationDistance;\n\t\tvec3 attenuationColor;\n\t#endif\n\t#ifdef USE_ANISOTROPY\n\t\tfloat anisotropy;\n\t\tfloat alphaT;\n\t\tvec3 anisotropyT;\n\t\tvec3 anisotropyB;\n\t#endif\n};\nvec3 clearcoatSpecularDirect = vec3( 0.0 );\nvec3 clearcoatSpecularIndirect = vec3( 0.0 );\nvec3 sheenSpecularDirect = vec3( 0.0 );\nvec3 sheenSpecularIndirect = vec3(0.0 );\nvec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) {\n float x = clamp( 1.0 - dotVH, 0.0, 1.0 );\n float x2 = x * x;\n float x5 = clamp( x * x2 * x2, 0.0, 0.9999 );\n return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 );\n}\nfloat V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\n#ifdef USE_ANISOTROPY\n\tfloat V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) {\n\t\tfloat gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) );\n\t\tfloat gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) );\n\t\tfloat v = 0.5 / ( gv + gl );\n\t\treturn saturate(v);\n\t}\n\tfloat D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) {\n\t\tfloat a2 = alphaT * alphaB;\n\t\thighp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH );\n\t\thighp float v2 = dot( v, v );\n\t\tfloat w2 = a2 / v2;\n\t\treturn RECIPROCAL_PI * a2 * pow2 ( w2 );\n\t}\n#endif\n#ifdef USE_CLEARCOAT\n\tvec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) {\n\t\tvec3 f0 = material.clearcoatF0;\n\t\tfloat f90 = material.clearcoatF90;\n\t\tfloat roughness = material.clearcoatRoughness;\n\t\tfloat alpha = pow2( roughness );\n\t\tvec3 halfDir = normalize( lightDir + viewDir );\n\t\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\t\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\t\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\t\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\t\tvec3 F = F_Schlick( f0, f90, dotVH );\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\t\tfloat D = D_GGX( alpha, dotNH );\n\t\treturn F * ( V * D );\n\t}\n#endif\nvec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {\n\tvec3 f0 = material.specularColor;\n\tfloat f90 = material.specularF90;\n\tfloat roughness = material.roughness;\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\t#ifdef USE_IRIDESCENCE\n\t\tF = mix( F, material.iridescenceFresnel, material.iridescence );\n\t#endif\n\t#ifdef USE_ANISOTROPY\n\t\tfloat dotTL = dot( material.anisotropyT, lightDir );\n\t\tfloat dotTV = dot( material.anisotropyT, viewDir );\n\t\tfloat dotTH = dot( material.anisotropyT, halfDir );\n\t\tfloat dotBL = dot( material.anisotropyB, lightDir );\n\t\tfloat dotBV = dot( material.anisotropyB, viewDir );\n\t\tfloat dotBH = dot( material.anisotropyB, halfDir );\n\t\tfloat V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL );\n\t\tfloat D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH );\n\t#else\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\t\tfloat D = D_GGX( alpha, dotNH );\n\t#endif\n\treturn F * ( V * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie( float roughness, float dotNH ) {\n\tfloat alpha = pow2( roughness );\n\tfloat invAlpha = 1.0 / alpha;\n\tfloat cos2h = dotNH * dotNH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n}\nfloat V_Neubelt( float dotNV, float dotNL ) {\n\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\n}\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat D = D_Charlie( sheenRoughness, dotNH );\n\tfloat V = V_Neubelt( dotNV, dotNL );\n\treturn sheenColor * ( D * V );\n}\n#endif\nfloat IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat r2 = roughness * roughness;\n\tfloat a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95;\n\tfloat b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72;\n\tfloat DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) );\n\treturn saturate( DG * RECIPROCAL_PI );\n}\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\treturn fab;\n}\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\treturn specularColor * fab.x + specularF90 * fab.y;\n}\n#ifdef USE_IRIDESCENCE\nvoid computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#else\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#endif\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\t#ifdef USE_IRIDESCENCE\n\t\tvec3 Fr = mix( specularColor, iridescenceF0, iridescence );\n\t#else\n\t\tvec3 Fr = specularColor;\n\t#endif\n\tvec3 FssEss = Fr * fab.x + specularF90 * fab.y;\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometryNormal;\n\t\tvec3 viewDir = geometryViewDir;\n\t\tvec3 position = geometryPosition;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.roughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\t\tclearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness );\n\t#endif\n\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material );\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness );\n\t#endif\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\t#ifdef USE_IRIDESCENCE\n\t\tcomputeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering );\n\t#else\n\t\tcomputeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\n\t#endif\n\tvec3 totalScattering = singleScattering + multiScattering;\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) );\n\treflectedLight.indirectSpecular += radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; +var lights_physical_pars_fragment = "uniform sampler2D dfgLUT;\nstruct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tvec3 diffuseContribution;\n\tvec3 specularColor;\n\tvec3 specularColorBlended;\n\tfloat roughness;\n\tfloat metalness;\n\tfloat specularF90;\n\tfloat dispersion;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\t#ifdef USE_IRIDESCENCE\n\t\tfloat iridescence;\n\t\tfloat iridescenceIOR;\n\t\tfloat iridescenceThickness;\n\t\tvec3 iridescenceFresnel;\n\t\tvec3 iridescenceF0;\n\t\tvec3 iridescenceFresnelDielectric;\n\t\tvec3 iridescenceFresnelMetallic;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenColor;\n\t\tfloat sheenRoughness;\n\t#endif\n\t#ifdef IOR\n\t\tfloat ior;\n\t#endif\n\t#ifdef USE_TRANSMISSION\n\t\tfloat transmission;\n\t\tfloat transmissionAlpha;\n\t\tfloat thickness;\n\t\tfloat attenuationDistance;\n\t\tvec3 attenuationColor;\n\t#endif\n\t#ifdef USE_ANISOTROPY\n\t\tfloat anisotropy;\n\t\tfloat alphaT;\n\t\tvec3 anisotropyT;\n\t\tvec3 anisotropyB;\n\t#endif\n};\nvec3 clearcoatSpecularDirect = vec3( 0.0 );\nvec3 clearcoatSpecularIndirect = vec3( 0.0 );\nvec3 sheenSpecularDirect = vec3( 0.0 );\nvec3 sheenSpecularIndirect = vec3(0.0 );\nvec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) {\n float x = clamp( 1.0 - dotVH, 0.0, 1.0 );\n float x2 = x * x;\n float x5 = clamp( x * x2 * x2, 0.0, 0.9999 );\n return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 );\n}\nfloat V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\n#ifdef USE_ANISOTROPY\n\tfloat V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) {\n\t\tfloat gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) );\n\t\tfloat gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) );\n\t\tfloat v = 0.5 / ( gv + gl );\n\t\treturn v;\n\t}\n\tfloat D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) {\n\t\tfloat a2 = alphaT * alphaB;\n\t\thighp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH );\n\t\thighp float v2 = dot( v, v );\n\t\tfloat w2 = a2 / v2;\n\t\treturn RECIPROCAL_PI * a2 * pow2 ( w2 );\n\t}\n#endif\n#ifdef USE_CLEARCOAT\n\tvec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) {\n\t\tvec3 f0 = material.clearcoatF0;\n\t\tfloat f90 = material.clearcoatF90;\n\t\tfloat roughness = material.clearcoatRoughness;\n\t\tfloat alpha = pow2( roughness );\n\t\tvec3 halfDir = normalize( lightDir + viewDir );\n\t\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\t\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\t\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\t\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\t\tvec3 F = F_Schlick( f0, f90, dotVH );\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\t\tfloat D = D_GGX( alpha, dotNH );\n\t\treturn F * ( V * D );\n\t}\n#endif\nvec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {\n\tvec3 f0 = material.specularColorBlended;\n\tfloat f90 = material.specularF90;\n\tfloat roughness = material.roughness;\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\t#ifdef USE_IRIDESCENCE\n\t\tF = mix( F, material.iridescenceFresnel, material.iridescence );\n\t#endif\n\t#ifdef USE_ANISOTROPY\n\t\tfloat dotTL = dot( material.anisotropyT, lightDir );\n\t\tfloat dotTV = dot( material.anisotropyT, viewDir );\n\t\tfloat dotTH = dot( material.anisotropyT, halfDir );\n\t\tfloat dotBL = dot( material.anisotropyB, lightDir );\n\t\tfloat dotBV = dot( material.anisotropyB, viewDir );\n\t\tfloat dotBH = dot( material.anisotropyB, halfDir );\n\t\tfloat V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL );\n\t\tfloat D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH );\n\t#else\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\t\tfloat D = D_GGX( alpha, dotNH );\n\t#endif\n\treturn F * ( V * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transpose( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie( float roughness, float dotNH ) {\n\tfloat alpha = pow2( roughness );\n\tfloat invAlpha = 1.0 / alpha;\n\tfloat cos2h = dotNH * dotNH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n}\nfloat V_Neubelt( float dotNV, float dotNL ) {\n\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\n}\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat D = D_Charlie( sheenRoughness, dotNH );\n\tfloat V = V_Neubelt( dotNV, dotNL );\n\treturn sheenColor * ( D * V );\n}\n#endif\nfloat IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat r2 = roughness * roughness;\n\tfloat rInv = 1.0 / ( roughness + 0.1 );\n\tfloat a = -1.9362 + 1.0678 * roughness + 0.4573 * r2 - 0.8469 * rInv;\n\tfloat b = -0.6014 + 0.5538 * roughness - 0.4670 * r2 - 0.1255 * rInv;\n\tfloat DG = exp( a * dotNV + b );\n\treturn saturate( DG );\n}\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 fab = texture2D( dfgLUT, vec2( roughness, dotNV ) ).rg;\n\treturn specularColor * fab.x + specularF90 * fab.y;\n}\n#ifdef USE_IRIDESCENCE\nvoid computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#else\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#endif\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 fab = texture2D( dfgLUT, vec2( roughness, dotNV ) ).rg;\n\t#ifdef USE_IRIDESCENCE\n\t\tvec3 Fr = mix( specularColor, iridescenceF0, iridescence );\n\t#else\n\t\tvec3 Fr = specularColor;\n\t#endif\n\tvec3 FssEss = Fr * fab.x + specularF90 * fab.y;\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nvec3 BRDF_GGX_Multiscatter( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {\n\tvec3 singleScatter = BRDF_GGX( lightDir, viewDir, normal, material );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 dfgV = texture2D( dfgLUT, vec2( material.roughness, dotNV ) ).rg;\n\tvec2 dfgL = texture2D( dfgLUT, vec2( material.roughness, dotNL ) ).rg;\n\tvec3 FssEss_V = material.specularColorBlended * dfgV.x + material.specularF90 * dfgV.y;\n\tvec3 FssEss_L = material.specularColorBlended * dfgL.x + material.specularF90 * dfgL.y;\n\tfloat Ess_V = dfgV.x + dfgV.y;\n\tfloat Ess_L = dfgL.x + dfgL.y;\n\tfloat Ems_V = 1.0 - Ess_V;\n\tfloat Ems_L = 1.0 - Ess_L;\n\tvec3 Favg = material.specularColorBlended + ( 1.0 - material.specularColorBlended ) * 0.047619;\n\tvec3 Fms = FssEss_V * FssEss_L * Favg / ( 1.0 - Ems_V * Ems_L * Favg + EPSILON );\n\tfloat compensationFactor = Ems_V * Ems_L;\n\tvec3 multiScatter = Fms * compensationFactor;\n\treturn singleScatter + multiScatter;\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometryNormal;\n\t\tvec3 viewDir = geometryViewDir;\n\t\tvec3 position = geometryPosition;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.roughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColorBlended * t2.x + ( material.specularF90 - material.specularColorBlended ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseContribution * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t\t#ifdef USE_CLEARCOAT\n\t\t\tvec3 Ncc = geometryClearcoatNormal;\n\t\t\tvec2 uvClearcoat = LTC_Uv( Ncc, viewDir, material.clearcoatRoughness );\n\t\t\tvec4 t1Clearcoat = texture2D( ltc_1, uvClearcoat );\n\t\t\tvec4 t2Clearcoat = texture2D( ltc_2, uvClearcoat );\n\t\t\tmat3 mInvClearcoat = mat3(\n\t\t\t\tvec3( t1Clearcoat.x, 0, t1Clearcoat.y ),\n\t\t\t\tvec3( 0, 1, 0 ),\n\t\t\t\tvec3( t1Clearcoat.z, 0, t1Clearcoat.w )\n\t\t\t);\n\t\t\tvec3 fresnelClearcoat = material.clearcoatF0 * t2Clearcoat.x + ( material.clearcoatF90 - material.clearcoatF0 ) * t2Clearcoat.y;\n\t\t\tclearcoatSpecularDirect += lightColor * fresnelClearcoat * LTC_Evaluate( Ncc, viewDir, position, mInvClearcoat, rectCoords );\n\t\t#endif\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\t\tclearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material );\n\t#endif\n\t#ifdef USE_SHEEN\n \n \t\tsheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness );\n \n \t\tfloat sheenAlbedoV = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness );\n \t\tfloat sheenAlbedoL = IBLSheenBRDF( geometryNormal, directLight.direction, material.sheenRoughness );\n \n \t\tfloat sheenEnergyComp = 1.0 - max3( material.sheenColor ) * max( sheenAlbedoV, sheenAlbedoL );\n \n \t\tirradiance *= sheenEnergyComp;\n \n \t#endif\n\treflectedLight.directSpecular += irradiance * BRDF_GGX_Multiscatter( directLight.direction, geometryViewDir, geometryNormal, material );\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseContribution );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 diffuse = irradiance * BRDF_Lambert( material.diffuseContribution );\n\t#ifdef USE_SHEEN\n\t\tfloat sheenAlbedo = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness );\n\t\tfloat sheenEnergyComp = 1.0 - max3( material.sheenColor ) * sheenAlbedo;\n\t\tdiffuse *= sheenEnergyComp;\n\t#endif\n\treflectedLight.indirectDiffuse += diffuse;\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ) * RECIPROCAL_PI;\n \t#endif\n\tvec3 singleScatteringDielectric = vec3( 0.0 );\n\tvec3 multiScatteringDielectric = vec3( 0.0 );\n\tvec3 singleScatteringMetallic = vec3( 0.0 );\n\tvec3 multiScatteringMetallic = vec3( 0.0 );\n\t#ifdef USE_IRIDESCENCE\n\t\tcomputeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnelDielectric, material.roughness, singleScatteringDielectric, multiScatteringDielectric );\n\t\tcomputeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.diffuseColor, material.specularF90, material.iridescence, material.iridescenceFresnelMetallic, material.roughness, singleScatteringMetallic, multiScatteringMetallic );\n\t#else\n\t\tcomputeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScatteringDielectric, multiScatteringDielectric );\n\t\tcomputeMultiscattering( geometryNormal, geometryViewDir, material.diffuseColor, material.specularF90, material.roughness, singleScatteringMetallic, multiScatteringMetallic );\n\t#endif\n\tvec3 singleScattering = mix( singleScatteringDielectric, singleScatteringMetallic, material.metalness );\n\tvec3 multiScattering = mix( multiScatteringDielectric, multiScatteringMetallic, material.metalness );\n\tvec3 totalScatteringDielectric = singleScatteringDielectric + multiScatteringDielectric;\n\tvec3 diffuse = material.diffuseContribution * ( 1.0 - totalScatteringDielectric );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tvec3 indirectSpecular = radiance * singleScattering;\n\tindirectSpecular += multiScattering * cosineWeightedIrradiance;\n\tvec3 indirectDiffuse = diffuse * cosineWeightedIrradiance;\n\t#ifdef USE_SHEEN\n\t\tfloat sheenAlbedo = IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness );\n\t\tfloat sheenEnergyComp = 1.0 - max3( material.sheenColor ) * sheenAlbedo;\n\t\tindirectSpecular *= sheenEnergyComp;\n\t\tindirectDiffuse *= sheenEnergyComp;\n\t#endif\n\treflectedLight.indirectSpecular += indirectSpecular;\n\treflectedLight.indirectDiffuse += indirectDiffuse;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; -var lights_fragment_begin = "\nvec3 geometryPosition = - vViewPosition;\nvec3 geometryNormal = normal;\nvec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\nvec3 geometryClearcoatNormal = vec3( 0.0 );\n#ifdef USE_CLEARCOAT\n\tgeometryClearcoatNormal = clearcoatNormal;\n#endif\n#ifdef USE_IRIDESCENCE\n\tfloat dotNVi = saturate( dot( normal, geometryViewDir ) );\n\tif ( material.iridescenceThickness == 0.0 ) {\n\t\tmaterial.iridescence = 0.0;\n\t} else {\n\t\tmaterial.iridescence = saturate( material.iridescence );\n\t}\n\tif ( material.iridescence > 0.0 ) {\n\t\tmaterial.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor );\n\t\tmaterial.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi );\n\t}\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointLightInfo( pointLight, geometryPosition, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowIntensity, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\tvec4 spotColor;\n\tvec3 spotLightCoord;\n\tbool inSpotLightMap;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotLightInfo( spotLight, geometryPosition, directLight );\n\t\t#if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX\n\t\t#elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t#define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS\n\t\t#else\n\t\t#define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#endif\n\t\t#if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS )\n\t\t\tspotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w;\n\t\t\tinSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) );\n\t\t\tspotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy );\n\t\t\tdirectLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color;\n\t\t#endif\n\t\t#undef SPOT_LIGHT_MAP_INDEX\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowIntensity, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalLightInfo( directionalLight, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#if defined( USE_LIGHT_PROBES )\n\t\tirradiance += getLightProbeIrradiance( lightProbe, geometryNormal );\n\t#endif\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; +var lights_fragment_begin = "\nvec3 geometryPosition = - vViewPosition;\nvec3 geometryNormal = normal;\nvec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\nvec3 geometryClearcoatNormal = vec3( 0.0 );\n#ifdef USE_CLEARCOAT\n\tgeometryClearcoatNormal = clearcoatNormal;\n#endif\n#ifdef USE_IRIDESCENCE\n\tfloat dotNVi = saturate( dot( normal, geometryViewDir ) );\n\tif ( material.iridescenceThickness == 0.0 ) {\n\t\tmaterial.iridescence = 0.0;\n\t} else {\n\t\tmaterial.iridescence = saturate( material.iridescence );\n\t}\n\tif ( material.iridescence > 0.0 ) {\n\t\tmaterial.iridescenceFresnelDielectric = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor );\n\t\tmaterial.iridescenceFresnelMetallic = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.diffuseColor );\n\t\tmaterial.iridescenceFresnel = mix( material.iridescenceFresnelDielectric, material.iridescenceFresnelMetallic, material.metalness );\n\t\tmaterial.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi );\n\t}\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointLightInfo( pointLight, geometryPosition, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowIntensity, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\tvec4 spotColor;\n\tvec3 spotLightCoord;\n\tbool inSpotLightMap;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotLightInfo( spotLight, geometryPosition, directLight );\n\t\t#if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX\n\t\t#elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t#define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS\n\t\t#else\n\t\t#define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#endif\n\t\t#if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS )\n\t\t\tspotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w;\n\t\t\tinSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) );\n\t\t\tspotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy );\n\t\t\tdirectLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color;\n\t\t#endif\n\t\t#undef SPOT_LIGHT_MAP_INDEX\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowIntensity, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalLightInfo( directionalLight, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#if defined( USE_LIGHT_PROBES )\n\t\tirradiance += getLightProbeIrradiance( lightProbe, geometryNormal );\n\t#endif\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; -var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\t\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getIBLIrradiance( geometryNormal );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\t#ifdef USE_ANISOTROPY\n\t\tradiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy );\n\t#else\n\t\tradiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness );\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness );\n\t#endif\n#endif"; +var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\t\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\t#if defined( STANDARD ) || defined( LAMBERT ) || defined( PHONG )\n\t\t\tiblIrradiance += getIBLIrradiance( geometryNormal );\n\t\t#endif\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\t#ifdef USE_ANISOTROPY\n\t\tradiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy );\n\t#else\n\t\tradiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness );\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness );\n\t#endif\n#endif"; -var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n#endif"; +var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\t#if defined( LAMBERT ) || defined( PHONG )\n\t\tirradiance += iblIrradiance;\n\t#endif\n\tRE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n#endif"; var logdepthbuf_fragment = "#if defined( USE_LOGARITHMIC_DEPTH_BUFFER )\n\tgl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; @@ -35960,7 +36343,7 @@ var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP\n\tuniform sampler2D var opaque_fragment = "#ifdef OPAQUE\ndiffuseColor.a = 1.0;\n#endif\n#ifdef USE_TRANSMISSION\ndiffuseColor.a *= material.transmissionAlpha;\n#endif\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );"; -var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;const float ShiftRight8 = 1. / 256.;\nconst float Inv255 = 1. / 255.;\nconst vec4 PackFactors = vec4( 1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0 );\nconst vec2 UnpackFactors2 = vec2( UnpackDownscale, 1.0 / PackFactors.g );\nconst vec3 UnpackFactors3 = vec3( UnpackDownscale / PackFactors.rg, 1.0 / PackFactors.b );\nconst vec4 UnpackFactors4 = vec4( UnpackDownscale / PackFactors.rgb, 1.0 / PackFactors.a );\nvec4 packDepthToRGBA( const in float v ) {\n\tif( v <= 0.0 )\n\t\treturn vec4( 0., 0., 0., 0. );\n\tif( v >= 1.0 )\n\t\treturn vec4( 1., 1., 1., 1. );\n\tfloat vuf;\n\tfloat af = modf( v * PackFactors.a, vuf );\n\tfloat bf = modf( vuf * ShiftRight8, vuf );\n\tfloat gf = modf( vuf * ShiftRight8, vuf );\n\treturn vec4( vuf * Inv255, gf * PackUpscale, bf * PackUpscale, af );\n}\nvec3 packDepthToRGB( const in float v ) {\n\tif( v <= 0.0 )\n\t\treturn vec3( 0., 0., 0. );\n\tif( v >= 1.0 )\n\t\treturn vec3( 1., 1., 1. );\n\tfloat vuf;\n\tfloat bf = modf( v * PackFactors.b, vuf );\n\tfloat gf = modf( vuf * ShiftRight8, vuf );\n\treturn vec3( vuf * Inv255, gf * PackUpscale, bf );\n}\nvec2 packDepthToRG( const in float v ) {\n\tif( v <= 0.0 )\n\t\treturn vec2( 0., 0. );\n\tif( v >= 1.0 )\n\t\treturn vec2( 1., 1. );\n\tfloat vuf;\n\tfloat gf = modf( v * 256., vuf );\n\treturn vec2( vuf * Inv255, gf );\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors4 );\n}\nfloat unpackRGBToDepth( const in vec3 v ) {\n\treturn dot( v, UnpackFactors3 );\n}\nfloat unpackRGToDepth( const in vec2 v ) {\n\treturn v.r * UnpackFactors2.r + v.g * UnpackFactors2.g;\n}\nvec4 pack2HalfToRGBA( const in vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\nvec2 unpackRGBATo2Half( const in vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\treturn depth * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * depth - far );\n}"; +var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;const float ShiftRight8 = 1. / 256.;\nconst float Inv255 = 1. / 255.;\nconst vec4 PackFactors = vec4( 1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0 );\nconst vec2 UnpackFactors2 = vec2( UnpackDownscale, 1.0 / PackFactors.g );\nconst vec3 UnpackFactors3 = vec3( UnpackDownscale / PackFactors.rg, 1.0 / PackFactors.b );\nconst vec4 UnpackFactors4 = vec4( UnpackDownscale / PackFactors.rgb, 1.0 / PackFactors.a );\nvec4 packDepthToRGBA( const in float v ) {\n\tif( v <= 0.0 )\n\t\treturn vec4( 0., 0., 0., 0. );\n\tif( v >= 1.0 )\n\t\treturn vec4( 1., 1., 1., 1. );\n\tfloat vuf;\n\tfloat af = modf( v * PackFactors.a, vuf );\n\tfloat bf = modf( vuf * ShiftRight8, vuf );\n\tfloat gf = modf( vuf * ShiftRight8, vuf );\n\treturn vec4( vuf * Inv255, gf * PackUpscale, bf * PackUpscale, af );\n}\nvec3 packDepthToRGB( const in float v ) {\n\tif( v <= 0.0 )\n\t\treturn vec3( 0., 0., 0. );\n\tif( v >= 1.0 )\n\t\treturn vec3( 1., 1., 1. );\n\tfloat vuf;\n\tfloat bf = modf( v * PackFactors.b, vuf );\n\tfloat gf = modf( vuf * ShiftRight8, vuf );\n\treturn vec3( vuf * Inv255, gf * PackUpscale, bf );\n}\nvec2 packDepthToRG( const in float v ) {\n\tif( v <= 0.0 )\n\t\treturn vec2( 0., 0. );\n\tif( v >= 1.0 )\n\t\treturn vec2( 1., 1. );\n\tfloat vuf;\n\tfloat gf = modf( v * 256., vuf );\n\treturn vec2( vuf * Inv255, gf );\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors4 );\n}\nfloat unpackRGBToDepth( const in vec3 v ) {\n\treturn dot( v, UnpackFactors3 );\n}\nfloat unpackRGToDepth( const in vec2 v ) {\n\treturn v.r * UnpackFactors2.r + v.g * UnpackFactors2.g;\n}\nvec4 pack2HalfToRGBA( const in vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\nvec2 unpackRGBATo2Half( const in vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\n\t\treturn depth * ( far - near ) - far;\n\t#else\n\t\treturn depth * ( near - far ) - near;\n\t#endif\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\t\n\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\treturn ( near * far ) / ( ( near - far ) * depth - near );\n\t#else\n\t\treturn ( near * far ) / ( ( far - near ) * depth - far );\n\t#endif\n}"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; @@ -35974,13 +36357,13 @@ var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUG var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; -var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n#endif\n#if NUM_SPOT_LIGHT_MAPS > 0\n\tuniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ];\n#endif\n#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\tfloat depth = unpackRGBAToDepth( texture2D( depths, uv ) );\n\t\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\t\treturn step( depth, compare );\n\t\t#else\n\t\t\treturn step( compare, depth );\n\t\t#endif\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow( sampler2D shadow, vec2 uv, float compare ) {\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\t\tfloat hard_shadow = step( distribution.x, compare );\n\t\t#else\n\t\t\tfloat hard_shadow = step( compare, distribution.x );\n\t\t#endif\n\t\tif ( hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;\n\t\tbool frustumTest = inFrustum && shadowCoord.z <= 1.0;\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn mix( 1.0, shadow, shadowIntensity );\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tfloat shadow = 1.0;\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\t\n\t\tfloat lightToPositionLength = length( lightToPosition );\n\t\tif ( lightToPositionLength - shadowCameraFar <= 0.0 && lightToPositionLength - shadowCameraNear >= 0.0 ) {\n\t\t\tfloat dp = ( lightToPositionLength - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\t\tdp += shadowBias;\n\t\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\t\tshadow = (\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t\t) * ( 1.0 / 9.0 );\n\t\t\t#else\n\t\t\t\tshadow = texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t\t#endif\n\t\t}\n\t\treturn mix( 1.0, shadow, shadowIntensity );\n\t}\n#endif"; +var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n#endif\n#if NUM_SPOT_LIGHT_MAPS > 0\n\tuniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ];\n#endif\n#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tuniform sampler2DShadow directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\t#else\n\t\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\t#endif\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tuniform sampler2DShadow spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\t#else\n\t\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\t#endif\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tuniform samplerCubeShadow pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\t#elif defined( SHADOWMAP_TYPE_BASIC )\n\t\t\tuniform samplerCube pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\t#endif\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\tfloat interleavedGradientNoise( vec2 position ) {\n\t\t\treturn fract( 52.9829189 * fract( dot( position, vec2( 0.06711056, 0.00583715 ) ) ) );\n\t\t}\n\t\tvec2 vogelDiskSample( int sampleIndex, int samplesCount, float phi ) {\n\t\t\tconst float goldenAngle = 2.399963229728653;\n\t\t\tfloat r = sqrt( ( float( sampleIndex ) + 0.5 ) / float( samplesCount ) );\n\t\t\tfloat theta = float( sampleIndex ) * goldenAngle + phi;\n\t\t\treturn vec2( cos( theta ), sin( theta ) ) * r;\n\t\t}\n\t#endif\n\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\tfloat getShadow( sampler2DShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\t\tfloat shadow = 1.0;\n\t\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\t\tshadowCoord.z += shadowBias;\n\t\t\tbool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;\n\t\t\tbool frustumTest = inFrustum && shadowCoord.z <= 1.0;\n\t\t\tif ( frustumTest ) {\n\t\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\t\tfloat radius = shadowRadius * texelSize.x;\n\t\t\t\tfloat phi = interleavedGradientNoise( gl_FragCoord.xy ) * PI2;\n\t\t\t\tshadow = (\n\t\t\t\t\ttexture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 0, 5, phi ) * radius, shadowCoord.z ) ) +\n\t\t\t\t\ttexture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 1, 5, phi ) * radius, shadowCoord.z ) ) +\n\t\t\t\t\ttexture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 2, 5, phi ) * radius, shadowCoord.z ) ) +\n\t\t\t\t\ttexture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 3, 5, phi ) * radius, shadowCoord.z ) ) +\n\t\t\t\t\ttexture( shadowMap, vec3( shadowCoord.xy + vogelDiskSample( 4, 5, phi ) * radius, shadowCoord.z ) )\n\t\t\t\t) * 0.2;\n\t\t\t}\n\t\t\treturn mix( 1.0, shadow, shadowIntensity );\n\t\t}\n\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\t\tfloat shadow = 1.0;\n\t\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\t\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\t\t\tshadowCoord.z -= shadowBias;\n\t\t\t#else\n\t\t\t\tshadowCoord.z += shadowBias;\n\t\t\t#endif\n\t\t\tbool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;\n\t\t\tbool frustumTest = inFrustum && shadowCoord.z <= 1.0;\n\t\t\tif ( frustumTest ) {\n\t\t\t\tvec2 distribution = texture2D( shadowMap, shadowCoord.xy ).rg;\n\t\t\t\tfloat mean = distribution.x;\n\t\t\t\tfloat variance = distribution.y * distribution.y;\n\t\t\t\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\t\t\t\tfloat hard_shadow = step( mean, shadowCoord.z );\n\t\t\t\t#else\n\t\t\t\t\tfloat hard_shadow = step( shadowCoord.z, mean );\n\t\t\t\t#endif\n\t\t\t\t\n\t\t\t\tif ( hard_shadow == 1.0 ) {\n\t\t\t\t\tshadow = 1.0;\n\t\t\t\t} else {\n\t\t\t\t\tvariance = max( variance, 0.0000001 );\n\t\t\t\t\tfloat d = shadowCoord.z - mean;\n\t\t\t\t\tfloat p_max = variance / ( variance + d * d );\n\t\t\t\t\tp_max = clamp( ( p_max - 0.3 ) / 0.65, 0.0, 1.0 );\n\t\t\t\t\tshadow = max( hard_shadow, p_max );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn mix( 1.0, shadow, shadowIntensity );\n\t\t}\n\t#else\n\t\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\t\tfloat shadow = 1.0;\n\t\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\t\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\t\t\tshadowCoord.z -= shadowBias;\n\t\t\t#else\n\t\t\t\tshadowCoord.z += shadowBias;\n\t\t\t#endif\n\t\t\tbool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;\n\t\t\tbool frustumTest = inFrustum && shadowCoord.z <= 1.0;\n\t\t\tif ( frustumTest ) {\n\t\t\t\tfloat depth = texture2D( shadowMap, shadowCoord.xy ).r;\n\t\t\t\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\t\t\t\tshadow = step( depth, shadowCoord.z );\n\t\t\t\t#else\n\t\t\t\t\tshadow = step( shadowCoord.z, depth );\n\t\t\t\t#endif\n\t\t\t}\n\t\t\treturn mix( 1.0, shadow, shadowIntensity );\n\t\t}\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#if defined( SHADOWMAP_TYPE_PCF )\n\tfloat getPointShadow( samplerCubeShadow shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tfloat shadow = 1.0;\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\tvec3 absVec = abs( lightToPosition );\n\t\tfloat viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z );\n\t\tif ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) {\n\t\t\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\t\t\tfloat dp = ( shadowCameraNear * ( shadowCameraFar - viewSpaceZ ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) );\n\t\t\t\tdp -= shadowBias;\n\t\t\t#else\n\t\t\t\tfloat dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) );\n\t\t\t\tdp += shadowBias;\n\t\t\t#endif\n\t\t\tfloat texelSize = shadowRadius / shadowMapSize.x;\n\t\t\tvec3 absDir = abs( bd3D );\n\t\t\tvec3 tangent = absDir.x > absDir.z ? vec3( 0.0, 1.0, 0.0 ) : vec3( 1.0, 0.0, 0.0 );\n\t\t\ttangent = normalize( cross( bd3D, tangent ) );\n\t\t\tvec3 bitangent = cross( bd3D, tangent );\n\t\t\tfloat phi = interleavedGradientNoise( gl_FragCoord.xy ) * PI2;\n\t\t\tvec2 sample0 = vogelDiskSample( 0, 5, phi );\n\t\t\tvec2 sample1 = vogelDiskSample( 1, 5, phi );\n\t\t\tvec2 sample2 = vogelDiskSample( 2, 5, phi );\n\t\t\tvec2 sample3 = vogelDiskSample( 3, 5, phi );\n\t\t\tvec2 sample4 = vogelDiskSample( 4, 5, phi );\n\t\t\tshadow = (\n\t\t\t\ttexture( shadowMap, vec4( bd3D + ( tangent * sample0.x + bitangent * sample0.y ) * texelSize, dp ) ) +\n\t\t\t\ttexture( shadowMap, vec4( bd3D + ( tangent * sample1.x + bitangent * sample1.y ) * texelSize, dp ) ) +\n\t\t\t\ttexture( shadowMap, vec4( bd3D + ( tangent * sample2.x + bitangent * sample2.y ) * texelSize, dp ) ) +\n\t\t\t\ttexture( shadowMap, vec4( bd3D + ( tangent * sample3.x + bitangent * sample3.y ) * texelSize, dp ) ) +\n\t\t\t\ttexture( shadowMap, vec4( bd3D + ( tangent * sample4.x + bitangent * sample4.y ) * texelSize, dp ) )\n\t\t\t) * 0.2;\n\t\t}\n\t\treturn mix( 1.0, shadow, shadowIntensity );\n\t}\n\t#elif defined( SHADOWMAP_TYPE_BASIC )\n\tfloat getPointShadow( samplerCube shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tfloat shadow = 1.0;\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tvec3 absVec = abs( lightToPosition );\n\t\tfloat viewSpaceZ = max( max( absVec.x, absVec.y ), absVec.z );\n\t\tif ( viewSpaceZ - shadowCameraFar <= 0.0 && viewSpaceZ - shadowCameraNear >= 0.0 ) {\n\t\t\tfloat dp = ( shadowCameraFar * ( viewSpaceZ - shadowCameraNear ) ) / ( viewSpaceZ * ( shadowCameraFar - shadowCameraNear ) );\n\t\t\tdp += shadowBias;\n\t\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t\tfloat depth = textureCube( shadowMap, bd3D ).r;\n\t\t\t#ifdef USE_REVERSED_DEPTH_BUFFER\n\t\t\t\tdepth = 1.0 - depth;\n\t\t\t#endif\n\t\t\tshadow = step( dp, depth );\n\t\t}\n\t\treturn mix( 1.0, shadow, shadowIntensity );\n\t}\n\t#endif\n\t#endif\n#endif"; var shadowmap_pars_vertex = "#if NUM_SPOT_LIGHT_COORDS > 0\n\tuniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ];\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n#endif\n#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowIntensity;\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; var shadowmap_vertex = "#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 )\n\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\tvec4 shadowWorldPosition;\n#endif\n#if defined( USE_SHADOWMAP )\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if NUM_SPOT_LIGHT_COORDS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition;\n\t\t#if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t\tshadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias;\n\t\t#endif\n\t\tvSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n#endif"; -var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowIntensity, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowIntensity, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowIntensity, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; +var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowIntensity, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowIntensity, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0 && ( defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_BASIC ) )\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowIntensity, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; @@ -35998,7 +36381,7 @@ var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = to var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn saturate( toneMappingExposure * color );\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 CineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nconst mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3(\n\tvec3( 1.6605, - 0.1246, - 0.0182 ),\n\tvec3( - 0.5876, 1.1329, - 0.1006 ),\n\tvec3( - 0.0728, - 0.0083, 1.1187 )\n);\nconst mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3(\n\tvec3( 0.6274, 0.0691, 0.0164 ),\n\tvec3( 0.3293, 0.9195, 0.0880 ),\n\tvec3( 0.0433, 0.0113, 0.8956 )\n);\nvec3 agxDefaultContrastApprox( vec3 x ) {\n\tvec3 x2 = x * x;\n\tvec3 x4 = x2 * x2;\n\treturn + 15.5 * x4 * x2\n\t\t- 40.14 * x4 * x\n\t\t+ 31.96 * x4\n\t\t- 6.868 * x2 * x\n\t\t+ 0.4298 * x2\n\t\t+ 0.1191 * x\n\t\t- 0.00232;\n}\nvec3 AgXToneMapping( vec3 color ) {\n\tconst mat3 AgXInsetMatrix = mat3(\n\t\tvec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ),\n\t\tvec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ),\n\t\tvec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 )\n\t);\n\tconst mat3 AgXOutsetMatrix = mat3(\n\t\tvec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ),\n\t\tvec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ),\n\t\tvec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 )\n\t);\n\tconst float AgxMinEv = - 12.47393;\tconst float AgxMaxEv = 4.026069;\n\tcolor *= toneMappingExposure;\n\tcolor = LINEAR_SRGB_TO_LINEAR_REC2020 * color;\n\tcolor = AgXInsetMatrix * color;\n\tcolor = max( color, 1e-10 );\tcolor = log2( color );\n\tcolor = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv );\n\tcolor = clamp( color, 0.0, 1.0 );\n\tcolor = agxDefaultContrastApprox( color );\n\tcolor = AgXOutsetMatrix * color;\n\tcolor = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) );\n\tcolor = LINEAR_REC2020_TO_LINEAR_SRGB * color;\n\tcolor = clamp( color, 0.0, 1.0 );\n\treturn color;\n}\nvec3 NeutralToneMapping( vec3 color ) {\n\tconst float StartCompression = 0.8 - 0.04;\n\tconst float Desaturation = 0.15;\n\tcolor *= toneMappingExposure;\n\tfloat x = min( color.r, min( color.g, color.b ) );\n\tfloat offset = x < 0.08 ? x - 6.25 * x * x : 0.04;\n\tcolor -= offset;\n\tfloat peak = max( color.r, max( color.g, color.b ) );\n\tif ( peak < StartCompression ) return color;\n\tfloat d = 1. - StartCompression;\n\tfloat newPeak = 1. - d * d / ( peak + d - StartCompression );\n\tcolor *= newPeak / peak;\n\tfloat g = 1. - 1. / ( Desaturation * ( peak - newPeak ) + 1. );\n\treturn mix( color, vec3( newPeak ), g );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; -var transmission_fragment = "#ifdef USE_TRANSMISSION\n\tmaterial.transmission = transmission;\n\tmaterial.transmissionAlpha = 1.0;\n\tmaterial.thickness = thickness;\n\tmaterial.attenuationDistance = attenuationDistance;\n\tmaterial.attenuationColor = attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tmaterial.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tmaterial.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g;\n\t#endif\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\tvec4 transmitted = getIBLVolumeRefraction(\n\t\tn, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, material.dispersion, material.ior, material.thickness,\n\t\tmaterial.attenuationColor, material.attenuationDistance );\n\tmaterial.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission );\n\ttotalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission );\n#endif"; +var transmission_fragment = "#ifdef USE_TRANSMISSION\n\tmaterial.transmission = transmission;\n\tmaterial.transmissionAlpha = 1.0;\n\tmaterial.thickness = thickness;\n\tmaterial.attenuationDistance = attenuationDistance;\n\tmaterial.attenuationColor = attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tmaterial.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tmaterial.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g;\n\t#endif\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\tvec4 transmitted = getIBLVolumeRefraction(\n\t\tn, v, material.roughness, material.diffuseContribution, material.specularColorBlended, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, material.dispersion, material.ior, material.thickness,\n\t\tmaterial.attenuationColor, material.attenuationDistance );\n\tmaterial.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission );\n\ttotalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission );\n#endif"; var transmission_pars_fragment = "#ifdef USE_TRANSMISSION\n\tuniform float transmission;\n\tuniform float thickness;\n\tuniform float attenuationDistance;\n\tuniform vec3 attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tuniform sampler2D transmissionMap;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tuniform sampler2D thicknessMap;\n\t#endif\n\tuniform vec2 transmissionSamplerSize;\n\tuniform sampler2D transmissionSamplerMap;\n\tuniform mat4 modelMatrix;\n\tuniform mat4 projectionMatrix;\n\tvarying vec3 vWorldPosition;\n\tfloat w0( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 );\n\t}\n\tfloat w1( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 );\n\t}\n\tfloat w2( float a ){\n\t\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 );\n\t}\n\tfloat w3( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * a * a );\n\t}\n\tfloat g0( float a ) {\n\t\treturn w0( a ) + w1( a );\n\t}\n\tfloat g1( float a ) {\n\t\treturn w2( a ) + w3( a );\n\t}\n\tfloat h0( float a ) {\n\t\treturn - 1.0 + w1( a ) / ( w0( a ) + w1( a ) );\n\t}\n\tfloat h1( float a ) {\n\t\treturn 1.0 + w3( a ) / ( w2( a ) + w3( a ) );\n\t}\n\tvec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) {\n\t\tuv = uv * texelSize.zw + 0.5;\n\t\tvec2 iuv = floor( uv );\n\t\tvec2 fuv = fract( uv );\n\t\tfloat g0x = g0( fuv.x );\n\t\tfloat g1x = g1( fuv.x );\n\t\tfloat h0x = h0( fuv.x );\n\t\tfloat h1x = h1( fuv.x );\n\t\tfloat h0y = h0( fuv.y );\n\t\tfloat h1y = h1( fuv.y );\n\t\tvec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\n\t\treturn g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) +\n\t\t\tg1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) );\n\t}\n\tvec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) {\n\t\tvec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) );\n\t\tvec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) );\n\t\tvec2 fLodSizeInv = 1.0 / fLodSize;\n\t\tvec2 cLodSizeInv = 1.0 / cLodSize;\n\t\tvec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) );\n\t\tvec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) );\n\t\treturn mix( fSample, cSample, fract( lod ) );\n\t}\n\tvec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) {\n\t\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\n\t\tvec3 modelScale;\n\t\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\n\t\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\n\t\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\n\t\treturn normalize( refractionVector ) * thickness * modelScale;\n\t}\n\tfloat applyIorToRoughness( const in float roughness, const in float ior ) {\n\t\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\n\t}\n\tvec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) {\n\t\tfloat lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\n\t\treturn textureBicubic( transmissionSamplerMap, fragCoord.xy, lod );\n\t}\n\tvec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tif ( isinf( attenuationDistance ) ) {\n\t\t\treturn vec3( 1.0 );\n\t\t} else {\n\t\t\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\n\t\t\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance );\t\t\treturn transmittance;\n\t\t}\n\t}\n\tvec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor,\n\t\tconst in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix,\n\t\tconst in mat4 viewMatrix, const in mat4 projMatrix, const in float dispersion, const in float ior, const in float thickness,\n\t\tconst in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tvec4 transmittedLight;\n\t\tvec3 transmittance;\n\t\t#ifdef USE_DISPERSION\n\t\t\tfloat halfSpread = ( ior - 1.0 ) * 0.025 * dispersion;\n\t\t\tvec3 iors = vec3( ior - halfSpread, ior, ior + halfSpread );\n\t\t\tfor ( int i = 0; i < 3; i ++ ) {\n\t\t\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, iors[ i ], modelMatrix );\n\t\t\t\tvec3 refractedRayExit = position + transmissionRay;\n\t\t\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\t\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\t\t\trefractionCoords += 1.0;\n\t\t\t\trefractionCoords /= 2.0;\n\t\t\t\tvec4 transmissionSample = getTransmissionSample( refractionCoords, roughness, iors[ i ] );\n\t\t\t\ttransmittedLight[ i ] = transmissionSample[ i ];\n\t\t\t\ttransmittedLight.a += transmissionSample.a;\n\t\t\t\ttransmittance[ i ] = diffuseColor[ i ] * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance )[ i ];\n\t\t\t}\n\t\t\ttransmittedLight.a /= 3.0;\n\t\t#else\n\t\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\n\t\t\tvec3 refractedRayExit = position + transmissionRay;\n\t\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\t\trefractionCoords += 1.0;\n\t\t\trefractionCoords /= 2.0;\n\t\t\ttransmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\n\t\t\ttransmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance );\n\t\t#endif\n\t\tvec3 attenuatedColor = transmittance * transmittedLight.rgb;\n\t\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\n\t\tfloat transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0;\n\t\treturn vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor );\n\t}\n#endif"; @@ -36028,7 +36411,7 @@ const fragment$e = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\ const vertex$d = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; -const fragment$d = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; +const fragment$d = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = vec4( dist, 0.0, 0.0, 1.0 );\n}"; const vertex$c = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; @@ -36044,7 +36427,7 @@ const fragment$a = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_ const vertex$9 = "#define LAMBERT\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; -const fragment$9 = "#define LAMBERT\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$9 = "#define LAMBERT\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; const vertex$8 = "#define MATCAP\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; @@ -36052,19 +36435,19 @@ const fragment$8 = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity const vertex$7 = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; -const fragment$7 = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), diffuseColor.a );\n\t#ifdef OPAQUE\n\t\tgl_FragColor.a = 1.0;\n\t#endif\n}"; +const fragment$7 = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( normalize( normal ) * 0.5 + 0.5, diffuseColor.a );\n\t#ifdef OPAQUE\n\t\tgl_FragColor.a = 1.0;\n\t#endif\n}"; const vertex$6 = "#define PHONG\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; -const fragment$6 = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$6 = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; const vertex$5 = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifdef USE_TRANSMISSION\n\tvarying vec3 vWorldPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n#ifdef USE_TRANSMISSION\n\tvWorldPosition = worldPosition.xyz;\n#endif\n}"; -const fragment$5 = "#define STANDARD\n#ifdef PHYSICAL\n\t#define IOR\n\t#define USE_SPECULAR\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef IOR\n\tuniform float ior;\n#endif\n#ifdef USE_SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularColor;\n\t#ifdef USE_SPECULAR_COLORMAP\n\t\tuniform sampler2D specularColorMap;\n\t#endif\n\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_DISPERSION\n\tuniform float dispersion;\n#endif\n#ifdef USE_IRIDESCENCE\n\tuniform float iridescence;\n\tuniform float iridescenceIOR;\n\tuniform float iridescenceThicknessMinimum;\n\tuniform float iridescenceThicknessMaximum;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheenColor;\n\tuniform float sheenRoughness;\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tuniform sampler2D sheenColorMap;\n\t#endif\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tuniform sampler2D sheenRoughnessMap;\n\t#endif\n#endif\n#ifdef USE_ANISOTROPY\n\tuniform vec2 anisotropyVector;\n\t#ifdef USE_ANISOTROPYMAP\n\t\tuniform sampler2D anisotropyMap;\n\t#endif\n#endif\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\t#include \n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\t#ifdef USE_SHEEN\n\t\tfloat sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );\n\t\toutgoingLight = outgoingLight * sheenEnergyComp + sheenSpecularDirect + sheenSpecularIndirect;\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) );\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\t\toutgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$5 = "#define STANDARD\n#ifdef PHYSICAL\n\t#define IOR\n\t#define USE_SPECULAR\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef IOR\n\tuniform float ior;\n#endif\n#ifdef USE_SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularColor;\n\t#ifdef USE_SPECULAR_COLORMAP\n\t\tuniform sampler2D specularColorMap;\n\t#endif\n\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_DISPERSION\n\tuniform float dispersion;\n#endif\n#ifdef USE_IRIDESCENCE\n\tuniform float iridescence;\n\tuniform float iridescenceIOR;\n\tuniform float iridescenceThicknessMinimum;\n\tuniform float iridescenceThicknessMaximum;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheenColor;\n\tuniform float sheenRoughness;\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tuniform sampler2D sheenColorMap;\n\t#endif\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tuniform sampler2D sheenRoughnessMap;\n\t#endif\n#endif\n#ifdef USE_ANISOTROPY\n\tuniform vec2 anisotropyVector;\n\t#ifdef USE_ANISOTROPYMAP\n\t\tuniform sampler2D anisotropyMap;\n\t#endif\n#endif\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\t#include \n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\t#ifdef USE_SHEEN\n \n\t\toutgoingLight = outgoingLight + sheenSpecularDirect + sheenSpecularIndirect;\n \n \t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) );\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\t\toutgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; const vertex$4 = "#define TOON\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; -const fragment$4 = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; +const fragment$4 = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; const vertex$3 = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \n#ifdef USE_POINTS_UV\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif\nvoid main() {\n\t#ifdef USE_POINTS_UV\n\t\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; @@ -36072,7 +36455,7 @@ const fragment$3 = "uniform vec3 diffuse;\nuniform float opacity;\n#include 0; // use PMREM if the user wants to blur the background - background = ( usePMREM ? cubeuvmaps : cubemaps ).get( background ); + background = environments.get( background, usePMREM ); } @@ -37103,7 +37486,7 @@ function WebGLBindingStates( gl, attributes ) { let updateBuffers = false; - const state = getBindingState( geometry, program, material ); + const state = getBindingState( object, geometry, program, material ); if ( currentState !== state ) { @@ -37156,16 +37539,28 @@ function WebGLBindingStates( gl, attributes ) { } - function getBindingState( geometry, program, material ) { + function getBindingState( object, geometry, program, material ) { const wireframe = ( material.wireframe === true ); - let programMap = bindingStates[ geometry.id ]; + let objectMap = bindingStates[ geometry.id ]; + + if ( objectMap === undefined ) { + + objectMap = {}; + bindingStates[ geometry.id ] = objectMap; + + } + + // Each InstancedMesh requires unique binding states because it contains instanced attributes. + const objectId = ( object.isInstancedMesh === true ) ? object.id : 0; + + let programMap = objectMap[ objectId ]; if ( programMap === undefined ) { programMap = {}; - bindingStates[ geometry.id ] = programMap; + objectMap[ objectId ] = programMap; } @@ -37566,21 +37961,27 @@ function WebGLBindingStates( gl, attributes ) { for ( const geometryId in bindingStates ) { - const programMap = bindingStates[ geometryId ]; + const objectMap = bindingStates[ geometryId ]; - for ( const programId in programMap ) { + for ( const objectId in objectMap ) { - const stateMap = programMap[ programId ]; + const programMap = objectMap[ objectId ]; - for ( const wireframe in stateMap ) { + for ( const programId in programMap ) { - deleteVertexArrayObject( stateMap[ wireframe ].object ); + const stateMap = programMap[ programId ]; - delete stateMap[ wireframe ]; + for ( const wireframe in stateMap ) { - } + deleteVertexArrayObject( stateMap[ wireframe ].object ); - delete programMap[ programId ]; + delete stateMap[ wireframe ]; + + } + + delete programMap[ programId ]; + + } } @@ -37594,21 +37995,27 @@ function WebGLBindingStates( gl, attributes ) { if ( bindingStates[ geometry.id ] === undefined ) return; - const programMap = bindingStates[ geometry.id ]; + const objectMap = bindingStates[ geometry.id ]; - for ( const programId in programMap ) { + for ( const objectId in objectMap ) { - const stateMap = programMap[ programId ]; + const programMap = objectMap[ objectId ]; - for ( const wireframe in stateMap ) { + for ( const programId in programMap ) { - deleteVertexArrayObject( stateMap[ wireframe ].object ); + const stateMap = programMap[ programId ]; - delete stateMap[ wireframe ]; + for ( const wireframe in stateMap ) { - } + deleteVertexArrayObject( stateMap[ wireframe ].object ); - delete programMap[ programId ]; + delete stateMap[ wireframe ]; + + } + + delete programMap[ programId ]; + + } } @@ -37620,26 +38027,73 @@ function WebGLBindingStates( gl, attributes ) { for ( const geometryId in bindingStates ) { - const programMap = bindingStates[ geometryId ]; + const objectMap = bindingStates[ geometryId ]; + + for ( const objectId in objectMap ) { - if ( programMap[ program.id ] === undefined ) continue; + const programMap = objectMap[ objectId ]; - const stateMap = programMap[ program.id ]; + if ( programMap[ program.id ] === undefined ) continue; - for ( const wireframe in stateMap ) { + const stateMap = programMap[ program.id ]; - deleteVertexArrayObject( stateMap[ wireframe ].object ); + for ( const wireframe in stateMap ) { + + deleteVertexArrayObject( stateMap[ wireframe ].object ); + + delete stateMap[ wireframe ]; + + } - delete stateMap[ wireframe ]; + delete programMap[ program.id ]; } - delete programMap[ program.id ]; + } + + } + + function releaseStatesOfObject( object ) { + + for ( const geometryId in bindingStates ) { + + const objectMap = bindingStates[ geometryId ]; + + const objectId = ( object.isInstancedMesh === true ) ? object.id : 0; + + const programMap = objectMap[ objectId ]; + + if ( programMap === undefined ) continue; + + for ( const programId in programMap ) { + + const stateMap = programMap[ programId ]; + + for ( const wireframe in stateMap ) { + + deleteVertexArrayObject( stateMap[ wireframe ].object ); + + delete stateMap[ wireframe ]; + + } + + delete programMap[ programId ]; + + } + + delete objectMap[ objectId ]; + + if ( Object.keys( objectMap ).length === 0 ) { + + delete bindingStates[ geometryId ]; + + } } } + function reset() { resetDefaultState(); @@ -37669,6 +38123,7 @@ function WebGLBindingStates( gl, attributes ) { resetDefaultState: resetDefaultState, dispose: dispose, releaseStatesOfGeometry: releaseStatesOfGeometry, + releaseStatesOfObject: releaseStatesOfObject, releaseStatesOfProgram: releaseStatesOfProgram, initAttributes: initAttributes, @@ -37852,7 +38307,7 @@ function WebGLCapabilities( gl, extensions, parameters, utils ) { if ( maxPrecision !== precision ) { - console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); + warn( 'WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); precision = maxPrecision; } @@ -37870,9 +38325,8 @@ function WebGLCapabilities( gl, extensions, parameters, utils ) { const maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); const maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); - const vertexTextures = maxVertexTextures > 0; - const maxSamples = gl.getParameter( gl.MAX_SAMPLES ); + const samples = gl.getParameter( gl.SAMPLES ); return { @@ -37898,9 +38352,9 @@ function WebGLCapabilities( gl, extensions, parameters, utils ) { maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, - vertexTextures: vertexTextures, + maxSamples: maxSamples, - maxSamples: maxSamples + samples: samples }; @@ -38072,6 +38526,10 @@ function WebGLClipping( properties ) { } +const _position$1 = /*@__PURE__*/ new Vector3(); +const _quaternion = /*@__PURE__*/ new Quaternion(); +const _scale = /*@__PURE__*/ new Vector3(); + /** * Abstract base class for cameras. This class should always be inherited * when you build a new camera. @@ -38177,7 +38635,19 @@ class Camera extends Object3D { super.updateMatrixWorld( force ); - this.matrixWorldInverse.copy( this.matrixWorld ).invert(); + // exclude scale from view matrix to be glTF conform + + this.matrixWorld.decompose( _position$1, _quaternion, _scale ); + + if ( _scale.x === 1 && _scale.y === 1 && _scale.z === 1 ) { + + this.matrixWorldInverse.copy( this.matrixWorld ).invert(); + + } else { + + this.matrixWorldInverse.compose( _position$1, _quaternion, _scale.set( 1, 1, 1 ) ).invert(); + + } } @@ -38185,7 +38655,19 @@ class Camera extends Object3D { super.updateWorldMatrix( updateParents, updateChildren ); - this.matrixWorldInverse.copy( this.matrixWorld ).invert(); + // exclude scale from view matrix to be glTF conform + + this.matrixWorld.decompose( _position$1, _quaternion, _scale ); + + if ( _scale.x === 1 && _scale.y === 1 && _scale.z === 1 ) { + + this.matrixWorldInverse.copy( this.matrixWorld ).invert(); + + } else { + + this.matrixWorldInverse.compose( _position$1, _quaternion, _scale.set( 1, 1, 1 ) ).invert(); + + } } @@ -38197,12 +38679,254 @@ class Camera extends Object3D { } +/** + * Camera that uses [orthographic projection](https://en.wikipedia.org/wiki/Orthographic_projection). + * + * In this projection mode, an object's size in the rendered image stays + * constant regardless of its distance from the camera. This can be useful + * for rendering 2D scenes and UI elements, amongst other things. + * + * ```js + * const camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 ); + * scene.add( camera ); + * ``` + * + * @augments Camera + */ +class OrthographicCamera extends Camera { + + /** + * Constructs a new orthographic camera. + * + * @param {number} [left=-1] - The left plane of the camera's frustum. + * @param {number} [right=1] - The right plane of the camera's frustum. + * @param {number} [top=1] - The top plane of the camera's frustum. + * @param {number} [bottom=-1] - The bottom plane of the camera's frustum. + * @param {number} [near=0.1] - The camera's near plane. + * @param {number} [far=2000] - The camera's far plane. + */ + constructor( left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000 ) { + + super(); + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isOrthographicCamera = true; + + this.type = 'OrthographicCamera'; + + /** + * The zoom factor of the camera. + * + * @type {number} + * @default 1 + */ + this.zoom = 1; + + /** + * Represents the frustum window specification. This property should not be edited + * directly but via {@link PerspectiveCamera#setViewOffset} and {@link PerspectiveCamera#clearViewOffset}. + * + * @type {?Object} + * @default null + */ + this.view = null; + + /** + * The left plane of the camera's frustum. + * + * @type {number} + * @default -1 + */ + this.left = left; + + /** + * The right plane of the camera's frustum. + * + * @type {number} + * @default 1 + */ + this.right = right; + + /** + * The top plane of the camera's frustum. + * + * @type {number} + * @default 1 + */ + this.top = top; + + /** + * The bottom plane of the camera's frustum. + * + * @type {number} + * @default -1 + */ + this.bottom = bottom; + + /** + * The camera's near plane. The valid range is greater than `0` + * and less than the current value of {@link OrthographicCamera#far}. + * + * Note that, unlike for the {@link PerspectiveCamera}, `0` is a + * valid value for an orthographic camera's near plane. + * + * @type {number} + * @default 0.1 + */ + this.near = near; + + /** + * The camera's far plane. Must be greater than the + * current value of {@link OrthographicCamera#near}. + * + * @type {number} + * @default 2000 + */ + this.far = far; + + this.updateProjectionMatrix(); + + } + + copy( source, recursive ) { + + super.copy( source, recursive ); + + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; + + this.zoom = source.zoom; + this.view = source.view === null ? null : Object.assign( {}, source.view ); + + return this; + + } + + /** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * @param {number} fullWidth - The full width of multiview setup. + * @param {number} fullHeight - The full height of multiview setup. + * @param {number} x - The horizontal offset of the subcamera. + * @param {number} y - The vertical offset of the subcamera. + * @param {number} width - The width of subcamera. + * @param {number} height - The height of subcamera. + * @see {@link PerspectiveCamera#setViewOffset} + */ + setViewOffset( fullWidth, fullHeight, x, y, width, height ) { + + if ( this.view === null ) { + + this.view = { + enabled: true, + fullWidth: 1, + fullHeight: 1, + offsetX: 0, + offsetY: 0, + width: 1, + height: 1 + }; + + } + + this.view.enabled = true; + this.view.fullWidth = fullWidth; + this.view.fullHeight = fullHeight; + this.view.offsetX = x; + this.view.offsetY = y; + this.view.width = width; + this.view.height = height; + + this.updateProjectionMatrix(); + + } + + /** + * Removes the view offset from the projection matrix. + */ + clearViewOffset() { + + if ( this.view !== null ) { + + this.view.enabled = false; + + } + + this.updateProjectionMatrix(); + + } + + /** + * Updates the camera's projection matrix. Must be called after any change of + * camera properties. + */ + updateProjectionMatrix() { + + const dx = ( this.right - this.left ) / ( 2 * this.zoom ); + const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + const cx = ( this.right + this.left ) / 2; + const cy = ( this.top + this.bottom ) / 2; + + let left = cx - dx; + let right = cx + dx; + let top = cy + dy; + let bottom = cy - dy; + + if ( this.view !== null && this.view.enabled ) { + + const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; + const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; + + left += scaleW * this.view.offsetX; + right = left + scaleW * this.view.width; + top -= scaleH * this.view.offsetY; + bottom = top - scaleH * this.view.height; + + } + + this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem, this.reversedDepth ); + + this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); + + } + + toJSON( meta ) { + + const data = super.toJSON( meta ); + + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; + + if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); + + return data; + + } + +} + const _v3 = /*@__PURE__*/ new Vector3(); const _minTarget = /*@__PURE__*/ new Vector2(); const _maxTarget = /*@__PURE__*/ new Vector2(); /** - * Camera that uses [perspective projection]{@link https://en.wikipedia.org/wiki/Perspective_(graphical)}. + * Camera that uses [perspective projection](https://en.wikipedia.org/wiki/Perspective_(graphical)). * * This projection mode is designed to mimic the way the human eye sees. It * is the most common projection mode used for rendering a 3D scene. @@ -38598,838 +39322,20 @@ class PerspectiveCamera extends Camera { } -const fov = -90; // negative fov is not an error -const aspect = 1; - -/** - * A special type of camera that is positioned in 3D space to render its surroundings into a - * cube render target. The render target can then be used as an environment map for rendering - * realtime reflections in your scene. - * - * ```js - * // Create cube render target - * const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 256, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter } ); - * - * // Create cube camera - * const cubeCamera = new THREE.CubeCamera( 1, 100000, cubeRenderTarget ); - * scene.add( cubeCamera ); - * - * // Create car - * const chromeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: cubeRenderTarget.texture } ); - * const car = new THREE.Mesh( carGeometry, chromeMaterial ); - * scene.add( car ); - * - * // Update the render target cube - * car.visible = false; - * cubeCamera.position.copy( car.position ); - * cubeCamera.update( renderer, scene ); - * - * // Render the scene - * car.visible = true; - * renderer.render( scene, camera ); - * ``` - * - * @augments Object3D - */ -class CubeCamera extends Object3D { - - /** - * Constructs a new cube camera. - * - * @param {number} near - The camera's near plane. - * @param {number} far - The camera's far plane. - * @param {WebGLCubeRenderTarget} renderTarget - The cube render target. - */ - constructor( near, far, renderTarget ) { - - super(); - - this.type = 'CubeCamera'; - - /** - * A reference to the cube render target. - * - * @type {WebGLCubeRenderTarget} - */ - this.renderTarget = renderTarget; - - /** - * The current active coordinate system. - * - * @type {?(WebGLCoordinateSystem|WebGPUCoordinateSystem)} - * @default null - */ - this.coordinateSystem = null; - - /** - * The current active mipmap level - * - * @type {number} - * @default 0 - */ - this.activeMipmapLevel = 0; - - const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); - cameraPX.layers = this.layers; - this.add( cameraPX ); - - const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); - cameraNX.layers = this.layers; - this.add( cameraNX ); - - const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); - cameraPY.layers = this.layers; - this.add( cameraPY ); - - const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); - cameraNY.layers = this.layers; - this.add( cameraNY ); - - const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.layers = this.layers; - this.add( cameraPZ ); - - const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.layers = this.layers; - this.add( cameraNZ ); - - } - - /** - * Must be called when the coordinate system of the cube camera is changed. - */ - updateCoordinateSystem() { - - const coordinateSystem = this.coordinateSystem; - - const cameras = this.children.concat(); - - const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras; - - for ( const camera of cameras ) this.remove( camera ); - - if ( coordinateSystem === WebGLCoordinateSystem ) { - - cameraPX.up.set( 0, 1, 0 ); - cameraPX.lookAt( 1, 0, 0 ); - - cameraNX.up.set( 0, 1, 0 ); - cameraNX.lookAt( -1, 0, 0 ); - - cameraPY.up.set( 0, 0, -1 ); - cameraPY.lookAt( 0, 1, 0 ); - - cameraNY.up.set( 0, 0, 1 ); - cameraNY.lookAt( 0, -1, 0 ); - - cameraPZ.up.set( 0, 1, 0 ); - cameraPZ.lookAt( 0, 0, 1 ); - - cameraNZ.up.set( 0, 1, 0 ); - cameraNZ.lookAt( 0, 0, -1 ); - - } else if ( coordinateSystem === WebGPUCoordinateSystem ) { - - cameraPX.up.set( 0, -1, 0 ); - cameraPX.lookAt( -1, 0, 0 ); - - cameraNX.up.set( 0, -1, 0 ); - cameraNX.lookAt( 1, 0, 0 ); - - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( 0, 1, 0 ); - - cameraNY.up.set( 0, 0, -1 ); - cameraNY.lookAt( 0, -1, 0 ); - - cameraPZ.up.set( 0, -1, 0 ); - cameraPZ.lookAt( 0, 0, 1 ); - - cameraNZ.up.set( 0, -1, 0 ); - cameraNZ.lookAt( 0, 0, -1 ); - - } else { - - throw new Error( 'THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem ); - - } - - for ( const camera of cameras ) { - - this.add( camera ); - - camera.updateMatrixWorld(); - - } - - } - - /** - * Calling this method will render the given scene with the given renderer - * into the cube render target of the camera. - * - * @param {(Renderer|WebGLRenderer)} renderer - The renderer. - * @param {Scene} scene - The scene to render. - */ - update( renderer, scene ) { - - if ( this.parent === null ) this.updateMatrixWorld(); - - const { renderTarget, activeMipmapLevel } = this; - - if ( this.coordinateSystem !== renderer.coordinateSystem ) { - - this.coordinateSystem = renderer.coordinateSystem; - - this.updateCoordinateSystem(); - - } - - const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; - - const currentRenderTarget = renderer.getRenderTarget(); - const currentActiveCubeFace = renderer.getActiveCubeFace(); - const currentActiveMipmapLevel = renderer.getActiveMipmapLevel(); - - const currentXrEnabled = renderer.xr.enabled; - - renderer.xr.enabled = false; - - const generateMipmaps = renderTarget.texture.generateMipmaps; - - renderTarget.texture.generateMipmaps = false; - - renderer.setRenderTarget( renderTarget, 0, activeMipmapLevel ); - renderer.render( scene, cameraPX ); - - renderer.setRenderTarget( renderTarget, 1, activeMipmapLevel ); - renderer.render( scene, cameraNX ); - - renderer.setRenderTarget( renderTarget, 2, activeMipmapLevel ); - renderer.render( scene, cameraPY ); - - renderer.setRenderTarget( renderTarget, 3, activeMipmapLevel ); - renderer.render( scene, cameraNY ); - - renderer.setRenderTarget( renderTarget, 4, activeMipmapLevel ); - renderer.render( scene, cameraPZ ); - - // mipmaps are generated during the last call of render() - // at this point, all sides of the cube render target are defined - - renderTarget.texture.generateMipmaps = generateMipmaps; - - renderer.setRenderTarget( renderTarget, 5, activeMipmapLevel ); - renderer.render( scene, cameraNZ ); - - renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel ); - - renderer.xr.enabled = currentXrEnabled; - - renderTarget.texture.needsPMREMUpdate = true; - - } - -} - -/** - * Creates a cube texture made up of six images. - * - * ```js - * const loader = new THREE.CubeTextureLoader(); - * loader.setPath( 'textures/cube/pisa/' ); - * - * const textureCube = loader.load( [ - * 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' - * ] ); - * - * const material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } ); - * ``` - * - * @augments Texture - */ -class CubeTexture extends Texture { - - /** - * Constructs a new cube texture. - * - * @param {Array} [images=[]] - An array holding a image for each side of a cube. - * @param {number} [mapping=CubeReflectionMapping] - The texture mapping. - * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. - * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. - * @param {number} [magFilter=LinearFilter] - The mag filter value. - * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value. - * @param {number} [format=RGBAFormat] - The texture format. - * @param {number} [type=UnsignedByteType] - The texture type. - * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. - * @param {string} [colorSpace=NoColorSpace] - The color space value. - */ - constructor( images = [], mapping = CubeReflectionMapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) { - - super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); - - /** - * This flag can be used for type testing. - * - * @type {boolean} - * @readonly - * @default true - */ - this.isCubeTexture = true; - - /** - * If set to `true`, the texture is flipped along the vertical axis when - * uploaded to the GPU. - * - * Overwritten and set to `false` by default. - * - * @type {boolean} - * @default false - */ - this.flipY = false; - - } - - /** - * Alias for {@link CubeTexture#image}. - * - * @type {Array} - */ - get images() { - - return this.image; - - } - - set images( value ) { - - this.image = value; - - } - -} - -/** - * A cube render target used in context of {@link WebGLRenderer}. - * - * @augments WebGLRenderTarget - */ -class WebGLCubeRenderTarget extends WebGLRenderTarget { - - /** - * Constructs a new cube render target. - * - * @param {number} [size=1] - The size of the render target. - * @param {RenderTarget~Options} [options] - The configuration object. - */ - constructor( size = 1, options = {} ) { - - super( size, size, options ); - - /** - * This flag can be used for type testing. - * - * @type {boolean} - * @readonly - * @default true - */ - this.isWebGLCubeRenderTarget = true; - - const image = { width: size, height: size, depth: 1 }; - const images = [ image, image, image, image, image, image ]; - - /** - * Overwritten with a different texture type. - * - * @type {DataArrayTexture} - */ - this.texture = new CubeTexture( images ); - this._setTextureOptions( options ); - - // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) - // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, - // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. - - // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped - // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture - // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). - - this.texture.isRenderTargetTexture = true; - - } - - /** - * Converts the given equirectangular texture to a cube map. - * - * @param {WebGLRenderer} renderer - The renderer. - * @param {Texture} texture - The equirectangular texture. - * @return {WebGLCubeRenderTarget} A reference to this cube render target. - */ - fromEquirectangularTexture( renderer, texture ) { - - this.texture.type = texture.type; - this.texture.colorSpace = texture.colorSpace; - - this.texture.generateMipmaps = texture.generateMipmaps; - this.texture.minFilter = texture.minFilter; - this.texture.magFilter = texture.magFilter; - - const shader = { - - uniforms: { - tEquirect: { value: null }, - }, - - vertexShader: /* glsl */` - - varying vec3 vWorldDirection; - - vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - - return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); - - } - - void main() { - - vWorldDirection = transformDirection( position, modelMatrix ); - - #include - #include - - } - `, - - fragmentShader: /* glsl */` - - uniform sampler2D tEquirect; - - varying vec3 vWorldDirection; - - #include - - void main() { - - vec3 direction = normalize( vWorldDirection ); - - vec2 sampleUV = equirectUv( direction ); - - gl_FragColor = texture2D( tEquirect, sampleUV ); - - } - ` - }; - - const geometry = new BoxGeometry( 5, 5, 5 ); - - const material = new ShaderMaterial( { - - name: 'CubemapFromEquirect', - - uniforms: cloneUniforms( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - side: BackSide, - blending: NoBlending - - } ); - - material.uniforms.tEquirect.value = texture; - - const mesh = new Mesh( geometry, material ); - - const currentMinFilter = texture.minFilter; - - // Avoid blurred poles - if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; - - const camera = new CubeCamera( 1, 10, this ); - camera.update( renderer, mesh ); - - texture.minFilter = currentMinFilter; - - mesh.geometry.dispose(); - mesh.material.dispose(); - - return this; - - } - - /** - * Clears this cube render target. - * - * @param {WebGLRenderer} renderer - The renderer. - * @param {boolean} [color=true] - Whether the color buffer should be cleared or not. - * @param {boolean} [depth=true] - Whether the depth buffer should be cleared or not. - * @param {boolean} [stencil=true] - Whether the stencil buffer should be cleared or not. - */ - clear( renderer, color = true, depth = true, stencil = true ) { - - const currentRenderTarget = renderer.getRenderTarget(); - - for ( let i = 0; i < 6; i ++ ) { - - renderer.setRenderTarget( this, i ); - - renderer.clear( color, depth, stencil ); - - } - - renderer.setRenderTarget( currentRenderTarget ); - - } - -} - -function WebGLCubeMaps( renderer ) { - - let cubemaps = new WeakMap(); - - function mapTextureMapping( texture, mapping ) { - - if ( mapping === EquirectangularReflectionMapping ) { - - texture.mapping = CubeReflectionMapping; - - } else if ( mapping === EquirectangularRefractionMapping ) { - - texture.mapping = CubeRefractionMapping; - - } - - return texture; - - } - - function get( texture ) { - - if ( texture && texture.isTexture ) { - - const mapping = texture.mapping; - - if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { - - if ( cubemaps.has( texture ) ) { - - const cubemap = cubemaps.get( texture ).texture; - return mapTextureMapping( cubemap, texture.mapping ); - - } else { - - const image = texture.image; - - if ( image && image.height > 0 ) { - - const renderTarget = new WebGLCubeRenderTarget( image.height ); - renderTarget.fromEquirectangularTexture( renderer, texture ); - cubemaps.set( texture, renderTarget ); - - texture.addEventListener( 'dispose', onTextureDispose ); - - return mapTextureMapping( renderTarget.texture, texture.mapping ); - - } else { - - // image not yet ready. try the conversion next frame - - return null; - - } - - } - - } - - } - - return texture; - - } - - function onTextureDispose( event ) { - - const texture = event.target; - - texture.removeEventListener( 'dispose', onTextureDispose ); - - const cubemap = cubemaps.get( texture ); - - if ( cubemap !== undefined ) { - - cubemaps.delete( texture ); - cubemap.dispose(); - - } - - } - - function dispose() { - - cubemaps = new WeakMap(); - - } - - return { - get: get, - dispose: dispose - }; - -} - -/** - * Camera that uses [orthographic projection]{@link https://en.wikipedia.org/wiki/Orthographic_projection}. - * - * In this projection mode, an object's size in the rendered image stays - * constant regardless of its distance from the camera. This can be useful - * for rendering 2D scenes and UI elements, amongst other things. - * - * ```js - * const camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 ); - * scene.add( camera ); - * ``` - * - * @augments Camera - */ -class OrthographicCamera extends Camera { - - /** - * Constructs a new orthographic camera. - * - * @param {number} [left=-1] - The left plane of the camera's frustum. - * @param {number} [right=1] - The right plane of the camera's frustum. - * @param {number} [top=1] - The top plane of the camera's frustum. - * @param {number} [bottom=-1] - The bottom plane of the camera's frustum. - * @param {number} [near=0.1] - The camera's near plane. - * @param {number} [far=2000] - The camera's far plane. - */ - constructor( left = -1, right = 1, top = 1, bottom = -1, near = 0.1, far = 2000 ) { - - super(); - - /** - * This flag can be used for type testing. - * - * @type {boolean} - * @readonly - * @default true - */ - this.isOrthographicCamera = true; - - this.type = 'OrthographicCamera'; - - /** - * The zoom factor of the camera. - * - * @type {number} - * @default 1 - */ - this.zoom = 1; - - /** - * Represents the frustum window specification. This property should not be edited - * directly but via {@link PerspectiveCamera#setViewOffset} and {@link PerspectiveCamera#clearViewOffset}. - * - * @type {?Object} - * @default null - */ - this.view = null; - - /** - * The left plane of the camera's frustum. - * - * @type {number} - * @default -1 - */ - this.left = left; - - /** - * The right plane of the camera's frustum. - * - * @type {number} - * @default 1 - */ - this.right = right; - - /** - * The top plane of the camera's frustum. - * - * @type {number} - * @default 1 - */ - this.top = top; - - /** - * The bottom plane of the camera's frustum. - * - * @type {number} - * @default -1 - */ - this.bottom = bottom; - - /** - * The camera's near plane. The valid range is greater than `0` - * and less than the current value of {@link OrthographicCamera#far}. - * - * Note that, unlike for the {@link PerspectiveCamera}, `0` is a - * valid value for an orthographic camera's near plane. - * - * @type {number} - * @default 0.1 - */ - this.near = near; - - /** - * The camera's far plane. Must be greater than the - * current value of {@link OrthographicCamera#near}. - * - * @type {number} - * @default 2000 - */ - this.far = far; - - this.updateProjectionMatrix(); - - } - - copy( source, recursive ) { - - super.copy( source, recursive ); - - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; - - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); - - return this; - - } - - /** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * @param {number} fullWidth - The full width of multiview setup. - * @param {number} fullHeight - The full height of multiview setup. - * @param {number} x - The horizontal offset of the subcamera. - * @param {number} y - The vertical offset of the subcamera. - * @param {number} width - The width of subcamera. - * @param {number} height - The height of subcamera. - * @see {@link PerspectiveCamera#setViewOffset} - */ - setViewOffset( fullWidth, fullHeight, x, y, width, height ) { - - if ( this.view === null ) { - - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; - - } - - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; - - this.updateProjectionMatrix(); - - } - - /** - * Removes the view offset from the projection matrix. - */ - clearViewOffset() { - - if ( this.view !== null ) { - - this.view.enabled = false; - - } - - this.updateProjectionMatrix(); - - } - - /** - * Updates the camera's projection matrix. Must be called after any change of - * camera properties. - */ - updateProjectionMatrix() { - - const dx = ( this.right - this.left ) / ( 2 * this.zoom ); - const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - const cx = ( this.right + this.left ) / 2; - const cy = ( this.top + this.bottom ) / 2; - - let left = cx - dx; - let right = cx + dx; - let top = cy + dy; - let bottom = cy - dy; - - if ( this.view !== null && this.view.enabled ) { - - const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; - const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; - - left += scaleW * this.view.offsetX; - right = left + scaleW * this.view.width; - top -= scaleH * this.view.offsetY; - bottom = top - scaleH * this.view.height; - - } - - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem, this.reversedDepth ); - - this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - - } - - toJSON( meta ) { - - const data = super.toJSON( meta ); - - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; - - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - - return data; - - } - -} - const LOD_MIN = 4; -// The standard deviations (radians) associated with the extra mips. These are -// chosen to approximate a Trowbridge-Reitz distribution function times the -// geometric shadowing function. These sigma values squared must match the -// variance #defines in cube_uv_reflection_fragment.glsl.js. +// The standard deviations (radians) associated with the extra mips. +// Used for scene blur in fromScene() method. const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; // The maximum length of the blur for loop. Smaller sigmas will use fewer // samples and exit early, but not recompile the shader. +// Used for scene blur in fromScene() method. const MAX_SAMPLES = 20; +// GGX VNDF importance sampling configuration +const GGX_SAMPLES = 256; + const _flatCamera = /*@__PURE__*/ new OrthographicCamera(); const _clearColor = /*@__PURE__*/ new Color(); let _oldTarget = null; @@ -39437,24 +39343,6 @@ let _oldActiveCubeFace = 0; let _oldActiveMipmapLevel = 0; let _oldXrEnabled = false; -// Golden Ratio -const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; -const INV_PHI = 1 / PHI; - -// Vertices of a dodecahedron (except the opposites, which represent the -// same axis), used as axis directions evenly spread on a sphere. -const _axisDirections = [ - /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ), - /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), - /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), - /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), - /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), - /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), - /*@__PURE__*/ new Vector3( -1, 1, -1 ), - /*@__PURE__*/ new Vector3( 1, 1, -1 ), - /*@__PURE__*/ new Vector3( -1, 1, 1 ), - /*@__PURE__*/ new Vector3( 1, 1, 1 ) ]; - const _origin = /*@__PURE__*/ new Vector3(); /** @@ -39468,9 +39356,11 @@ const _origin = /*@__PURE__*/ new Vector3(); * higher roughness levels. In this way we maintain resolution to smoothly * interpolate diffuse lighting while limiting sampling computation. * - * Paper: Fast, Accurate Image-Based Lighting: - * {@link https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view} -*/ + * The prefiltering uses GGX VNDF (Visible Normal Distribution Function) + * importance sampling based on "Sampling the GGX Distribution of Visible Normals" + * (Heitz, 2018) to generate environment maps that accurately match the GGX BRDF + * used in material rendering for physically-based image-based lighting. + */ class PMREMGenerator { /** @@ -39485,15 +39375,17 @@ class PMREMGenerator { this._lodMax = 0; this._cubeSize = 0; - this._lodPlanes = []; this._sizeLods = []; this._sigmas = []; + this._lodMeshes = []; + + this._backgroundBox = null; - this._blurMaterial = null; this._cubemapMaterial = null; this._equirectMaterial = null; - this._compileMaterial( this._blurMaterial ); + this._blurMaterial = null; + this._ggxMaterial = null; } @@ -39509,7 +39401,7 @@ class PMREMGenerator { * @param {number} [far=100] - The far plane distance. * @param {Object} [options={}] - The configuration options. * @param {number} [options.size=256] - The texture size of the PMREM. - * @param {Vector3} [options.renderTarget=origin] - The position of the internal cube camera that renders the scene. + * @param {Vector3} [options.position=origin] - The position of the internal cube camera that renders the scene. * @return {WebGLRenderTarget} The resulting PMREM. */ fromScene( scene, sigma = 0, near = 0.1, far = 100, options = {} ) { @@ -39618,6 +39510,13 @@ class PMREMGenerator { if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); + if ( this._backgroundBox !== null ) { + + this._backgroundBox.geometry.dispose(); + this._backgroundBox.material.dispose(); + + } + } // private interface @@ -39632,12 +39531,13 @@ class PMREMGenerator { _dispose() { if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); + if ( this._ggxMaterial !== null ) this._ggxMaterial.dispose(); if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); - for ( let i = 0; i < this._lodPlanes.length; i ++ ) { + for ( let i = 0; i < this._lodMeshes.length; i ++ ) { - this._lodPlanes[ i ].dispose(); + this._lodMeshes[ i ].geometry.dispose(); } @@ -39709,9 +39609,10 @@ class PMREMGenerator { this._pingPongRenderTarget = _createRenderTarget( width, height, params ); const { _lodMax } = this; - ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); + ( { lodMeshes: this._lodMeshes, sizeLods: this._sizeLods, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); this._blurMaterial = _getBlurShader( _lodMax, width, height ); + this._ggxMaterial = _getGGXShader( _lodMax, width, height ); } @@ -39721,8 +39622,8 @@ class PMREMGenerator { _compileMaterial( material ) { - const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material ); - this._renderer.compile( tmpMesh, _flatCamera ); + const mesh = new Mesh( new BufferGeometry(), material ); + this._renderer.compile( mesh, _flatCamera ); } @@ -39753,16 +39654,25 @@ class PMREMGenerator { } - const backgroundMaterial = new MeshBasicMaterial( { - name: 'PMREM.Background', - side: BackSide, - depthWrite: false, - depthTest: false, - } ); + if ( this._backgroundBox === null ) { + + this._backgroundBox = new Mesh( + new BoxGeometry(), + new MeshBasicMaterial( { + name: 'PMREM.Background', + side: BackSide, + depthWrite: false, + depthTest: false, + } ) + ); + + } - const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); + const backgroundBox = this._backgroundBox; + const backgroundMaterial = backgroundBox.material; let useSolidColor = false; + const background = scene.background; if ( background ) { @@ -39823,9 +39733,6 @@ class PMREMGenerator { } - backgroundBox.geometry.dispose(); - backgroundBox.material.dispose(); - renderer.toneMapping = toneMapping; renderer.autoClear = originalAutoClear; scene.background = background; @@ -39859,7 +39766,9 @@ class PMREMGenerator { } const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; - const mesh = new Mesh( this._lodPlanes[ 0 ], material ); + + const mesh = this._lodMeshes[ 0 ]; + mesh.material = material; const uniforms = material.uniforms; @@ -39879,15 +39788,13 @@ class PMREMGenerator { const renderer = this._renderer; const autoClear = renderer.autoClear; renderer.autoClear = false; - const n = this._lodPlanes.length; - - for ( let i = 1; i < n; i ++ ) { - const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); + const n = this._lodMeshes.length; - const poleAxis = _axisDirections[ ( n - i - 1 ) % _axisDirections.length ]; + // Use GGX VNDF importance sampling + for ( let i = 1; i < n; i ++ ) { - this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); + this._applyGGXFilter( cubeUVRenderTarget, i - 1, i ); } @@ -39895,6 +39802,63 @@ class PMREMGenerator { } + /** + * Applies GGX VNDF importance sampling filter to generate a prefiltered environment map. + * Uses Monte Carlo integration with VNDF importance sampling to accurately represent the + * GGX BRDF for physically-based rendering. Reads from the previous LOD level and + * applies incremental roughness filtering to avoid over-blurring. + * + * @private + * @param {WebGLRenderTarget} cubeUVRenderTarget + * @param {number} lodIn - Source LOD level to read from + * @param {number} lodOut - Target LOD level to write to + */ + _applyGGXFilter( cubeUVRenderTarget, lodIn, lodOut ) { + + const renderer = this._renderer; + const pingPongRenderTarget = this._pingPongRenderTarget; + + const ggxMaterial = this._ggxMaterial; + const ggxMesh = this._lodMeshes[ lodOut ]; + ggxMesh.material = ggxMaterial; + + const ggxUniforms = ggxMaterial.uniforms; + + // Calculate incremental roughness between LOD levels + const targetRoughness = lodOut / ( this._lodMeshes.length - 1 ); + const sourceRoughness = lodIn / ( this._lodMeshes.length - 1 ); + const incrementalRoughness = Math.sqrt( targetRoughness * targetRoughness - sourceRoughness * sourceRoughness ); + + // Apply blur strength mapping for better quality across the roughness range + const blurStrength = 0.0 + targetRoughness * 1.25; + const adjustedRoughness = incrementalRoughness * blurStrength; + + // Calculate viewport position based on output LOD level + const { _lodMax } = this; + const outputSize = this._sizeLods[ lodOut ]; + const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 ); + const y = 4 * ( this._cubeSize - outputSize ); + + // Read from previous LOD with incremental roughness + ggxUniforms[ 'envMap' ].value = cubeUVRenderTarget.texture; + ggxUniforms[ 'roughness' ].value = adjustedRoughness; + ggxUniforms[ 'mipInt' ].value = _lodMax - lodIn; // Sample from input LOD + + _setViewport( pingPongRenderTarget, x, y, 3 * outputSize, 2 * outputSize ); + renderer.setRenderTarget( pingPongRenderTarget ); + renderer.render( ggxMesh, _flatCamera ); + + // Copy from pingPong back to cubeUV (simple direct copy) + ggxUniforms[ 'envMap' ].value = pingPongRenderTarget.texture; + ggxUniforms[ 'roughness' ].value = 0.0; // Direct copy + ggxUniforms[ 'mipInt' ].value = _lodMax - lodOut; // Read from the level we just wrote + + _setViewport( cubeUVRenderTarget, x, y, 3 * outputSize, 2 * outputSize ); + renderer.setRenderTarget( cubeUVRenderTarget ); + renderer.render( ggxMesh, _flatCamera ); + + } + /** * This is a two-pass Gaussian blur for a cubemap. Normally this is done * vertically and horizontally, but this breaks down on a cube. Here we apply @@ -39902,6 +39866,8 @@ class PMREMGenerator { * the poles) to approximate the orthogonally-separable blur. It is least * accurate at the poles, but still does a decent job. * + * Used for initial scene blur in fromScene() method when sigma > 0. + * * @private * @param {WebGLRenderTarget} cubeUVRenderTarget * @param {number} lodIn @@ -39940,7 +39906,7 @@ class PMREMGenerator { if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) { - console.error( + error( 'blur direction must be either latitudinal or longitudinal!' ); } @@ -39948,7 +39914,9 @@ class PMREMGenerator { // Number of standard deviations at which to cut off the discrete approximation. const STANDARD_DEVIATIONS = 3; - const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial ); + const blurMesh = this._lodMeshes[ lodOut ]; + blurMesh.material = blurMaterial; + const blurUniforms = blurMaterial.uniforms; const pixels = this._sizeLods[ lodIn ] - 1; @@ -39958,7 +39926,7 @@ class PMREMGenerator { if ( samples > MAX_SAMPLES ) { - console.warn( `sigmaRadians, ${ + warn( `sigmaRadians, ${ sigmaRadians}, is too large and will clip, as it requested ${ samples} samples when the maximum is set to ${MAX_SAMPLES}` ); @@ -40022,9 +39990,9 @@ class PMREMGenerator { function _createPlanes( lodMax ) { - const lodPlanes = []; const sizeLods = []; const sigmas = []; + const lodMeshes = []; let lod = lodMax; @@ -40086,7 +40054,7 @@ function _createPlanes( lodMax ) { planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); - lodPlanes.push( planes ); + lodMeshes.push( new Mesh( planes, null ) ); if ( lod > LOD_MIN ) { @@ -40096,7 +40064,7 @@ function _createPlanes( lodMax ) { } - return { lodPlanes, sizeLods, sigmas }; + return { lodMeshes, sizeLods, sigmas }; } @@ -40117,6 +40085,143 @@ function _setViewport( target, x, y, width, height ) { } +function _getGGXShader( lodMax, width, height ) { + + const shaderMaterial = new ShaderMaterial( { + + name: 'PMREMGGXConvolution', + + defines: { + 'GGX_SAMPLES': GGX_SAMPLES, + 'CUBEUV_TEXEL_WIDTH': 1.0 / width, + 'CUBEUV_TEXEL_HEIGHT': 1.0 / height, + 'CUBEUV_MAX_MIP': `${lodMax}.0`, + }, + + uniforms: { + 'envMap': { value: null }, + 'roughness': { value: 0.0 }, + 'mipInt': { value: 0 } + }, + + vertexShader: _getCommonVertexShader(), + + fragmentShader: /* glsl */` + + precision highp float; + precision highp int; + + varying vec3 vOutputDirection; + + uniform sampler2D envMap; + uniform float roughness; + uniform float mipInt; + + #define ENVMAP_TYPE_CUBE_UV + #include + + #define PI 3.14159265359 + + // Van der Corput radical inverse + float radicalInverse_VdC(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 + } + + // Hammersley sequence + vec2 hammersley(uint i, uint N) { + return vec2(float(i) / float(N), radicalInverse_VdC(i)); + } + + // GGX VNDF importance sampling (Eric Heitz 2018) + // "Sampling the GGX Distribution of Visible Normals" + // https://jcgt.org/published/0007/04/01/ + vec3 importanceSampleGGX_VNDF(vec2 Xi, vec3 V, float roughness) { + float alpha = roughness * roughness; + + // Section 4.1: Orthonormal basis + vec3 T1 = vec3(1.0, 0.0, 0.0); + vec3 T2 = cross(V, T1); + + // Section 4.2: Parameterization of projected area + float r = sqrt(Xi.x); + float phi = 2.0 * PI * Xi.y; + float t1 = r * cos(phi); + float t2 = r * sin(phi); + float s = 0.5 * (1.0 + V.z); + t2 = (1.0 - s) * sqrt(1.0 - t1 * t1) + s * t2; + + // Section 4.3: Reprojection onto hemisphere + vec3 Nh = t1 * T1 + t2 * T2 + sqrt(max(0.0, 1.0 - t1 * t1 - t2 * t2)) * V; + + // Section 3.4: Transform back to ellipsoid configuration + return normalize(vec3(alpha * Nh.x, alpha * Nh.y, max(0.0, Nh.z))); + } + + void main() { + vec3 N = normalize(vOutputDirection); + vec3 V = N; // Assume view direction equals normal for pre-filtering + + vec3 prefilteredColor = vec3(0.0); + float totalWeight = 0.0; + + // For very low roughness, just sample the environment directly + if (roughness < 0.001) { + gl_FragColor = vec4(bilinearCubeUV(envMap, N, mipInt), 1.0); + return; + } + + // Tangent space basis for VNDF sampling + vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 tangent = normalize(cross(up, N)); + vec3 bitangent = cross(N, tangent); + + for(uint i = 0u; i < uint(GGX_SAMPLES); i++) { + vec2 Xi = hammersley(i, uint(GGX_SAMPLES)); + + // For PMREM, V = N, so in tangent space V is always (0, 0, 1) + vec3 H_tangent = importanceSampleGGX_VNDF(Xi, vec3(0.0, 0.0, 1.0), roughness); + + // Transform H back to world space + vec3 H = normalize(tangent * H_tangent.x + bitangent * H_tangent.y + N * H_tangent.z); + vec3 L = normalize(2.0 * dot(V, H) * H - V); + + float NdotL = max(dot(N, L), 0.0); + + if(NdotL > 0.0) { + // Sample environment at fixed mip level + // VNDF importance sampling handles the distribution filtering + vec3 sampleColor = bilinearCubeUV(envMap, L, mipInt); + + // Weight by NdotL for the split-sum approximation + // VNDF PDF naturally accounts for the visible microfacet distribution + prefilteredColor += sampleColor * NdotL; + totalWeight += NdotL; + } + } + + if (totalWeight > 0.0) { + prefilteredColor = prefilteredColor / totalWeight; + } + + gl_FragColor = vec4(prefilteredColor, 1.0); + } + `, + + blending: NoBlending, + depthTest: false, + depthWrite: false + + } ); + + return shaderMaterial; + +} + function _getBlurShader( lodMax, width, height ) { const weights = new Float32Array( MAX_SAMPLES ); @@ -40357,13 +40462,576 @@ function _getCommonVertexShader() { } -function WebGLCubeUVMaps( renderer ) { +const fov = -90; // negative fov is not an error +const aspect = 1; + +/** + * A special type of camera that is positioned in 3D space to render its surroundings into a + * cube render target. The render target can then be used as an environment map for rendering + * realtime reflections in your scene. + * + * ```js + * // Create cube render target + * const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 256, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter } ); + * + * // Create cube camera + * const cubeCamera = new THREE.CubeCamera( 1, 100000, cubeRenderTarget ); + * scene.add( cubeCamera ); + * + * // Create car + * const chromeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: cubeRenderTarget.texture } ); + * const car = new THREE.Mesh( carGeometry, chromeMaterial ); + * scene.add( car ); + * + * // Update the render target cube + * car.visible = false; + * cubeCamera.position.copy( car.position ); + * cubeCamera.update( renderer, scene ); + * + * // Render the scene + * car.visible = true; + * renderer.render( scene, camera ); + * ``` + * + * @augments Object3D + */ +class CubeCamera extends Object3D { + + /** + * Constructs a new cube camera. + * + * @param {number} near - The camera's near plane. + * @param {number} far - The camera's far plane. + * @param {WebGLCubeRenderTarget} renderTarget - The cube render target. + */ + constructor( near, far, renderTarget ) { + + super(); + + this.type = 'CubeCamera'; + + /** + * A reference to the cube render target. + * + * @type {WebGLCubeRenderTarget} + */ + this.renderTarget = renderTarget; + + /** + * The current active coordinate system. + * + * @type {?(WebGLCoordinateSystem|WebGPUCoordinateSystem)} + * @default null + */ + this.coordinateSystem = null; + + /** + * The current active mipmap level + * + * @type {number} + * @default 0 + */ + this.activeMipmapLevel = 0; + + const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); + cameraPX.layers = this.layers; + this.add( cameraPX ); + + const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); + cameraNX.layers = this.layers; + this.add( cameraNX ); + + const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); + cameraPY.layers = this.layers; + this.add( cameraPY ); + + const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); + cameraNY.layers = this.layers; + this.add( cameraNY ); + + const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.layers = this.layers; + this.add( cameraPZ ); + + const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.layers = this.layers; + this.add( cameraNZ ); + + } + + /** + * Must be called when the coordinate system of the cube camera is changed. + */ + updateCoordinateSystem() { + + const coordinateSystem = this.coordinateSystem; + + const cameras = this.children.concat(); + + const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras; + + for ( const camera of cameras ) this.remove( camera ); + + if ( coordinateSystem === WebGLCoordinateSystem ) { + + cameraPX.up.set( 0, 1, 0 ); + cameraPX.lookAt( 1, 0, 0 ); + + cameraNX.up.set( 0, 1, 0 ); + cameraNX.lookAt( -1, 0, 0 ); + + cameraPY.up.set( 0, 0, -1 ); + cameraPY.lookAt( 0, 1, 0 ); + + cameraNY.up.set( 0, 0, 1 ); + cameraNY.lookAt( 0, -1, 0 ); + + cameraPZ.up.set( 0, 1, 0 ); + cameraPZ.lookAt( 0, 0, 1 ); + + cameraNZ.up.set( 0, 1, 0 ); + cameraNZ.lookAt( 0, 0, -1 ); + + } else if ( coordinateSystem === WebGPUCoordinateSystem ) { + + cameraPX.up.set( 0, -1, 0 ); + cameraPX.lookAt( -1, 0, 0 ); + + cameraNX.up.set( 0, -1, 0 ); + cameraNX.lookAt( 1, 0, 0 ); + + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( 0, 1, 0 ); + + cameraNY.up.set( 0, 0, -1 ); + cameraNY.lookAt( 0, -1, 0 ); + + cameraPZ.up.set( 0, -1, 0 ); + cameraPZ.lookAt( 0, 0, 1 ); + + cameraNZ.up.set( 0, -1, 0 ); + cameraNZ.lookAt( 0, 0, -1 ); + + } else { + + throw new Error( 'THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem ); + + } + + for ( const camera of cameras ) { + + this.add( camera ); + + camera.updateMatrixWorld(); + + } + + } + + /** + * Calling this method will render the given scene with the given renderer + * into the cube render target of the camera. + * + * @param {(Renderer|WebGLRenderer)} renderer - The renderer. + * @param {Scene} scene - The scene to render. + */ + update( renderer, scene ) { + + if ( this.parent === null ) this.updateMatrixWorld(); + + const { renderTarget, activeMipmapLevel } = this; + + if ( this.coordinateSystem !== renderer.coordinateSystem ) { + + this.coordinateSystem = renderer.coordinateSystem; + + this.updateCoordinateSystem(); + + } + + const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; + + const currentRenderTarget = renderer.getRenderTarget(); + const currentActiveCubeFace = renderer.getActiveCubeFace(); + const currentActiveMipmapLevel = renderer.getActiveMipmapLevel(); + + const currentXrEnabled = renderer.xr.enabled; + + renderer.xr.enabled = false; + + const generateMipmaps = renderTarget.texture.generateMipmaps; + + renderTarget.texture.generateMipmaps = false; + + // https://github.com/mrdoob/three.js/issues/31413#issuecomment-3095966812 + + let reversedDepthBuffer = false; + + if ( renderer.isWebGLRenderer === true ) { + + reversedDepthBuffer = renderer.state.buffers.depth.getReversed(); + + } else { + + reversedDepthBuffer = renderer.reversedDepthBuffer; + + } + + renderer.setRenderTarget( renderTarget, 0, activeMipmapLevel ); + if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); + renderer.render( scene, cameraPX ); + + renderer.setRenderTarget( renderTarget, 1, activeMipmapLevel ); + if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); + renderer.render( scene, cameraNX ); + + renderer.setRenderTarget( renderTarget, 2, activeMipmapLevel ); + if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); + renderer.render( scene, cameraPY ); + + renderer.setRenderTarget( renderTarget, 3, activeMipmapLevel ); + if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); + renderer.render( scene, cameraNY ); + + renderer.setRenderTarget( renderTarget, 4, activeMipmapLevel ); + if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); + renderer.render( scene, cameraPZ ); + + // mipmaps are generated during the last call of render() + // at this point, all sides of the cube render target are defined + + renderTarget.texture.generateMipmaps = generateMipmaps; + + renderer.setRenderTarget( renderTarget, 5, activeMipmapLevel ); + if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth(); + renderer.render( scene, cameraNZ ); + + renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel ); + + renderer.xr.enabled = currentXrEnabled; + + renderTarget.texture.needsPMREMUpdate = true; + + } + +} + +/** + * Creates a cube texture made up of six images. + * + * ```js + * const loader = new THREE.CubeTextureLoader(); + * loader.setPath( 'textures/cube/pisa/' ); + * + * const textureCube = loader.load( [ + * 'px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png' + * ] ); + * + * const material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } ); + * ``` + * + * @augments Texture + */ +class CubeTexture extends Texture { + + /** + * Constructs a new cube texture. + * + * @param {Array} [images=[]] - An array holding a image for each side of a cube. + * @param {number} [mapping=CubeReflectionMapping] - The texture mapping. + * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. + * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. + * @param {number} [magFilter=LinearFilter] - The mag filter value. + * @param {number} [minFilter=LinearMipmapLinearFilter] - The min filter value. + * @param {number} [format=RGBAFormat] - The texture format. + * @param {number} [type=UnsignedByteType] - The texture type. + * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. + * @param {string} [colorSpace=NoColorSpace] - The color space value. + */ + constructor( images = [], mapping = CubeReflectionMapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) { + + super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isCubeTexture = true; + + /** + * If set to `true`, the texture is flipped along the vertical axis when + * uploaded to the GPU. + * + * Overwritten and set to `false` by default. + * + * @type {boolean} + * @default false + */ + this.flipY = false; + + } + + /** + * Alias for {@link CubeTexture#image}. + * + * @type {Array} + */ + get images() { + + return this.image; + + } + + set images( value ) { + + this.image = value; + + } + +} + +/** + * A cube render target used in context of {@link WebGLRenderer}. + * + * @augments WebGLRenderTarget + */ +class WebGLCubeRenderTarget extends WebGLRenderTarget { + + /** + * Constructs a new cube render target. + * + * @param {number} [size=1] - The size of the render target. + * @param {RenderTarget~Options} [options] - The configuration object. + */ + constructor( size = 1, options = {} ) { + + super( size, size, options ); + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isWebGLCubeRenderTarget = true; + + const image = { width: size, height: size, depth: 1 }; + const images = [ image, image, image, image, image, image ]; + + /** + * Overwritten with a different texture type. + * + * @type {DataArrayTexture} + */ + this.texture = new CubeTexture( images ); + this._setTextureOptions( options ); + + // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) + // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, + // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. + + // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped + // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture + // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). + + this.texture.isRenderTargetTexture = true; + + } + + /** + * Converts the given equirectangular texture to a cube map. + * + * @param {WebGLRenderer} renderer - The renderer. + * @param {Texture} texture - The equirectangular texture. + * @return {WebGLCubeRenderTarget} A reference to this cube render target. + */ + fromEquirectangularTexture( renderer, texture ) { + + this.texture.type = texture.type; + this.texture.colorSpace = texture.colorSpace; + + this.texture.generateMipmaps = texture.generateMipmaps; + this.texture.minFilter = texture.minFilter; + this.texture.magFilter = texture.magFilter; + + const shader = { + + uniforms: { + tEquirect: { value: null }, + }, + + vertexShader: /* glsl */` + + varying vec3 vWorldDirection; + + vec3 transformDirection( in vec3 dir, in mat4 matrix ) { + + return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); + + } + + void main() { + + vWorldDirection = transformDirection( position, modelMatrix ); + + #include + #include + + } + `, + + fragmentShader: /* glsl */` + + uniform sampler2D tEquirect; + + varying vec3 vWorldDirection; + + #include + + void main() { + + vec3 direction = normalize( vWorldDirection ); + + vec2 sampleUV = equirectUv( direction ); + + gl_FragColor = texture2D( tEquirect, sampleUV ); + + } + ` + }; + + const geometry = new BoxGeometry( 5, 5, 5 ); + + const material = new ShaderMaterial( { + + name: 'CubemapFromEquirect', + + uniforms: cloneUniforms( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader, + side: BackSide, + blending: NoBlending + + } ); + + material.uniforms.tEquirect.value = texture; + + const mesh = new Mesh( geometry, material ); + + const currentMinFilter = texture.minFilter; + + // Avoid blurred poles + if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; + + const camera = new CubeCamera( 1, 10, this ); + camera.update( renderer, mesh ); + + texture.minFilter = currentMinFilter; + + mesh.geometry.dispose(); + mesh.material.dispose(); + + return this; + + } + + /** + * Clears this cube render target. + * + * @param {WebGLRenderer} renderer - The renderer. + * @param {boolean} [color=true] - Whether the color buffer should be cleared or not. + * @param {boolean} [depth=true] - Whether the depth buffer should be cleared or not. + * @param {boolean} [stencil=true] - Whether the stencil buffer should be cleared or not. + */ + clear( renderer, color = true, depth = true, stencil = true ) { + + const currentRenderTarget = renderer.getRenderTarget(); + + for ( let i = 0; i < 6; i ++ ) { + + renderer.setRenderTarget( this, i ); + + renderer.clear( color, depth, stencil ); + + } + + renderer.setRenderTarget( currentRenderTarget ); + + } + +} + +function WebGLEnvironments( renderer ) { - let cubeUVmaps = new WeakMap(); + let cubeMaps = new WeakMap(); + let pmremMaps = new WeakMap(); let pmremGenerator = null; - function get( texture ) { + function get( texture, usePMREM = false ) { + + if ( texture === null || texture === undefined ) return null; + + if ( usePMREM ) { + + return getPMREM( texture ); + + } + + return getCube( texture ); + + } + + function getCube( texture ) { + + if ( texture && texture.isTexture ) { + + const mapping = texture.mapping; + + if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { + + if ( cubeMaps.has( texture ) ) { + + const cubemap = cubeMaps.get( texture ).texture; + return mapTextureMapping( cubemap, texture.mapping ); + + } else { + + const image = texture.image; + + if ( image && image.height > 0 ) { + + const renderTarget = new WebGLCubeRenderTarget( image.height ); + renderTarget.fromEquirectangularTexture( renderer, texture ); + cubeMaps.set( texture, renderTarget ); + + texture.addEventListener( 'dispose', onCubemapDispose ); + + return mapTextureMapping( renderTarget.texture, texture.mapping ); + + } else { + + // image not yet ready. try the conversion next frame + + return null; + + } + + } + + } + + } + + return texture; + + } + + function getPMREM( texture ) { if ( texture && texture.isTexture ) { @@ -40376,7 +41044,7 @@ function WebGLCubeUVMaps( renderer ) { if ( isEquirectMap || isCubeMap ) { - let renderTarget = cubeUVmaps.get( texture ); + let renderTarget = pmremMaps.get( texture ); const currentPMREMVersion = renderTarget !== undefined ? renderTarget.texture.pmremVersion : 0; @@ -40387,7 +41055,7 @@ function WebGLCubeUVMaps( renderer ) { renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget ); renderTarget.texture.pmremVersion = texture.pmremVersion; - cubeUVmaps.set( texture, renderTarget ); + pmremMaps.set( texture, renderTarget ); return renderTarget.texture; @@ -40408,9 +41076,9 @@ function WebGLCubeUVMaps( renderer ) { renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); renderTarget.texture.pmremVersion = texture.pmremVersion; - cubeUVmaps.set( texture, renderTarget ); + pmremMaps.set( texture, renderTarget ); - texture.addEventListener( 'dispose', onTextureDispose ); + texture.addEventListener( 'dispose', onPMREMDispose ); return renderTarget.texture; @@ -40434,6 +41102,22 @@ function WebGLCubeUVMaps( renderer ) { } + function mapTextureMapping( texture, mapping ) { + + if ( mapping === EquirectangularReflectionMapping ) { + + texture.mapping = CubeReflectionMapping; + + } else if ( mapping === EquirectangularRefractionMapping ) { + + texture.mapping = CubeRefractionMapping; + + } + + return texture; + + } + function isCubeTextureComplete( image ) { let count = 0; @@ -40447,21 +41131,37 @@ function WebGLCubeUVMaps( renderer ) { return count === length; + } + + function onCubemapDispose( event ) { + + const texture = event.target; + + texture.removeEventListener( 'dispose', onCubemapDispose ); + + const cubemap = cubeMaps.get( texture ); + + if ( cubemap !== undefined ) { + + cubeMaps.delete( texture ); + cubemap.dispose(); + + } } - function onTextureDispose( event ) { + function onPMREMDispose( event ) { const texture = event.target; - texture.removeEventListener( 'dispose', onTextureDispose ); + texture.removeEventListener( 'dispose', onPMREMDispose ); - const cubemapUV = cubeUVmaps.get( texture ); + const pmrem = pmremMaps.get( texture ); - if ( cubemapUV !== undefined ) { + if ( pmrem !== undefined ) { - cubeUVmaps.delete( texture ); - cubemapUV.dispose(); + pmremMaps.delete( texture ); + pmrem.dispose(); } @@ -40469,7 +41169,8 @@ function WebGLCubeUVMaps( renderer ) { function dispose() { - cubeUVmaps = new WeakMap(); + cubeMaps = new WeakMap(); + pmremMaps = new WeakMap(); if ( pmremGenerator !== null ) { @@ -40499,30 +41200,7 @@ function WebGLExtensions( gl ) { } - let extension; - - switch ( name ) { - - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; - - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; - - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; - - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; - - default: - extension = gl.getExtension( name ); - - } + const extension = gl.getExtension( name ); extensions[ name ] = extension; @@ -40555,7 +41233,7 @@ function WebGLExtensions( gl ) { if ( extension === null ) { - warnOnce( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + warnOnce( 'WebGLRenderer: ' + name + ' extension not supported.' ); } @@ -40651,6 +41329,12 @@ function WebGLGeometries( gl, attributes, info, bindingStates ) { const geometryPosition = geometry.attributes.position; let version = 0; + if ( geometryPosition === undefined ) { + + return; + + } + if ( geometryIndex !== null ) { const array = geometryIndex.array; @@ -40666,7 +41350,7 @@ function WebGLGeometries( gl, attributes, info, bindingStates ) { } - } else if ( geometryPosition !== undefined ) { + } else { const array = geometryPosition.array; version = geometryPosition.version; @@ -40681,13 +41365,11 @@ function WebGLGeometries( gl, attributes, info, bindingStates ) { } - } else { - - return; - } - const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); + // check whether a 32 bit or 16 bit buffer is required to store the indices + // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 + const attribute = new ( geometryPosition.count >= 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); attribute.version = version; // Updating index buffer in VAO now. See WebGLBindingStates @@ -40885,7 +41567,7 @@ function WebGLInfo( gl ) { break; default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); + error( 'WebGLInfo: Unknown draw mode:', mode ); break; } @@ -41203,7 +41885,7 @@ function WebGLMorphtargets( gl, capabilities, textures ) { } -function WebGLObjects( gl, geometries, attributes, info ) { +function WebGLObjects( gl, geometries, attributes, bindingStates, info ) { let updateMap = new WeakMap(); @@ -41278,6 +41960,8 @@ function WebGLObjects( gl, geometries, attributes, info ) { instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); + bindingStates.releaseStatesOfObject( instancedMesh ); + attributes.remove( instancedMesh.instanceMatrix ); if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); @@ -41293,6 +41977,291 @@ function WebGLObjects( gl, geometries, attributes, info ) { } +/** + * This class works just like {@link ShaderMaterial}, except that definitions + * of built-in uniforms and attributes are not automatically prepended to the + * GLSL shader code. + * + * `RawShaderMaterial` can only be used with {@link WebGLRenderer}. + * + * @augments ShaderMaterial + */ +class RawShaderMaterial extends ShaderMaterial { + + /** + * Constructs a new raw shader material. + * + * @param {Object} [parameters] - An object with one or more properties + * defining the material's appearance. Any property of the material + * (including any property from inherited materials) can be passed + * in here. Color values can be passed any type of value accepted + * by {@link Color#set}. + */ + constructor( parameters ) { + + super( parameters ); + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isRawShaderMaterial = true; + + this.type = 'RawShaderMaterial'; + + } + +} + +const toneMappingMap = { + [ LinearToneMapping ]: 'LINEAR_TONE_MAPPING', + [ ReinhardToneMapping ]: 'REINHARD_TONE_MAPPING', + [ CineonToneMapping ]: 'CINEON_TONE_MAPPING', + [ ACESFilmicToneMapping ]: 'ACES_FILMIC_TONE_MAPPING', + [ AgXToneMapping ]: 'AGX_TONE_MAPPING', + [ NeutralToneMapping ]: 'NEUTRAL_TONE_MAPPING', + [ CustomToneMapping ]: 'CUSTOM_TONE_MAPPING' +}; + +function WebGLOutput( type, width, height, depth, stencil ) { + + // render targets for scene and post-processing + const targetA = new WebGLRenderTarget( width, height, { + type: type, + depthBuffer: depth, + stencilBuffer: stencil + } ); + + const targetB = new WebGLRenderTarget( width, height, { + type: HalfFloatType, + depthBuffer: false, + stencilBuffer: false + } ); + + // create fullscreen triangle geometry + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( [ -1, 3, 0, -1, -1, 0, 3, -1, 0 ], 3 ) ); + geometry.setAttribute( 'uv', new Float32BufferAttribute( [ 0, 2, 0, 0, 2, 0 ], 2 ) ); + + // create output material with tone mapping support + const material = new RawShaderMaterial( { + uniforms: { + tDiffuse: { value: null } + }, + vertexShader: /* glsl */` + precision highp float; + + uniform mat4 modelViewMatrix; + uniform mat4 projectionMatrix; + + attribute vec3 position; + attribute vec2 uv; + + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`, + fragmentShader: /* glsl */` + precision highp float; + + uniform sampler2D tDiffuse; + + varying vec2 vUv; + + #include + #include + + void main() { + gl_FragColor = texture2D( tDiffuse, vUv ); + + #ifdef LINEAR_TONE_MAPPING + gl_FragColor.rgb = LinearToneMapping( gl_FragColor.rgb ); + #elif defined( REINHARD_TONE_MAPPING ) + gl_FragColor.rgb = ReinhardToneMapping( gl_FragColor.rgb ); + #elif defined( CINEON_TONE_MAPPING ) + gl_FragColor.rgb = CineonToneMapping( gl_FragColor.rgb ); + #elif defined( ACES_FILMIC_TONE_MAPPING ) + gl_FragColor.rgb = ACESFilmicToneMapping( gl_FragColor.rgb ); + #elif defined( AGX_TONE_MAPPING ) + gl_FragColor.rgb = AgXToneMapping( gl_FragColor.rgb ); + #elif defined( NEUTRAL_TONE_MAPPING ) + gl_FragColor.rgb = NeutralToneMapping( gl_FragColor.rgb ); + #elif defined( CUSTOM_TONE_MAPPING ) + gl_FragColor.rgb = CustomToneMapping( gl_FragColor.rgb ); + #endif + + #ifdef SRGB_TRANSFER + gl_FragColor = sRGBTransferOETF( gl_FragColor ); + #endif + }`, + depthTest: false, + depthWrite: false + } ); + + const mesh = new Mesh( geometry, material ); + const camera = new OrthographicCamera( -1, 1, 1, -1, 0, 1 ); + + let _outputColorSpace = null; + let _outputToneMapping = null; + let _isCompositing = false; + let _savedToneMapping; + let _savedRenderTarget = null; + let _effects = []; + let _hasRenderPass = false; + + this.setSize = function ( width, height ) { + + targetA.setSize( width, height ); + targetB.setSize( width, height ); + + for ( let i = 0; i < _effects.length; i ++ ) { + + const effect = _effects[ i ]; + if ( effect.setSize ) effect.setSize( width, height ); + + } + + }; + + this.setEffects = function ( effects ) { + + _effects = effects; + _hasRenderPass = _effects.length > 0 && _effects[ 0 ].isRenderPass === true; + + const width = targetA.width; + const height = targetA.height; + + for ( let i = 0; i < _effects.length; i ++ ) { + + const effect = _effects[ i ]; + if ( effect.setSize ) effect.setSize( width, height ); + + } + + }; + + this.begin = function ( renderer, renderTarget ) { + + // Don't begin during compositing phase (post-processing effects call render()) + if ( _isCompositing ) return false; + + if ( renderer.toneMapping === NoToneMapping && _effects.length === 0 ) return false; + + _savedRenderTarget = renderTarget; + + // resize internal buffers to match render target (e.g. XR resolution) + if ( renderTarget !== null ) { + + const width = renderTarget.width; + const height = renderTarget.height; + + if ( targetA.width !== width || targetA.height !== height ) { + + this.setSize( width, height ); + + } + + } + + // if first effect is a RenderPass, it will set its own render target + if ( _hasRenderPass === false ) { + + renderer.setRenderTarget( targetA ); + + } + + // disable tone mapping during render - it will be applied in end() + _savedToneMapping = renderer.toneMapping; + renderer.toneMapping = NoToneMapping; + + return true; + + }; + + this.hasRenderPass = function () { + + return _hasRenderPass; + + }; + + this.end = function ( renderer, deltaTime ) { + + // restore tone mapping + renderer.toneMapping = _savedToneMapping; + + _isCompositing = true; + + // run post-processing effects + let readBuffer = targetA; + let writeBuffer = targetB; + + for ( let i = 0; i < _effects.length; i ++ ) { + + const effect = _effects[ i ]; + + if ( effect.enabled === false ) continue; + + effect.render( renderer, writeBuffer, readBuffer, deltaTime ); + + if ( effect.needsSwap !== false ) { + + const temp = readBuffer; + readBuffer = writeBuffer; + writeBuffer = temp; + + } + + } + + // update output material defines if settings changed + if ( _outputColorSpace !== renderer.outputColorSpace || _outputToneMapping !== renderer.toneMapping ) { + + _outputColorSpace = renderer.outputColorSpace; + _outputToneMapping = renderer.toneMapping; + + material.defines = {}; + + if ( ColorManagement.getTransfer( _outputColorSpace ) === SRGBTransfer ) material.defines.SRGB_TRANSFER = ''; + + const toneMapping = toneMappingMap[ _outputToneMapping ]; + if ( toneMapping ) material.defines[ toneMapping ] = ''; + + material.needsUpdate = true; + + } + + // final output to canvas (or XR render target) + material.uniforms.tDiffuse.value = readBuffer.texture; + renderer.setRenderTarget( _savedRenderTarget ); + renderer.render( mesh, camera ); + + _savedRenderTarget = null; + _isCompositing = false; + + }; + + this.isCompositing = function () { + + return _isCompositing; + + }; + + this.dispose = function () { + + targetA.dispose(); + targetB.dispose(); + geometry.dispose(); + material.dispose(); + + }; + +} + /** * Creates a three-dimensional texture from raw data, with parameters to * divide it into width, height, and depth. @@ -42068,7 +43037,7 @@ function setValueT1( gl, v, textures ) { if ( this.type === gl.SAMPLER_2D_SHADOW ) { - emptyShadowTexture.compareFunction = LessEqualCompare; // #28670 + emptyShadowTexture.compareFunction = textures.isReversedDepthBuffer() ? GreaterEqualCompare : LessEqualCompare; emptyTexture2D = emptyShadowTexture; } else { @@ -42318,9 +43287,21 @@ function setValueT1Array( gl, v, textures ) { } + let emptyTexture2D; + + if ( this.type === gl.SAMPLER_2D_SHADOW ) { + + emptyTexture2D = emptyShadowTexture; + + } else { + + emptyTexture2D = emptyTexture; + + } + for ( let i = 0; i !== n; ++ i ) { - textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); + textures.setTexture2D( v[ i ] || emptyTexture2D, units[ i ] ); } @@ -42606,6 +43587,31 @@ class WebGLUniforms { } + // Sort uniforms to prioritize shadow samplers first (for optimal texture unit allocation) + + const shadowSamplers = []; + const otherUniforms = []; + + for ( const u of this.seq ) { + + if ( u.type === gl.SAMPLER_2D_SHADOW || u.type === gl.SAMPLER_CUBE_SHADOW || u.type === gl.SAMPLER_2D_ARRAY_SHADOW ) { + + shadowSamplers.push( u ); + + } else { + + otherUniforms.push( u ); + + } + + } + + if ( shadowSamplers.length > 0 ) { + + this.seq = shadowSamplers.concat( otherUniforms ); + + } + } setValue( gl, name, value, textures ) { @@ -42711,7 +43717,7 @@ function getEncodingComponents( colorSpace ) { return [ encodingMatrix, 'sRGBTransferOETF' ]; default: - console.warn( 'THREE.WebGLProgram: Unsupported color space: ', colorSpace ); + warn( 'WebGLProgram: Unsupported color space: ', colorSpace ); return [ encodingMatrix, 'LinearTransferOETF' ]; } @@ -42731,7 +43737,7 @@ function getShaderErrors( gl, shader, type ) { if ( errorMatches ) { // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + // log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); const errorLine = parseInt( errorMatches[ 1 ] ); return type.toUpperCase() + '\n\n' + errors + '\n\n' + handleSource( gl.getShaderSource( shader ), errorLine ); @@ -42760,43 +43766,24 @@ function getTexelEncodingFunction( functionName, colorSpace ) { } -function getToneMappingFunction( functionName, toneMapping ) { - - let toneMappingName; - - switch ( toneMapping ) { - - case LinearToneMapping: - toneMappingName = 'Linear'; - break; - - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; - - case CineonToneMapping: - toneMappingName = 'Cineon'; - break; - - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; +const toneMappingFunctions = { + [ LinearToneMapping ]: 'Linear', + [ ReinhardToneMapping ]: 'Reinhard', + [ CineonToneMapping ]: 'Cineon', + [ ACESFilmicToneMapping ]: 'ACESFilmic', + [ AgXToneMapping ]: 'AgX', + [ NeutralToneMapping ]: 'Neutral', + [ CustomToneMapping ]: 'Custom' +}; - case AgXToneMapping: - toneMappingName = 'AgX'; - break; +function getToneMappingFunction( functionName, toneMapping ) { - case NeutralToneMapping: - toneMappingName = 'Neutral'; - break; + const toneMappingName = toneMappingFunctions[ toneMapping ]; - case CustomToneMapping: - toneMappingName = 'Custom'; - break; + if ( toneMappingName === undefined ) { - default: - console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); - toneMappingName = 'Linear'; + warn( 'WebGLProgram: Unsupported toneMapping:', toneMapping ); + return 'vec3 ' + functionName + '( vec3 color ) { return LinearToneMapping( color ); }'; } @@ -42873,7 +43860,7 @@ function fetchAttributeLocations( gl, program ) { if ( info.type === gl.FLOAT_MAT3 ) locationSize = 3; if ( info.type === gl.FLOAT_MAT4 ) locationSize = 4; - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); + // log( 'WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); attributes[ name ] = { type: info.type, @@ -42943,7 +43930,7 @@ function includeReplacer( match, include ) { if ( newInclude !== undefined ) { string = ShaderChunk[ newInclude ]; - console.warn( 'THREE.WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.', include, newInclude ); + warn( 'WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.', include, newInclude ); } else { @@ -43024,99 +44011,54 @@ function generatePrecision( parameters ) { } -function generateShadowMapTypeDefine( parameters ) { - - let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - - if ( parameters.shadowMapType === PCFShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - - } else if ( parameters.shadowMapType === VSMShadowMap ) { - - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; +const shadowMapTypeDefines = { + [ PCFShadowMap ]: 'SHADOWMAP_TYPE_PCF', + [ VSMShadowMap ]: 'SHADOWMAP_TYPE_VSM' +}; - } +function generateShadowMapTypeDefine( parameters ) { - return shadowMapTypeDefine; + return shadowMapTypeDefines[ parameters.shadowMapType ] || 'SHADOWMAP_TYPE_BASIC'; } -function generateEnvMapTypeDefine( parameters ) { - - let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; - - case CubeUVReflectionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; +const envMapTypeDefines = { + [ CubeReflectionMapping ]: 'ENVMAP_TYPE_CUBE', + [ CubeRefractionMapping ]: 'ENVMAP_TYPE_CUBE', + [ CubeUVReflectionMapping ]: 'ENVMAP_TYPE_CUBE_UV' +}; - } +function generateEnvMapTypeDefine( parameters ) { - } + if ( parameters.envMap === false ) return 'ENVMAP_TYPE_CUBE'; - return envMapTypeDefine; + return envMapTypeDefines[ parameters.envMapMode ] || 'ENVMAP_TYPE_CUBE'; } -function generateEnvMapModeDefine( parameters ) { - - let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - - if ( parameters.envMap ) { - - switch ( parameters.envMapMode ) { - - case CubeRefractionMapping: - - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; +const envMapModeDefines = { + [ CubeRefractionMapping ]: 'ENVMAP_MODE_REFRACTION' +}; - } +function generateEnvMapModeDefine( parameters ) { - } + if ( parameters.envMap === false ) return 'ENVMAP_MODE_REFLECTION'; - return envMapModeDefine; + return envMapModeDefines[ parameters.envMapMode ] || 'ENVMAP_MODE_REFLECTION'; } -function generateEnvMapBlendingDefine( parameters ) { - - let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - - if ( parameters.envMap ) { - - switch ( parameters.combine ) { - - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; - - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; - - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; +const envMapBlendingDefines = { + [ MultiplyOperation ]: 'ENVMAP_BLENDING_MULTIPLY', + [ MixOperation ]: 'ENVMAP_BLENDING_MIX', + [ AddOperation ]: 'ENVMAP_BLENDING_ADD' +}; - } +function generateEnvMapBlendingDefine( parameters ) { - } + if ( parameters.envMap === false ) return 'ENVMAP_BLENDING_NONE'; - return envMapBlendingDefine; + return envMapBlendingDefines[ parameters.combine ] || 'ENVMAP_BLENDING_NONE'; } @@ -43139,7 +44081,7 @@ function generateCubeUVSize( parameters ) { function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { // TODO Send this event to Three.js DevTools - // console.log( 'WebGLProgram', cacheKey ); + // log( 'WebGLProgram', cacheKey ); const gl = renderer.getContext(); @@ -43459,8 +44401,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', parameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '', - parameters.vertexColors || parameters.instancingColor || parameters.batchingColor ? '#define USE_COLOR' : '', - parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', + parameters.vertexAlphas || parameters.batchingColor ? '#define USE_COLOR_ALPHA' : '', parameters.vertexUv1s ? '#define USE_UV1' : '', parameters.vertexUv2s ? '#define USE_UV2' : '', parameters.vertexUv3s ? '#define USE_UV3' : '', @@ -43555,8 +44497,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { const vertexGlsl = versionString + prefixVertex + vertexShader; const fragmentGlsl = versionString + prefixFragment + fragmentShader; - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); + // log( '*VERTEX*', vertexGlsl ); + // log( '*FRAGMENT*', fragmentGlsl ); const glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); const glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); @@ -43610,7 +44552,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - console.error( + error( 'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' + 'VALIDATE_STATUS ' + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + '\n\n' + 'Material Name: ' + self.name + '\n' + @@ -43624,7 +44566,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { } else if ( programLog !== '' ) { - console.warn( 'THREE.WebGLProgram: Program Info Log:', programLog ); + warn( 'WebGLProgram: Program Info Log:', programLog ); } else if ( vertexLog === '' || fragmentLog === '' ) { @@ -43874,21 +44816,21 @@ class WebGLShaderStage { } -function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { +function WebGLPrograms( renderer, environments, extensions, capabilities, bindingStates, clipping ) { const _programLayers = new Layers(); const _customShaders = new WebGLShaderCache(); const _activeChannels = new Set(); const programs = []; + const programsMap = new Map(); const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; - const SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures; let precision = capabilities.precision; const shaderIDs = { MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', + MeshDistanceMaterial: 'distance', MeshNormalMaterial: 'normal', MeshBasicMaterial: 'basic', MeshLambertMaterial: 'lambert', @@ -43918,9 +44860,10 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities const fog = scene.fog; const geometry = object.geometry; - const environment = material.isMeshStandardMaterial ? scene.environment : null; + const environment = ( material.isMeshStandardMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial ) ? scene.environment : null; - const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const usePMREM = material.isMeshStandardMaterial || ( material.isMeshLambertMaterial && ! material.envMap ) || ( material.isMeshPhongMaterial && ! material.envMap ); + const envMap = environments.get( material.envMap || environment, usePMREM ); const envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null; const shaderID = shaderIDs[ material.type ]; @@ -43934,7 +44877,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities if ( precision !== material.precision ) { - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); + warn( 'WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); } @@ -44066,7 +45009,6 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, instancingMorph: IS_INSTANCEDMESH && object.morphTexture !== null, - supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, outputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ), alphaToCoverage: !! material.alphaToCoverage, @@ -44079,7 +45021,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities lightMap: HAS_LIGHTMAP, bumpMap: HAS_BUMPMAP, normalMap: HAS_NORMALMAP, - displacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP, + displacementMap: HAS_DISPLACEMENTMAP, emissiveMap: HAS_EMISSIVEMAP, normalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap, @@ -44170,7 +45112,12 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities useFog: material.fog === true, fogExp2: ( !! fog && fog.isFogExp2 ), - flatShading: ( material.flatShading === true && material.wireframe === false ), + flatShading: material.wireframe === false && ( + material.flatShading === true || + ( geometry.attributes.normal === undefined && HAS_NORMALMAP === false && + ( material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isMeshPhysicalMaterial ) + ) + ), sizeAttenuation: material.sizeAttenuation === true, logarithmicDepthBuffer: logarithmicDepthBuffer, @@ -44339,52 +45286,50 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities _programLayers.disableAll(); - if ( parameters.supportsVertexTextures ) - _programLayers.enable( 0 ); if ( parameters.instancing ) - _programLayers.enable( 1 ); + _programLayers.enable( 0 ); if ( parameters.instancingColor ) - _programLayers.enable( 2 ); + _programLayers.enable( 1 ); if ( parameters.instancingMorph ) - _programLayers.enable( 3 ); + _programLayers.enable( 2 ); if ( parameters.matcap ) - _programLayers.enable( 4 ); + _programLayers.enable( 3 ); if ( parameters.envMap ) - _programLayers.enable( 5 ); + _programLayers.enable( 4 ); if ( parameters.normalMapObjectSpace ) - _programLayers.enable( 6 ); + _programLayers.enable( 5 ); if ( parameters.normalMapTangentSpace ) - _programLayers.enable( 7 ); + _programLayers.enable( 6 ); if ( parameters.clearcoat ) - _programLayers.enable( 8 ); + _programLayers.enable( 7 ); if ( parameters.iridescence ) - _programLayers.enable( 9 ); + _programLayers.enable( 8 ); if ( parameters.alphaTest ) - _programLayers.enable( 10 ); + _programLayers.enable( 9 ); if ( parameters.vertexColors ) - _programLayers.enable( 11 ); + _programLayers.enable( 10 ); if ( parameters.vertexAlphas ) - _programLayers.enable( 12 ); + _programLayers.enable( 11 ); if ( parameters.vertexUv1s ) - _programLayers.enable( 13 ); + _programLayers.enable( 12 ); if ( parameters.vertexUv2s ) - _programLayers.enable( 14 ); + _programLayers.enable( 13 ); if ( parameters.vertexUv3s ) - _programLayers.enable( 15 ); + _programLayers.enable( 14 ); if ( parameters.vertexTangents ) - _programLayers.enable( 16 ); + _programLayers.enable( 15 ); if ( parameters.anisotropy ) - _programLayers.enable( 17 ); + _programLayers.enable( 16 ); if ( parameters.alphaHash ) - _programLayers.enable( 18 ); + _programLayers.enable( 17 ); if ( parameters.batching ) - _programLayers.enable( 19 ); + _programLayers.enable( 18 ); if ( parameters.dispersion ) - _programLayers.enable( 20 ); + _programLayers.enable( 19 ); if ( parameters.batchingColor ) - _programLayers.enable( 21 ); + _programLayers.enable( 20 ); if ( parameters.gradientMap ) - _programLayers.enable( 22 ); + _programLayers.enable( 21 ); array.push( _programLayers.mask ); _programLayers.disableAll(); @@ -44460,29 +45405,19 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities function acquireProgram( parameters, cacheKey ) { - let program; + let program = programsMap.get( cacheKey ); - // Check if code has been already compiled - for ( let p = 0, pl = programs.length; p < pl; p ++ ) { + if ( program !== undefined ) { - const preexistingProgram = programs[ p ]; + ++ program.usedTimes; - if ( preexistingProgram.cacheKey === cacheKey ) { - - program = preexistingProgram; - ++ program.usedTimes; - - break; - - } - - } - - if ( program === undefined ) { + } else { program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); programs.push( program ); + programsMap.set( cacheKey, program ); + } return program; @@ -44498,6 +45433,9 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); + // Remove from map + programsMap.delete( program.cacheKey ); + // Free WebGL resources program.destroy(); @@ -44598,6 +45536,10 @@ function painterSortStable( a, b ) { return a.material.id - b.material.id; + } else if ( a.materialVariant !== b.materialVariant ) { + + return a.materialVariant - b.materialVariant; + } else if ( a.z !== b.z ) { return a.z - b.z; @@ -44652,6 +45594,15 @@ function WebGLRenderList() { } + function materialVariant( object ) { + + let variant = 0; + if ( object.isInstancedMesh ) variant += 2; + if ( object.isSkinnedMesh ) variant += 1; + return variant; + + } + function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { let renderItem = renderItems[ renderItemsIndex ]; @@ -44663,6 +45614,7 @@ function WebGLRenderList() { object: object, geometry: geometry, material: material, + materialVariant: materialVariant( object ), groupOrder: groupOrder, renderOrder: object.renderOrder, z: z, @@ -44677,6 +45629,7 @@ function WebGLRenderList() { renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; + renderItem.materialVariant = materialVariant( object ); renderItem.groupOrder = groupOrder; renderItem.renderOrder = object.renderOrder; renderItem.z = z; @@ -45055,7 +46008,23 @@ function WebGLLights( extensions ) { const intensity = light.intensity; const distance = light.distance; - const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; + let shadowMap = null; + + if ( light.shadow && light.shadow.map ) { + + if ( light.shadow.map.texture.format === RGFormat ) { + + // VSM uses color texture with blurred mean/std_dev + shadowMap = light.shadow.map.texture; + + } else { + + // Other types use depth texture + shadowMap = light.shadow.map.depthTexture || light.shadow.map.texture; + + } + + } if ( light.isAmbientLight ) { @@ -45493,6 +46462,7 @@ function WebGLRenderStates( extensions ) { * near and far plane. White is nearest, black is farthest. * * @augments Material + * @demo scenes/material-browser.html#MeshDepthMaterial */ class MeshDepthMaterial extends Material { @@ -45747,9 +46717,95 @@ class MeshDistanceMaterial extends Material { } +/** + * This class can be used to automatically save the depth information of a + * cube rendering into a cube texture with depth format. Used for PointLight shadows. + * + * @augments DepthTexture + */ +class CubeDepthTexture extends DepthTexture { + + /** + * Constructs a new cube depth texture. + * + * @param {number} size - The size (width and height) of each cube face. + * @param {number} [type=UnsignedIntType] - The texture type. + * @param {number} [mapping=CubeReflectionMapping] - The texture mapping. + * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. + * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. + * @param {number} [magFilter=NearestFilter] - The mag filter value. + * @param {number} [minFilter=NearestFilter] - The min filter value. + * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. + * @param {number} [format=DepthFormat] - The texture format. + */ + constructor( size, type = UnsignedIntType, mapping = CubeReflectionMapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, format = DepthFormat ) { + + // Create 6 identical image descriptors for the cube faces + const image = { width: size, height: size, depth: 1 }; + const images = [ image, image, image, image, image, image ]; + + // Call DepthTexture constructor with width, height + super( size, size, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ); + + // Replace the single image with the array of 6 images + this.image = images; + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isCubeDepthTexture = true; + + /** + * Set to true for cube texture handling in WebGLTextures. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isCubeTexture = true; + + } + + /** + * Alias for {@link CubeDepthTexture#image}. + * + * @type {Array} + */ + get images() { + + return this.image; + + } + + set images( value ) { + + this.image = value; + + } + +} + const vertex = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; -const fragment = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tconst float samples = float( VSM_SAMPLES );\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; +const fragment = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\nvoid main() {\n\tconst float samples = float( VSM_SAMPLES );\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ).rg;\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ).r;\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( max( 0.0, squared_mean - mean * mean ) );\n\tgl_FragColor = vec4( mean, std_dev, 0.0, 1.0 );\n}"; + +const _cubeDirections = [ + /*@__PURE__*/ new Vector3( 1, 0, 0 ), /*@__PURE__*/ new Vector3( -1, 0, 0 ), /*@__PURE__*/ new Vector3( 0, 1, 0 ), + /*@__PURE__*/ new Vector3( 0, -1, 0 ), /*@__PURE__*/ new Vector3( 0, 0, 1 ), /*@__PURE__*/ new Vector3( 0, 0, -1 ) +]; + +const _cubeUps = [ + /*@__PURE__*/ new Vector3( 0, -1, 0 ), /*@__PURE__*/ new Vector3( 0, -1, 0 ), /*@__PURE__*/ new Vector3( 0, 0, 1 ), + /*@__PURE__*/ new Vector3( 0, 0, -1 ), /*@__PURE__*/ new Vector3( 0, -1, 0 ), /*@__PURE__*/ new Vector3( 0, -1, 0 ) +]; + +const _projScreenMatrix$2 = /*@__PURE__*/ new Matrix4(); +const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); +const _lookTarget$1 = /*@__PURE__*/ new Vector3(); function WebGLShadowMap( renderer, objects, capabilities ) { @@ -45760,7 +46816,7 @@ function WebGLShadowMap( renderer, objects, capabilities ) { _viewport = new Vector4(), - _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), + _depthMaterial = new MeshDepthMaterial(), _distanceMaterial = new MeshDistanceMaterial(), _materialCache = {}, @@ -45815,6 +46871,13 @@ function WebGLShadowMap( renderer, objects, capabilities ) { if ( lights.length === 0 ) return; + if ( this.type === PCFSoftShadowMap ) { + + warn( 'WebGLShadowMap: PCFSoftShadowMap has been deprecated. Using PCFShadowMap instead.' ); + this.type = PCFShadowMap; + + } + const currentRenderTarget = renderer.getRenderTarget(); const activeCubeFace = renderer.getActiveCubeFace(); const activeMipmapLevel = renderer.getActiveMipmapLevel(); @@ -45839,8 +46902,31 @@ function WebGLShadowMap( renderer, objects, capabilities ) { // check for shadow map type changes - const toVSM = ( _previousType !== VSMShadowMap && this.type === VSMShadowMap ); - const fromVSM = ( _previousType === VSMShadowMap && this.type !== VSMShadowMap ); + const typeChanged = _previousType !== this.type; + + // When shadow map type changes, materials need recompilation because sampler types change + // (sampler2DShadow for PCF vs sampler2D for Basic) + if ( typeChanged ) { + + scene.traverse( function ( object ) { + + if ( object.material ) { + + if ( Array.isArray( object.material ) ) { + + object.material.forEach( mat => mat.needsUpdate = true ); + + } else { + + object.material.needsUpdate = true; + + } + + } + + } ); + + } // render depth map @@ -45851,7 +46937,7 @@ function WebGLShadowMap( renderer, objects, capabilities ) { if ( shadow === undefined ) { - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); + warn( 'WebGLShadowMap:', light, 'has no shadow.' ); continue; } @@ -45886,42 +46972,154 @@ function WebGLShadowMap( renderer, objects, capabilities ) { } - if ( shadow.map === null || toVSM === true || fromVSM === true ) { + const reversedDepthBuffer = renderer.state.buffers.depth.getReversed(); + shadow.camera._reversedDepth = reversedDepthBuffer; - const pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {}; + if ( shadow.map === null || typeChanged === true ) { if ( shadow.map !== null ) { + if ( shadow.map.depthTexture !== null ) { + + shadow.map.depthTexture.dispose(); + shadow.map.depthTexture = null; + + } + shadow.map.dispose(); } - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + '.shadowMap'; + if ( this.type === VSMShadowMap ) { + + if ( light.isPointLight ) { + + warn( 'WebGLShadowMap: VSM shadow maps are not supported for PointLights. Use PCF or BasicShadowMap instead.' ); + continue; + + } + + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, { + format: RGFormat, + type: HalfFloatType, + minFilter: LinearFilter, + magFilter: LinearFilter, + generateMipmaps: false + } ); + shadow.map.texture.name = light.name + '.shadowMap'; + + // Native depth texture for VSM - depth is captured here, then blurred into the color texture + shadow.map.depthTexture = new DepthTexture( _shadowMapSize.x, _shadowMapSize.y, FloatType ); + shadow.map.depthTexture.name = light.name + '.shadowMapDepth'; + shadow.map.depthTexture.format = DepthFormat; + shadow.map.depthTexture.compareFunction = null; // For regular sampling (not shadow comparison) + shadow.map.depthTexture.minFilter = NearestFilter; + shadow.map.depthTexture.magFilter = NearestFilter; + + } else { + + if ( light.isPointLight ) { + + shadow.map = new WebGLCubeRenderTarget( _shadowMapSize.x ); + shadow.map.depthTexture = new CubeDepthTexture( _shadowMapSize.x, UnsignedIntType ); + + } else { + + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); + shadow.map.depthTexture = new DepthTexture( _shadowMapSize.x, _shadowMapSize.y, UnsignedIntType ); + + } + + shadow.map.depthTexture.name = light.name + '.shadowMap'; + shadow.map.depthTexture.format = DepthFormat; + + if ( this.type === PCFShadowMap ) { + + shadow.map.depthTexture.compareFunction = reversedDepthBuffer ? GreaterEqualCompare : LessEqualCompare; + shadow.map.depthTexture.minFilter = LinearFilter; + shadow.map.depthTexture.magFilter = LinearFilter; + + } else { + + shadow.map.depthTexture.compareFunction = null; + shadow.map.depthTexture.minFilter = NearestFilter; + shadow.map.depthTexture.magFilter = NearestFilter; + + } + + } shadow.camera.updateProjectionMatrix(); } - renderer.setRenderTarget( shadow.map ); - renderer.clear(); + // For cube render targets (PointLights), render all 6 faces. Otherwise, render once. + const faceCount = shadow.map.isWebGLCubeRenderTarget ? 6 : 1; - const viewportCount = shadow.getViewportCount(); + for ( let face = 0; face < faceCount; face ++ ) { - for ( let vp = 0; vp < viewportCount; vp ++ ) { + // For cube render targets, render to each face separately + if ( shadow.map.isWebGLCubeRenderTarget ) { - const viewport = shadow.getViewport( vp ); + renderer.setRenderTarget( shadow.map, face ); + renderer.clear(); - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); + } else { + + // For 2D render targets, use viewports + if ( face === 0 ) { + + renderer.setRenderTarget( shadow.map ); + renderer.clear(); + + } + + const viewport = shadow.getViewport( face ); + + _viewport.set( + _viewportSize.x * viewport.x, + _viewportSize.y * viewport.y, + _viewportSize.x * viewport.z, + _viewportSize.y * viewport.w + ); + + _state.viewport( _viewport ); + + } + + if ( light.isPointLight ) { + + const camera = shadow.camera; + const shadowMatrix = shadow.matrix; + + const far = light.distance || camera.far; + + if ( far !== camera.far ) { - _state.viewport( _viewport ); + camera.far = far; + camera.updateProjectionMatrix(); - shadow.updateMatrices( light, vp ); + } + + _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); + camera.position.copy( _lightPositionWorld$1 ); + + _lookTarget$1.copy( camera.position ); + _lookTarget$1.add( _cubeDirections[ face ] ); + camera.up.copy( _cubeUps[ face ] ); + camera.lookAt( _lookTarget$1 ); + camera.updateMatrixWorld(); + + shadowMatrix.makeTranslation( - _lightPositionWorld$1.x, - _lightPositionWorld$1.y, - _lightPositionWorld$1.z ); + + _projScreenMatrix$2.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + shadow._frustum.setFromProjectionMatrix( _projScreenMatrix$2, camera.coordinateSystem, camera.reversedDepth ); + + } else { + + shadow.updateMatrices( light ); + + } _frustum = shadow.getFrustum(); @@ -45965,13 +47163,16 @@ function WebGLShadowMap( renderer, objects, capabilities ) { if ( shadow.mapPass === null ) { - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); + shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, { + format: RGFormat, + type: HalfFloatType + } ); } - // vertical pass + // vertical pass - read from native depth texture - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; + shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.depthTexture; shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; shadowMaterialVertical.uniforms.radius.value = shadow.radius; renderer.setRenderTarget( shadow.mapPass ); @@ -46170,18 +47371,6 @@ function WebGLShadowMap( renderer, objects, capabilities ) { } -const reversedFuncs = { - [ NeverDepth ]: AlwaysDepth, - [ LessDepth ]: GreaterDepth, - [ EqualDepth ]: NotEqualDepth, - [ LessEqualDepth ]: GreaterEqualDepth, - - [ AlwaysDepth ]: NeverDepth, - [ GreaterDepth ]: LessDepth, - [ NotEqualDepth ]: EqualDepth, - [ GreaterEqualDepth ]: LessEqualDepth, -}; - function WebGLState( gl, extensions ) { function ColorBuffer() { @@ -46313,7 +47502,7 @@ function WebGLState( gl, extensions ) { setFunc: function ( depthFunc ) { - if ( currentReversed ) depthFunc = reversedFuncs[ depthFunc ]; + if ( currentReversed ) depthFunc = ReversedDepthFuncs[ depthFunc ]; if ( currentDepthFunc !== depthFunc ) { @@ -46381,6 +47570,8 @@ function WebGLState( gl, extensions ) { if ( currentDepthClear !== depth ) { + currentDepthClear = depth; + if ( currentReversed ) { depth = 1 - depth; @@ -46388,7 +47579,6 @@ function WebGLState( gl, extensions ) { } gl.clearDepth( depth ); - currentDepthClear = depth; } @@ -46839,7 +48029,7 @@ function WebGLState( gl, extensions ) { break; default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + error( 'WebGLState: Invalid blending: ', blending ); break; } @@ -46857,15 +48047,15 @@ function WebGLState( gl, extensions ) { break; case SubtractiveBlending: - console.error( 'THREE.WebGLState: SubtractiveBlending requires material.premultipliedAlpha = true' ); + error( 'WebGLState: SubtractiveBlending requires material.premultipliedAlpha = true' ); break; case MultiplyBlending: - console.error( 'THREE.WebGLState: MultiplyBlending requires material.premultipliedAlpha = true' ); + error( 'WebGLState: MultiplyBlending requires material.premultipliedAlpha = true' ); break; default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + error( 'WebGLState: Invalid blending: ', blending ); break; } @@ -47042,11 +48232,17 @@ function WebGLState( gl, extensions ) { if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - gl.polygonOffset( factor, units ); - currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; + if ( depthBuffer.getReversed() ) { + + factor = - factor; + + } + + gl.polygonOffset( factor, units ); + } } else { @@ -47150,9 +48346,9 @@ function WebGLState( gl, extensions ) { gl.compressedTexImage2D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47164,9 +48360,9 @@ function WebGLState( gl, extensions ) { gl.compressedTexImage3D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47178,9 +48374,9 @@ function WebGLState( gl, extensions ) { gl.texSubImage2D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47192,9 +48388,9 @@ function WebGLState( gl, extensions ) { gl.texSubImage3D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47206,9 +48402,9 @@ function WebGLState( gl, extensions ) { gl.compressedTexSubImage2D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47220,9 +48416,9 @@ function WebGLState( gl, extensions ) { gl.compressedTexSubImage3D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47234,9 +48430,9 @@ function WebGLState( gl, extensions ) { gl.texStorage2D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47248,9 +48444,9 @@ function WebGLState( gl, extensions ) { gl.texStorage3D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47262,9 +48458,9 @@ function WebGLState( gl, extensions ) { gl.texImage2D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47276,9 +48472,9 @@ function WebGLState( gl, extensions ) { gl.texImage3D( ...arguments ); - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLState:', error ); + error( 'WebGLState:', e ); } @@ -47548,8 +48744,12 @@ function getByteLength( width, height, format, type ) { // https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_etc/ case RGB_ETC1_Format: case RGB_ETC2_Format: + case R11_EAC_Format: + case SIGNED_R11_EAC_Format: return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 8; case RGBA_ETC2_EAC_Format: + case RG11_EAC_Format: + case SIGNED_RG11_EAC_Format: return Math.floor( ( width + 3 ) / 4 ) * Math.floor( ( height + 3 ) / 4 ) * 16; // https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_astc/ @@ -47652,9 +48852,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, try { useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' - // eslint-disable-next-line compat/compat && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; + } catch ( err ) { // Ignore any errors @@ -47666,7 +48866,6 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, // Use OffscreenCanvas when available. Specially needed in web workers return useOffscreenCanvas ? - // eslint-disable-next-line compat/compat new OffscreenCanvas( width, height ) : createElementNS( 'canvas' ); } @@ -47711,7 +48910,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, const context = canvas.getContext( '2d' ); context.drawImage( image, 0, 0, width, height ); - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + dimensions.width + 'x' + dimensions.height + ') to (' + width + 'x' + height + ').' ); + warn( 'WebGLRenderer: Texture has been resized from (' + dimensions.width + 'x' + dimensions.height + ') to (' + width + 'x' + height + ').' ); return canvas; @@ -47719,7 +48918,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( 'data' in image ) { - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + dimensions.width + 'x' + dimensions.height + ').' ); + warn( 'WebGLRenderer: Image in DataTexture is too big (' + dimensions.width + 'x' + dimensions.height + ').' ); } @@ -47760,7 +48959,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; - console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); + warn( 'WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); } @@ -47873,7 +49072,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } else if ( depthType === UnsignedShortType ) { glInternalFormat = _gl.DEPTH24_STENCIL8; - console.warn( 'DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.' ); + warn( 'DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.' ); } @@ -48103,7 +49302,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( textureUnit >= capabilities.maxTextures ) { - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); + warn( 'WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); } @@ -48150,11 +49349,11 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( image === null ) { - console.warn( 'THREE.WebGLRenderer: Texture marked for update but no image data found.' ); + warn( 'WebGLRenderer: Texture marked for update but no image data found.' ); } else if ( image.complete === false ) { - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); + warn( 'WebGLRenderer: Texture marked for update but image is incomplete' ); } else { @@ -48182,6 +49381,10 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, uploadTexture( textureProperties, texture, slot ); return; + } else if ( texture.isExternalTexture ) { + + textureProperties.__webglTexture = texture.sourceTexture ? texture.sourceTexture : null; + } state.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); @@ -48207,7 +49410,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, const textureProperties = properties.get( texture ); - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + if ( texture.isCubeDepthTexture !== true && texture.version > 0 && textureProperties.__version !== texture.version ) { uploadCubeTexture( textureProperties, texture, slot ); return; @@ -48251,7 +49454,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, ( texture.magFilter === LinearFilter || texture.magFilter === LinearMipmapNearestFilter || texture.magFilter === NearestMipmapLinearFilter || texture.magFilter === LinearMipmapLinearFilter || texture.minFilter === LinearFilter || texture.minFilter === LinearMipmapNearestFilter || texture.minFilter === NearestMipmapLinearFilter || texture.minFilter === LinearMipmapLinearFilter ) ) { - console.warn( 'THREE.WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device.' ); + warn( 'WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device.' ); } @@ -48660,7 +49863,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } else { - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); + warn( 'WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); } @@ -48716,7 +49919,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } else { - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); + warn( 'WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); } @@ -49010,7 +50213,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } else { - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); + warn( 'WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); } @@ -49199,7 +50402,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { + function setupRenderBufferStorage( renderbuffer, renderTarget, useMultisample ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); @@ -49212,15 +50415,13 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, const glAttachmentType = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; // set up the attachment - const samples = getRenderTargetSamples( renderTarget ); - const isUseMultisampledRTT = useMultisampledRTT( renderTarget ); - if ( isUseMultisampledRTT ) { + if ( useMultisampledRTT( renderTarget ) ) { - multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, getRenderTargetSamples( renderTarget ), glInternalFormat, renderTarget.width, renderTarget.height ); - } else if ( isMultisample ) { + } else if ( useMultisample ) { - _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, getRenderTargetSamples( renderTarget ), glInternalFormat, renderTarget.width, renderTarget.height ); } else { @@ -49241,15 +50442,14 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, const glFormat = utils.convert( texture.format, texture.colorSpace ); const glType = utils.convert( texture.type ); const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); - const samples = getRenderTargetSamples( renderTarget ); - if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { + if ( useMultisampledRTT( renderTarget ) ) { - _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, getRenderTargetSamples( renderTarget ), glInternalFormat, renderTarget.width, renderTarget.height ); - } else if ( useMultisampledRTT( renderTarget ) ) { + } else if ( useMultisample ) { - multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, getRenderTargetSamples( renderTarget ), glInternalFormat, renderTarget.width, renderTarget.height ); } else { @@ -49266,10 +50466,9 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { + function setupDepthTexture( framebuffer, renderTarget, cubeFace ) { - const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); - if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); @@ -49293,20 +50492,69 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } - setTexture2D( renderTarget.depthTexture, 0 ); + if ( isCube ) { + + // For cube depth textures, initialize and bind without uploading image data + if ( textureProperties.__webglInit === undefined ) { + + textureProperties.__webglInit = true; + renderTarget.depthTexture.addEventListener( 'dispose', onTextureDispose ); + + } + + // Only create and allocate storage once + if ( textureProperties.__webglTexture === undefined ) { + + textureProperties.__webglTexture = _gl.createTexture(); + + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.depthTexture ); + + // Allocate storage for all 6 faces with correct depth texture format + const glFormat = utils.convert( renderTarget.depthTexture.format ); + const glType = utils.convert( renderTarget.depthTexture.type ); + + // Use proper internal format for depth textures + let glInternalFormat; + if ( renderTarget.depthTexture.format === DepthFormat ) { + + glInternalFormat = _gl.DEPTH_COMPONENT24; + + } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { + + glInternalFormat = _gl.DEPTH24_STENCIL8; + + } + + for ( let i = 0; i < 6; i ++ ) { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + } + + } + + } else { + + setTexture2D( renderTarget.depthTexture, 0 ); + + } const webglDepthTexture = textureProperties.__webglTexture; const samples = getRenderTargetSamples( renderTarget ); + const glTextureType = isCube ? _gl.TEXTURE_CUBE_MAP_POSITIVE_X + cubeFace : _gl.TEXTURE_2D; + const glAttachmentType = renderTarget.depthTexture.format === DepthStencilFormat ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; + if ( renderTarget.depthTexture.format === DepthFormat ) { if ( useMultisampledRTT( renderTarget ) ) { - multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); + multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, glAttachmentType, glTextureType, webglDepthTexture, 0, samples ); } else { - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, glAttachmentType, glTextureType, webglDepthTexture, 0 ); } @@ -49314,11 +50562,11 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( useMultisampledRTT( renderTarget ) ) { - multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); + multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, glAttachmentType, glTextureType, webglDepthTexture, 0, samples ); } else { - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, glAttachmentType, glTextureType, webglDepthTexture, 0 ); } @@ -49369,17 +50617,28 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) { - if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); + if ( isCube ) { - const mipmaps = renderTarget.texture.mipmaps; + // For cube render targets with depth texture, setup each face + for ( let i = 0; i < 6; i ++ ) { - if ( mipmaps && mipmaps.length > 0 ) { + setupDepthTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, i ); - setupDepthTexture( renderTargetProperties.__webglFramebuffer[ 0 ], renderTarget ); + } } else { - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); + const mipmaps = renderTarget.texture.mipmaps; + + if ( mipmaps && mipmaps.length > 0 ) { + + setupDepthTexture( renderTargetProperties.__webglFramebuffer[ 0 ], renderTarget, 0 ); + + } else { + + setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 0 ); + + } } @@ -49903,13 +51162,13 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, if ( format !== RGBAFormat || type !== UnsignedByteType ) { - console.warn( 'THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' ); + warn( 'WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' ); } } else { - console.error( 'THREE.WebGLTextures: Unsupported texture color space:', colorSpace ); + error( 'WebGLTextures: Unsupported texture color space:', colorSpace ); } @@ -49961,6 +51220,12 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, this.setupFrameBufferTexture = setupFrameBufferTexture; this.useMultisampledRTT = useMultisampledRTT; + this.isReversedDepthBuffer = function () { + + return state.buffers.depth.getReversed(); + + }; + } function WebGLUtils( gl, extensions ) { @@ -50064,7 +51329,7 @@ function WebGLUtils( gl, extensions ) { // ETC - if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { + if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format || p === R11_EAC_Format || p === SIGNED_R11_EAC_Format || p === RG11_EAC_Format || p === SIGNED_RG11_EAC_Format ) { extension = extensions.get( 'WEBGL_compressed_texture_etc' ); @@ -50072,6 +51337,10 @@ function WebGLUtils( gl, extensions ) { if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; if ( p === RGBA_ETC2_EAC_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; + if ( p === R11_EAC_Format ) return extension.COMPRESSED_R11_EAC; + if ( p === SIGNED_R11_EAC_Format ) return extension.COMPRESSED_SIGNED_R11_EAC; + if ( p === RG11_EAC_Format ) return extension.COMPRESSED_RG11_EAC; + if ( p === SIGNED_RG11_EAC_Format ) return extension.COMPRESSED_SIGNED_RG11_EAC; } else { @@ -51150,7 +52419,7 @@ class WebXRManager extends EventDispatcher { if ( scope.isPresenting === true ) { - console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); + warn( 'WebXRManager: Cannot change framebuffer scale while presenting.' ); } @@ -51172,7 +52441,7 @@ class WebXRManager extends EventDispatcher { if ( scope.isPresenting === true ) { - console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); + warn( 'WebXRManager: Cannot change reference space type while presenting.' ); } @@ -51620,8 +52889,8 @@ class WebXRManager extends EventDispatcher { // inherit camera layers and enable eye layers (1 = left, 2 = right) cameraXR.layers.mask = camera.layers.mask | 0b110; - cameraL.layers.mask = cameraXR.layers.mask & 0b011; - cameraR.layers.mask = cameraXR.layers.mask & 0b101; + cameraL.layers.mask = cameraXR.layers.mask & -5; + cameraR.layers.mask = cameraXR.layers.mask & -3; const parent = camera.parent; const cameras = cameraXR.cameras; @@ -52015,6 +53284,12 @@ function WebGLMaterials( renderer, properties ) { refreshUniformsCommon( uniforms, material ); + if ( material.envMap ) { + + uniforms.envMapIntensity.value = material.envMapIntensity; + + } + } else if ( material.isMeshToonMaterial ) { refreshUniformsCommon( uniforms, material ); @@ -52025,6 +53300,12 @@ function WebGLMaterials( renderer, properties ) { refreshUniformsCommon( uniforms, material ); refreshUniformsPhong( uniforms, material ); + if ( material.envMap ) { + + uniforms.envMapIntensity.value = material.envMapIntensity; + + } + } else if ( material.isMeshStandardMaterial ) { refreshUniformsCommon( uniforms, material ); @@ -52638,7 +53919,7 @@ function WebGLUniformsGroups( gl, info, capabilities, state ) { } - console.error( 'THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.' ); + error( 'WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.' ); return 0; @@ -52893,11 +54174,11 @@ function WebGLUniformsGroups( gl, info, capabilities, state ) { } else if ( value.isTexture ) { - console.warn( 'THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.' ); + warn( 'WebGLRenderer: Texture samplers can not be part of an uniforms group.' ); } else { - console.warn( 'THREE.WebGLRenderer: Unsupported uniform value type.', value ); + warn( 'WebGLRenderer: Unsupported uniform value type.', value ); } @@ -52946,6 +54227,137 @@ function WebGLUniformsGroups( gl, info, capabilities, state ) { } +/** + * Creates a texture directly from raw buffer data. + * + * The interpretation of the data depends on type and format: If the type is + * `UnsignedByteType`, a `Uint8Array` will be useful for addressing the + * texel data. If the format is `RGBAFormat`, data needs four values for + * one texel; Red, Green, Blue and Alpha (typically the opacity). + * + * @augments Texture + */ +class DataTexture extends Texture { + + /** + * Constructs a new data texture. + * + * @param {?TypedArray} [data=null] - The buffer data. + * @param {number} [width=1] - The width of the texture. + * @param {number} [height=1] - The height of the texture. + * @param {number} [format=RGBAFormat] - The texture format. + * @param {number} [type=UnsignedByteType] - The texture type. + * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. + * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. + * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. + * @param {number} [magFilter=NearestFilter] - The mag filter value. + * @param {number} [minFilter=NearestFilter] - The min filter value. + * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. + * @param {string} [colorSpace=NoColorSpace] - The color space. + */ + constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace ) { + + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isDataTexture = true; + + /** + * The image definition of a data texture. + * + * @type {{data:TypedArray,width:number,height:number}} + */ + this.image = { data: data, width: width, height: height }; + + /** + * Whether to generate mipmaps (if possible) for a texture. + * + * Overwritten and set to `false` by default. + * + * @type {boolean} + * @default false + */ + this.generateMipmaps = false; + + /** + * If set to `true`, the texture is flipped along the vertical axis when + * uploaded to the GPU. + * + * Overwritten and set to `false` by default. + * + * @type {boolean} + * @default false + */ + this.flipY = false; + + /** + * Specifies the alignment requirements for the start of each pixel row in memory. + * + * Overwritten and set to `1` by default. + * + * @type {boolean} + * @default 1 + */ + this.unpackAlignment = 1; + + } + +} + +/** + * Precomputed DFG LUT for Image-Based Lighting + * Resolution: 16x16 + * Samples: 4096 per texel + * Format: RG16F (2 half floats per texel: scale, bias) + */ + + +const DATA = new Uint16Array( [ + 0x30b5, 0x3ad1, 0x314c, 0x3a4d, 0x33d2, 0x391c, 0x35ef, 0x3828, 0x37f3, 0x36a6, 0x38d1, 0x3539, 0x3979, 0x3410, 0x39f8, 0x3252, 0x3a53, 0x30f0, 0x3a94, 0x2fc9, 0x3abf, 0x2e35, 0x3ada, 0x2d05, 0x3ae8, 0x2c1f, 0x3aed, 0x2ae0, 0x3aea, 0x29d1, 0x3ae1, 0x28ff, + 0x3638, 0x38e4, 0x364a, 0x38ce, 0x3699, 0x385e, 0x374e, 0x372c, 0x3839, 0x35a4, 0x38dc, 0x3462, 0x396e, 0x32c4, 0x39de, 0x3134, 0x3a2b, 0x3003, 0x3a59, 0x2e3a, 0x3a6d, 0x2ce1, 0x3a6e, 0x2bba, 0x3a5f, 0x2a33, 0x3a49, 0x290a, 0x3a2d, 0x2826, 0x3a0a, 0x26e8, + 0x3894, 0x36d7, 0x3897, 0x36c9, 0x38a3, 0x3675, 0x38bc, 0x35ac, 0x38ee, 0x349c, 0x393e, 0x3332, 0x3997, 0x3186, 0x39e2, 0x3038, 0x3a13, 0x2e75, 0x3a29, 0x2cf5, 0x3a2d, 0x2bac, 0x3a21, 0x29ff, 0x3a04, 0x28bc, 0x39dc, 0x2790, 0x39ad, 0x261a, 0x3978, 0x24fa, + 0x39ac, 0x34a8, 0x39ac, 0x34a3, 0x39ae, 0x3480, 0x39ae, 0x3423, 0x39b1, 0x330e, 0x39c2, 0x31a9, 0x39e0, 0x3063, 0x39fc, 0x2eb5, 0x3a0c, 0x2d1d, 0x3a14, 0x2bcf, 0x3a07, 0x29ff, 0x39e9, 0x28a3, 0x39be, 0x273c, 0x3989, 0x25b3, 0x394a, 0x2488, 0x3907, 0x2345, + 0x3a77, 0x3223, 0x3a76, 0x321f, 0x3a73, 0x3204, 0x3a6a, 0x31b3, 0x3a58, 0x3114, 0x3a45, 0x303b, 0x3a34, 0x2eb6, 0x3a26, 0x2d31, 0x3a1e, 0x2bef, 0x3a0b, 0x2a0d, 0x39ec, 0x28a1, 0x39c0, 0x271b, 0x3987, 0x2580, 0x3944, 0x2449, 0x38fa, 0x22bd, 0x38ac, 0x2155, + 0x3b07, 0x2fca, 0x3b06, 0x2fca, 0x3b00, 0x2fb8, 0x3af4, 0x2f7c, 0x3adb, 0x2eea, 0x3ab4, 0x2e00, 0x3a85, 0x2cec, 0x3a5e, 0x2bc5, 0x3a36, 0x2a00, 0x3a0d, 0x2899, 0x39dc, 0x2707, 0x39a0, 0x2562, 0x395a, 0x2424, 0x390b, 0x2268, 0x38b7, 0x20fd, 0x385f, 0x1fd1, + 0x3b69, 0x2cb9, 0x3b68, 0x2cbb, 0x3b62, 0x2cbb, 0x3b56, 0x2cae, 0x3b3b, 0x2c78, 0x3b0d, 0x2c0a, 0x3acf, 0x2ae3, 0x3a92, 0x2998, 0x3a54, 0x2867, 0x3a17, 0x26d0, 0x39d3, 0x253c, 0x3989, 0x2402, 0x3935, 0x2226, 0x38dc, 0x20bd, 0x387d, 0x1f54, 0x381d, 0x1db3, + 0x3ba9, 0x296b, 0x3ba8, 0x296f, 0x3ba3, 0x297b, 0x3b98, 0x2987, 0x3b7f, 0x2976, 0x3b4e, 0x2927, 0x3b0e, 0x2895, 0x3ac2, 0x27b7, 0x3a73, 0x263b, 0x3a23, 0x24e7, 0x39d0, 0x239b, 0x3976, 0x21d9, 0x3917, 0x207e, 0x38b2, 0x1ee7, 0x384b, 0x1d53, 0x37c7, 0x1c1e, + 0x3bd2, 0x25cb, 0x3bd1, 0x25d3, 0x3bcd, 0x25f0, 0x3bc2, 0x261f, 0x3bad, 0x2645, 0x3b7d, 0x262d, 0x3b3e, 0x25c4, 0x3aec, 0x250f, 0x3a93, 0x243a, 0x3a32, 0x22ce, 0x39d0, 0x215b, 0x3969, 0x202a, 0x38fe, 0x1e6e, 0x388f, 0x1cf1, 0x381f, 0x1b9b, 0x3762, 0x19dd, + 0x3be9, 0x21ab, 0x3be9, 0x21b7, 0x3be5, 0x21e5, 0x3bdd, 0x2241, 0x3bc9, 0x22a7, 0x3ba0, 0x22ec, 0x3b62, 0x22cd, 0x3b0f, 0x2247, 0x3aae, 0x2175, 0x3a44, 0x2088, 0x39d4, 0x1f49, 0x3960, 0x1dbe, 0x38e9, 0x1c77, 0x3870, 0x1ae8, 0x37f1, 0x1953, 0x3708, 0x181b, + 0x3bf6, 0x1cea, 0x3bf6, 0x1cfb, 0x3bf3, 0x1d38, 0x3bec, 0x1dbd, 0x3bda, 0x1e7c, 0x3bb7, 0x1f25, 0x3b7d, 0x1f79, 0x3b2c, 0x1f4c, 0x3ac6, 0x1ea6, 0x3a55, 0x1dbb, 0x39da, 0x1cbd, 0x395a, 0x1b9d, 0x38d8, 0x1a00, 0x3855, 0x18ac, 0x37ab, 0x173c, 0x36b7, 0x1598, + 0x3bfc, 0x1736, 0x3bfc, 0x1759, 0x3bf9, 0x17e7, 0x3bf4, 0x1896, 0x3be4, 0x1997, 0x3bc6, 0x1aa8, 0x3b91, 0x1b84, 0x3b43, 0x1bd2, 0x3ade, 0x1b8a, 0x3a65, 0x1acd, 0x39e2, 0x19d3, 0x3957, 0x18cd, 0x38ca, 0x17b3, 0x383e, 0x1613, 0x376d, 0x14bf, 0x366f, 0x135e, + 0x3bff, 0x101b, 0x3bff, 0x1039, 0x3bfc, 0x10c8, 0x3bf9, 0x1226, 0x3bea, 0x1428, 0x3bcf, 0x1584, 0x3b9f, 0x16c5, 0x3b54, 0x179a, 0x3af0, 0x17ce, 0x3a76, 0x1771, 0x39ea, 0x16a4, 0x3956, 0x15a7, 0x38bf, 0x14a7, 0x3829, 0x1379, 0x3735, 0x11ea, 0x362d, 0x10a1, + 0x3c00, 0x061b, 0x3c00, 0x066a, 0x3bfe, 0x081c, 0x3bfa, 0x0a4c, 0x3bed, 0x0d16, 0x3bd5, 0x0fb3, 0x3ba9, 0x114d, 0x3b63, 0x127c, 0x3b01, 0x132f, 0x3a85, 0x1344, 0x39f4, 0x12d2, 0x3957, 0x120d, 0x38b5, 0x1122, 0x3817, 0x103c, 0x3703, 0x0ed3, 0x35f0, 0x0d6d, + 0x3c00, 0x007a, 0x3c00, 0x0089, 0x3bfe, 0x011d, 0x3bfb, 0x027c, 0x3bf0, 0x04fa, 0x3bda, 0x0881, 0x3bb1, 0x0acd, 0x3b6f, 0x0c97, 0x3b10, 0x0d7b, 0x3a93, 0x0df1, 0x39fe, 0x0def, 0x3959, 0x0d8a, 0x38af, 0x0ce9, 0x3808, 0x0c31, 0x36d5, 0x0af0, 0x35b9, 0x09a3, + 0x3c00, 0x0000, 0x3c00, 0x0001, 0x3bff, 0x0015, 0x3bfb, 0x0059, 0x3bf2, 0x00fd, 0x3bdd, 0x01df, 0x3bb7, 0x031c, 0x3b79, 0x047c, 0x3b1d, 0x05d4, 0x3aa0, 0x06d5, 0x3a08, 0x075a, 0x395d, 0x075e, 0x38aa, 0x06f7, 0x37f4, 0x0648, 0x36ac, 0x0576, 0x3586, 0x049f +] ); + +let lut = null; + +function getDFGLUT() { + + if ( lut === null ) { + + lut = new DataTexture( DATA, 16, 16, RGFormat, HalfFloatType ); + lut.name = 'DFG_LUT'; + lut.minFilter = LinearFilter; + lut.magFilter = LinearFilter; + lut.wrapS = ClampToEdgeWrapping; + lut.wrapT = ClampToEdgeWrapping; + lut.generateMipmaps = false; + lut.needsUpdate = true; + + } + + return lut; + +} + /** * This renderer uses WebGL 2 to display scenes. * @@ -52972,6 +54384,7 @@ class WebGLRenderer { powerPreference = 'default', failIfMajorPerformanceCaveat = false, reversedDepthBuffer = false, + outputBufferType = UnsignedByteType, } = parameters; /** @@ -53001,6 +54414,23 @@ class WebGLRenderer { } + const _outputBufferType = outputBufferType; + + const INTEGER_FORMATS = new Set( [ + RGBAIntegerFormat, + RGIntegerFormat, + RedIntegerFormat + ] ); + + const UNSIGNED_TYPES = new Set( [ + UnsignedByteType, + UnsignedIntType, + UnsignedShortType, + UnsignedInt248Type, + UnsignedShort4444Type, + UnsignedShort5551Type + ] ); + const uintClearColor = new Uint32Array( 4 ); const intClearColor = new Int32Array( 4 ); @@ -53013,16 +54443,20 @@ class WebGLRenderer { const renderListStack = []; const renderStateStack = []; + // internal render target for non-UnsignedByteType color buffer + + let output = null; + // public properties /** - * A canvas where the renderer draws its output.This is automatically created by the renderer + * A canvas where the renderer draws its output. This is automatically created by the renderer * in the constructor (if not provided already); you just need to add it to your page like so: * ```js * document.body.appendChild( renderer.domElement ); * ``` * - * @type {DOMElement} + * @type {HTMLCanvasElement|OffscreenCanvas} */ this.domElement = canvas; @@ -53032,7 +54466,7 @@ class WebGLRenderer { * - `checkShaderErrors`: If it is `true`, defines whether material shader programs are * checked for errors during compilation and linkage process. It may be useful to disable * this check in production for performance gain. It is strongly recommended to keep these - * checks enabled during development. If the shader does not compile and link - it will not + * checks enabled during development. If the shader does not compile and link, it will not * work and associated material will not render. * - `onShaderError(gl, program, glVertexShader,glFragmentShader)`: A callback function that * can be used for custom error reporting. The callback receives the WebGL context, an instance @@ -53273,15 +54707,15 @@ class WebGLRenderer { } - } catch ( error ) { + } catch ( e ) { - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; + error( 'WebGLRenderer: ' + e.message ); + throw e; } let extensions, capabilities, state, info; - let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; + let properties, textures, environments, attributes, geometries, objects; let programCache, materials, renderLists, renderStates, clipping, shadowMap; let background, morphtargets, bufferRenderer, indexedBufferRenderer; @@ -53308,19 +54742,18 @@ class WebGLRenderer { info = new WebGLInfo( _gl ); properties = new WebGLProperties(); textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - cubemaps = new WebGLCubeMaps( _this ); - cubeuvmaps = new WebGLCubeUVMaps( _this ); + environments = new WebGLEnvironments( _this ); attributes = new WebGLAttributes( _gl ); bindingStates = new WebGLBindingStates( _gl, attributes ); geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); - objects = new WebGLObjects( _gl, geometries, attributes, info ); + objects = new WebGLObjects( _gl, geometries, attributes, bindingStates, info ); morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); clipping = new WebGLClipping( properties ); - programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); + programCache = new WebGLPrograms( _this, environments, extensions, capabilities, bindingStates, clipping ); materials = new WebGLMaterials( _this, properties ); renderLists = new WebGLRenderLists(); renderStates = new WebGLRenderStates( extensions ); - background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha ); + background = new WebGLBackground( _this, environments, state, objects, _alpha, premultipliedAlpha ); shadowMap = new WebGLShadowMap( _this, objects, capabilities ); uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state ); @@ -53408,6 +54841,14 @@ class WebGLRenderer { initGLContext(); + // initialize internal render target for non-UnsignedByteType color buffer + + if ( _outputBufferType !== UnsignedByteType ) { + + output = new WebGLOutput( _outputBufferType, canvas.width, canvas.height, depth, stencil ); + + } + // xr const xr = new WebXRManager( _this, _gl ); @@ -53512,7 +54953,7 @@ class WebGLRenderer { if ( xr.isPresenting ) { - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); + warn( 'WebGLRenderer: Can\'t change size while VR device is presenting.' ); return; } @@ -53530,6 +54971,12 @@ class WebGLRenderer { } + if ( output !== null ) { + + output.setSize( canvas.width, canvas.height ); + + } + this.setViewport( 0, 0, width, height ); }; @@ -53573,6 +55020,39 @@ class WebGLRenderer { }; + /** + * Sets the post-processing effects to be applied after rendering. + * + * @param {Array} effects - An array of post-processing effects. + */ + this.setEffects = function ( effects ) { + + if ( _outputBufferType === UnsignedByteType ) { + + console.error( 'THREE.WebGLRenderer: setEffects() requires outputBufferType set to HalfFloatType or FloatType.' ); + return; + + } + + if ( effects ) { + + for ( let i = 0; i < effects.length; i ++ ) { + + if ( effects[ i ].isOutputPass === true ) { + + console.warn( 'THREE.WebGLRenderer: OutputPass is not needed in setEffects(). Tone mapping and color space conversion are applied automatically.' ); + break; + + } + + } + + } + + output.setEffects( effects || [] ); + + }; + /** * Returns the current viewport definition. * @@ -53774,9 +55254,7 @@ class WebGLRenderer { if ( _currentRenderTarget !== null ) { const targetFormat = _currentRenderTarget.texture.format; - isIntegerFormat = targetFormat === RGBAIntegerFormat || - targetFormat === RGIntegerFormat || - targetFormat === RedIntegerFormat; + isIntegerFormat = INTEGER_FORMATS.has( targetFormat ); } @@ -53785,12 +55263,7 @@ class WebGLRenderer { if ( isIntegerFormat ) { const targetType = _currentRenderTarget.texture.type; - const isUnsignedType = targetType === UnsignedByteType || - targetType === UnsignedIntType || - targetType === UnsignedShortType || - targetType === UnsignedInt248Type || - targetType === UnsignedShort4444Type || - targetType === UnsignedShort5551Type; + const isUnsignedType = UNSIGNED_TYPES.has( targetType ); const clearColor = background.getClearColor(); const a = background.getClearAlpha(); @@ -53837,7 +55310,11 @@ class WebGLRenderer { } - _gl.clear( bits ); + if ( bits !== 0 ) { + + _gl.clear( bits ); + + } }; @@ -53882,8 +55359,7 @@ class WebGLRenderer { renderLists.dispose(); renderStates.dispose(); properties.dispose(); - cubemaps.dispose(); - cubeuvmaps.dispose(); + environments.dispose(); objects.dispose(); bindingStates.dispose(); uniformsGroups.dispose(); @@ -53904,7 +55380,7 @@ class WebGLRenderer { event.preventDefault(); - console.log( 'THREE.WebGLRenderer: Context Lost.' ); + log( 'WebGLRenderer: Context Lost.' ); _isContextLost = true; @@ -53912,7 +55388,7 @@ class WebGLRenderer { function onContextRestore( /* event */ ) { - console.log( 'THREE.WebGLRenderer: Context Restored.' ); + log( 'WebGLRenderer: Context Restored.' ); _isContextLost = false; @@ -53934,7 +55410,7 @@ class WebGLRenderer { function onContextCreationError( event ) { - console.error( 'THREE.WebGLRenderer: A WebGL context could not be created. Reason: ', event.statusMessage ); + error( 'WebGLRenderer: A WebGL context could not be created. Reason: ', event.statusMessage ); } @@ -54107,7 +55583,7 @@ class WebGLRenderer { if ( object._multiDrawInstances !== null ) { // @deprecated, r174 - warnOnce( 'THREE.WebGLRenderer: renderMultiDrawInstances has been deprecated and will be removed in r184. Append to renderMultiDraw arguments and use indirection.' ); + warnOnce( 'WebGLRenderer: renderMultiDrawInstances has been deprecated and will be removed in r184. Append to renderMultiDraw arguments and use indirection.' ); renderer.renderMultiDrawInstances( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount, object._multiDrawInstances ); } else { @@ -54383,6 +55859,13 @@ class WebGLRenderer { if ( typeof self !== 'undefined' ) animation.setContext( self ); + /** + * Applications are advised to always define the animation loop + * with this method and not manually with `requestAnimationFrame()` + * for best compatibility. + * + * @param {?onAnimationCallback} callback - The application's animation loop. + */ this.setAnimationLoop = function ( callback ) { onAnimationFrameCallback = callback; @@ -54415,13 +55898,19 @@ class WebGLRenderer { if ( camera !== undefined && camera.isCamera !== true ) { - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + error( 'WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); return; } if ( _isContextLost === true ) return; + // use internal render target for HalfFloatType color buffer (only when tone mapping is enabled) + + const isXRPresenting = xr.enabled === true && xr.isPresenting === true; + + const useOutput = output !== null && ( _currentRenderTarget === null || isXRPresenting ) && output.begin( _this, _currentRenderTarget ); + // update scene graph if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); @@ -54430,7 +55919,7 @@ class WebGLRenderer { if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); - if ( xr.enabled === true && xr.isPresenting === true ) { + if ( xr.enabled === true && xr.isPresenting === true && ( output === null || output.isCompositing() === false ) ) { if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); @@ -54502,46 +55991,52 @@ class WebGLRenderer { if ( this.info.autoReset === true ) this.info.reset(); - // render scene + // render scene (skip if first effect is a render pass - it will render the scene itself) - const opaqueObjects = currentRenderList.opaque; - const transmissiveObjects = currentRenderList.transmissive; + const skipSceneRender = useOutput && output.hasRenderPass(); - currentRenderState.setupLights(); + if ( skipSceneRender === false ) { - if ( camera.isArrayCamera ) { + const opaqueObjects = currentRenderList.opaque; + const transmissiveObjects = currentRenderList.transmissive; - const cameras = camera.cameras; + currentRenderState.setupLights(); - if ( transmissiveObjects.length > 0 ) { + if ( camera.isArrayCamera ) { - for ( let i = 0, l = cameras.length; i < l; i ++ ) { + const cameras = camera.cameras; - const camera2 = cameras[ i ]; + if ( transmissiveObjects.length > 0 ) { + + for ( let i = 0, l = cameras.length; i < l; i ++ ) { + + const camera2 = cameras[ i ]; - renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera2 ); + renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera2 ); + + } } - } + if ( _renderBackground ) background.render( scene ); - if ( _renderBackground ) background.render( scene ); + for ( let i = 0, l = cameras.length; i < l; i ++ ) { - for ( let i = 0, l = cameras.length; i < l; i ++ ) { + const camera2 = cameras[ i ]; - const camera2 = cameras[ i ]; + renderScene( currentRenderList, scene, camera2, camera2.viewport ); - renderScene( currentRenderList, scene, camera2, camera2.viewport ); + } - } + } else { - } else { + if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ); - if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ); + if ( _renderBackground ) background.render( scene ); - if ( _renderBackground ) background.render( scene ); + renderScene( currentRenderList, scene, camera ); - renderScene( currentRenderList, scene, camera ); + } } @@ -54559,6 +56054,14 @@ class WebGLRenderer { } + // copy from internal render target to canvas using fullscreen quad + + if ( useOutput ) { + + output.end( _this ); + + } + // if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); @@ -54713,9 +56216,7 @@ class WebGLRenderer { function renderScene( currentRenderList, scene, camera, viewport ) { - const opaqueObjects = currentRenderList.opaque; - const transmissiveObjects = currentRenderList.transmissive; - const transparentObjects = currentRenderList.transparent; + const { opaque: opaqueObjects, transmissive: transmissiveObjects, transparent: transparentObjects } = currentRenderList; currentRenderState.setupLightsView( camera ); @@ -54749,11 +56250,13 @@ class WebGLRenderer { if ( currentRenderState.state.transmissionRenderTarget[ camera.id ] === undefined ) { + const hasHalfFloatSupport = extensions.has( 'EXT_color_buffer_half_float' ) || extensions.has( 'EXT_color_buffer_float' ); + currentRenderState.state.transmissionRenderTarget[ camera.id ] = new WebGLRenderTarget( 1, 1, { generateMipmaps: true, - type: ( extensions.has( 'EXT_color_buffer_half_float' ) || extensions.has( 'EXT_color_buffer_float' ) ) ? HalfFloatType : UnsignedByteType, + type: hasHalfFloatSupport ? HalfFloatType : UnsignedByteType, minFilter: LinearMipmapLinearFilter, - samples: 4, + samples: capabilities.samples, stencilBuffer: stencil, resolveDepthBuffer: false, resolveStencilBuffer: false, @@ -54820,10 +56323,7 @@ class WebGLRenderer { const renderItem = transmissiveObjects[ i ]; - const object = renderItem.object; - const geometry = renderItem.geometry; - const material = renderItem.material; - const group = renderItem.group; + const { object, geometry, material, group } = renderItem; if ( material.side === DoubleSide && object.layers.test( camera.layers ) ) { @@ -54870,9 +56370,7 @@ class WebGLRenderer { const renderItem = renderList[ i ]; - const object = renderItem.object; - const geometry = renderItem.geometry; - const group = renderItem.group; + const { object, geometry, group } = renderItem; let material = renderItem.material; if ( material.allowOverride === true && overrideMaterial !== null ) { @@ -54940,9 +56438,11 @@ class WebGLRenderer { // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change - materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; + materialProperties.environment = ( material.isMeshStandardMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial ) ? scene.environment : null; materialProperties.fog = scene.fog; - materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); + + const usePMREM = material.isMeshStandardMaterial || ( material.isMeshLambertMaterial && ! material.envMap ) || ( material.isMeshPhongMaterial && ! material.envMap ); + materialProperties.envMap = environments.get( material.envMap || materialProperties.environment, usePMREM ); materialProperties.envMapRotation = ( materialProperties.environment !== null && material.envMap === null ) ? scene.environmentRotation : material.envMapRotation; if ( programs === undefined ) { @@ -55015,12 +56515,9 @@ class WebGLRenderer { uniforms.pointLightShadows.value = lights.state.pointShadow; uniforms.hemisphereLights.value = lights.state.hemi; - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; uniforms.spotLightMap.value = lights.state.spotLightMap; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms @@ -55076,9 +56573,10 @@ class WebGLRenderer { textures.resetTextureUnits(); const fog = scene.fog; - const environment = material.isMeshStandardMaterial ? scene.environment : null; + const environment = ( material.isMeshStandardMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial ) ? scene.environment : null; const colorSpace = ( _currentRenderTarget === null ) ? _this.outputColorSpace : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ); - const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const usePMREM = material.isMeshStandardMaterial || ( material.isMeshLambertMaterial && ! material.envMap ) || ( material.isMeshPhongMaterial && ! material.envMap ); + const envMap = environments.get( material.envMap || environment, usePMREM ); const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; const vertexTangents = !! geometry.attributes.tangent && ( !! material.normalMap || material.anisotropy > 0 ); const morphTargets = !! geometry.morphAttributes.position; @@ -55326,6 +56824,30 @@ class WebGLRenderer { } + // Pre-allocate texture units for shadow samplers before setting data textures + if ( materialProperties.needsLights ) { + + // Set shadow map uniforms first to ensure they get the first texture units + if ( lights.state.directionalShadowMap.length > 0 ) { + + p_uniforms.setValue( _gl, 'directionalShadowMap', lights.state.directionalShadowMap, textures ); + + } + + if ( lights.state.spotShadowMap.length > 0 ) { + + p_uniforms.setValue( _gl, 'spotShadowMap', lights.state.spotShadowMap, textures ); + + } + + if ( lights.state.pointShadowMap.length > 0 ) { + + p_uniforms.setValue( _gl, 'pointShadowMap', lights.state.pointShadowMap, textures ); + + } + + } + // skinning and morph target uniforms must be set even if material didn't change // auto-setting of texture unit for bone and morph texture must go before other textures // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures @@ -55379,19 +56901,16 @@ class WebGLRenderer { } - // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 + if ( ( material.isMeshStandardMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial ) && material.envMap === null && scene.environment !== null ) { - if ( material.isMeshGouraudMaterial && material.envMap !== null ) { - - m_uniforms.envMap.value = envMap; - - m_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? -1 : 1; + m_uniforms.envMapIntensity.value = scene.environmentIntensity; } - if ( material.isMeshStandardMaterial && material.envMap === null && scene.environment !== null ) { + // Set DFG LUT for physically-based materials + if ( m_uniforms.dfgLUT !== undefined ) { - m_uniforms.envMapIntensity.value = scene.environmentIntensity; + m_uniforms.dfgLUT.value = getDFGLUT(); } @@ -55573,7 +57092,6 @@ class WebGLRenderer { _currentActiveCubeFace = activeCubeFace; _currentActiveMipmapLevel = activeMipmapLevel; - let useDefaultFramebuffer = true; let framebuffer = null; let isCube = false; let isRenderTarget3D = false; @@ -55584,9 +57102,21 @@ class WebGLRenderer { if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { - // We need to make sure to rebind the framebuffer. - state.bindFramebuffer( _gl.FRAMEBUFFER, null ); - useDefaultFramebuffer = false; + // Externally-managed framebuffer (e.g. XR) + // Bind to the stored framebuffer (may be null for default, or a WebGLFramebuffer) + state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); + + _currentViewport.copy( renderTarget.viewport ); + _currentScissor.copy( renderTarget.scissor ); + _currentScissorTest = renderTarget.scissorTest; + + state.viewport( _currentViewport ); + state.scissor( _currentScissor ); + state.setScissorTest( _currentScissorTest ); + + _currentMaterialId = -1; + + return; } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { @@ -55685,7 +57215,7 @@ class WebGLRenderer { const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - if ( framebufferBound && useDefaultFramebuffer ) { + if ( framebufferBound ) { state.drawBuffers( renderTarget, framebuffer ); @@ -55741,7 +57271,7 @@ class WebGLRenderer { if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + error( 'WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); return; } @@ -55764,16 +57294,20 @@ class WebGLRenderer { const textureFormat = texture.format; const textureType = texture.type; + // when using MRT, select the correct color buffer for the subsequent read command + + if ( renderTarget.textures.length > 1 ) _gl.readBuffer( _gl.COLOR_ATTACHMENT0 + textureIndex ); + if ( ! capabilities.textureFormatReadable( textureFormat ) ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + error( 'WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); return; } if ( ! capabilities.textureTypeReadable( textureType ) ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + error( 'WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); return; } @@ -55782,10 +57316,6 @@ class WebGLRenderer { if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - // when using MRT, select the correct color buffer for the subsequent read command - - if ( renderTarget.textures.length > 1 ) _gl.readBuffer( _gl.COLOR_ATTACHMENT0 + textureIndex ); - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); } @@ -55846,6 +57376,11 @@ class WebGLRenderer { const textureFormat = texture.format; const textureType = texture.type; + // when using MRT, select the correct color buffer for the subsequent read command + + if ( renderTarget.textures.length > 1 ) _gl.readBuffer( _gl.COLOR_ATTACHMENT0 + textureIndex ); + + if ( ! capabilities.textureFormatReadable( textureFormat ) ) { throw new Error( 'THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.' ); @@ -55862,10 +57397,6 @@ class WebGLRenderer { _gl.bindBuffer( _gl.PIXEL_PACK_BUFFER, glBuffer ); _gl.bufferData( _gl.PIXEL_PACK_BUFFER, buffer.byteLength, _gl.STREAM_READ ); - // when using MRT, select the correct color buffer for the subsequent read command - - if ( renderTarget.textures.length > 1 ) _gl.readBuffer( _gl.COLOR_ATTACHMENT0 + textureIndex ); - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), 0 ); // reset the frame buffer to the currently set buffer before waiting @@ -55935,27 +57466,9 @@ class WebGLRenderer { * @param {?(Box2|Box3)} [srcRegion=null] - A bounding box which describes the source region. Can be two or three-dimensional. * @param {?(Vector2|Vector3)} [dstPosition=null] - A vector that represents the origin of the destination region. Can be two or three-dimensional. * @param {number} [srcLevel=0] - The source mipmap level to copy. - * @param {?number} [dstLevel=null] - The destination mipmap level. + * @param {?number} [dstLevel=0] - The destination mipmap level. */ - this.copyTextureToTexture = function ( srcTexture, dstTexture, srcRegion = null, dstPosition = null, srcLevel = 0, dstLevel = null ) { - - // support the previous signature with just a single dst mipmap level - if ( dstLevel === null ) { - - if ( srcLevel !== 0 ) { - - // @deprecated, r171 - warnOnce( 'WebGLRenderer: copyTextureToTexture function signature has changed to support src and dst mipmap levels.' ); - dstLevel = srcLevel; - srcLevel = 0; - - } else { - - dstLevel = 0; - - } - - } + this.copyTextureToTexture = function ( srcTexture, dstTexture, srcRegion = null, dstPosition = null, srcLevel = 0, dstLevel = 0 ) { // gather the necessary dimensions to copy let width, height, depth, minX, minY, minZ; @@ -56620,89 +58133,6 @@ class InstancedBufferAttribute extends BufferAttribute { } -/** - * Creates a texture directly from raw buffer data. - * - * The interpretation of the data depends on type and format: If the type is - * `UnsignedByteType`, a `Uint8Array` will be useful for addressing the - * texel data. If the format is `RGBAFormat`, data needs four values for - * one texel; Red, Green, Blue and Alpha (typically the opacity). - * - * @augments Texture - */ -class DataTexture extends Texture { - - /** - * Constructs a new data texture. - * - * @param {?TypedArray} [data=null] - The buffer data. - * @param {number} [width=1] - The width of the texture. - * @param {number} [height=1] - The height of the texture. - * @param {number} [format=RGBAFormat] - The texture format. - * @param {number} [type=UnsignedByteType] - The texture type. - * @param {number} [mapping=Texture.DEFAULT_MAPPING] - The texture mapping. - * @param {number} [wrapS=ClampToEdgeWrapping] - The wrapS value. - * @param {number} [wrapT=ClampToEdgeWrapping] - The wrapT value. - * @param {number} [magFilter=NearestFilter] - The mag filter value. - * @param {number} [minFilter=NearestFilter] - The min filter value. - * @param {number} [anisotropy=Texture.DEFAULT_ANISOTROPY] - The anisotropy value. - * @param {string} [colorSpace=NoColorSpace] - The color space. - */ - constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace ) { - - super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); - - /** - * This flag can be used for type testing. - * - * @type {boolean} - * @readonly - * @default true - */ - this.isDataTexture = true; - - /** - * The image definition of a data texture. - * - * @type {{data:TypedArray,width:number,height:number}} - */ - this.image = { data: data, width: width, height: height }; - - /** - * Whether to generate mipmaps (if possible) for a texture. - * - * Overwritten and set to `false` by default. - * - * @type {boolean} - * @default false - */ - this.generateMipmaps = false; - - /** - * If set to `true`, the texture is flipped along the vertical axis when - * uploaded to the GPU. - * - * Overwritten and set to `false` by default. - * - * @type {boolean} - * @default false - */ - this.flipY = false; - - /** - * Specifies the alignment requirements for the start of each pixel row in memory. - * - * Overwritten and set to `1` by default. - * - * @type {boolean} - * @default 1 - */ - this.unpackAlignment = 1; - - } - -} - const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); @@ -56753,6 +58183,15 @@ class InstancedMesh extends Mesh { */ this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); + /** + * Represents the local transformation of all instances of the previous frame. + * Required for computing velocity. Maintained in {@link InstanceNode}. + * + * @type {?InstancedBufferAttribute} + * @default null + */ + this.previousInstanceMatrix = null; + /** * Represents the color of all instances. You have to set its * {@link BufferAttribute#needsUpdate} flag to true if you modify instanced data @@ -56882,6 +58321,8 @@ class InstancedMesh extends Mesh { this.instanceMatrix.copy( source.instanceMatrix ); + if ( source.previousInstanceMatrix !== null ) this.previousInstanceMatrix = source.previousInstanceMatrix.clone(); + if ( source.morphTexture !== null ) this.morphTexture = source.morphTexture.clone(); if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); @@ -57280,7 +58721,7 @@ let Line$1 = class Line extends Object3D { * morph targets name, the value its attribute index. This member is `undefined` * by default and only set when morph targets are detected in the geometry. * - * @type {Object|undefined} + * @type {Object|undefined} * @default undefined */ this.morphTargetDictionary = undefined; @@ -57342,7 +58783,7 @@ let Line$1 = class Line extends Object3D { } else { - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + warn( 'Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); } @@ -57583,7 +59024,7 @@ class LineSegments extends Line$1 { } else { - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + warn( 'LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); } @@ -57680,7 +59121,7 @@ class PointsMaterial extends Material { /** * Defines the size of the points in pixels. * - * Might be capped if the value exceeds hardware dependent parameters like [gl.ALIASED_POINT_SIZE_RANGE]{@link https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getParamete}. + * Might be capped if the value exceeds hardware dependent parameters like [gl.ALIASED_POINT_SIZE_RANGE](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getParamete). * * @type {number} * @default 1 @@ -57781,7 +59222,7 @@ class Points extends Object3D { * morph targets name, the value its attribute index. This member is `undefined` * by default and only set when morph targets are detected in the geometry. * - * @type {Object|undefined} + * @type {Object|undefined} * @default undefined */ this.morphTargetDictionary = undefined; @@ -58005,6 +59446,7 @@ class CanvasTexture extends Texture { * ``` * * @augments BufferGeometry + * @demo scenes/geometry-browser.html#CircleGeometry */ class CircleGeometry extends BufferGeometry { @@ -58186,7 +59628,7 @@ class Curve { */ getPoint( /* t, optionalTarget */ ) { - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); + warn( 'Curve: .getPoint() not implemented.' ); } @@ -59244,7 +60686,13 @@ class CatmullRomCurve3 extends Curve { } -// Bezier Curves formulas obtained from: https://en.wikipedia.org/wiki/B%C3%A9zier_curve +/** + * Interpolations contains spline and Bézier functions internally used by concrete curve classes. + * + * Bezier Curves formulas obtained from: https://en.wikipedia.org/wiki/B%C3%A9zier_curve + * + * @module Interpolations + */ /** * Computes a point on a Catmull-Rom spline. @@ -61026,8 +62474,8 @@ class Shape extends Path$1 { } /* eslint-disable */ -// copy of mapbox/earcut version 3.0.1 -// https://github.com/mapbox/earcut/tree/v3.0.1 +// copy of mapbox/earcut version 3.0.2 +// https://github.com/mapbox/earcut/tree/v3.0.2 function earcut(data, holeIndices, dim = 2) { @@ -61044,10 +62492,10 @@ function earcut(data, holeIndices, dim = 2) { // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox if (data.length > 80 * dim) { - minX = Infinity; - minY = Infinity; - let maxX = -Infinity; - let maxY = -Infinity; + minX = data[0]; + minY = data[1]; + let maxX = minX; + let maxY = minY; for (let i = dim; i < outerLen; i += dim) { const x = data[i]; @@ -61323,7 +62771,7 @@ function compareXYSlope(a, b) { return result; } -// find a bridge between vertices that connects hole with an outer ring and and link it +// find a bridge between vertices that connects hole with an outer ring and link it function eliminateHole(hole, outerNode) { const bridge = findHoleBridge(hole, outerNode); if (!bridge) { @@ -61661,6 +63109,12 @@ function signedArea(data, start, end, dim) { return sum; } +/** + * An implementation of the earcut polygon triangulation algorithm. + * The code is a port of [mapbox/earcut](https://github.com/mapbox/earcut). + * + * @see https://github.com/mapbox/earcut + */ class Earcut { /** @@ -61811,6 +63265,7 @@ function addContour( vertices, contour ) { * ``` * * @augments BufferGeometry + * @demo scenes/geometry-browser.html#ExtrudeGeometry */ class ExtrudeGeometry extends BufferGeometry { @@ -61895,11 +63350,11 @@ class ExtrudeGeometry extends BufferGeometry { // SETUP TNB variables - // TODO1 - have a .isClosed in spline? + const isClosed = extrudePath.isCatmullRomCurve3 ? extrudePath.closed : false; - splineTube = extrudePath.computeFrenetFrames( steps, false ); + splineTube = extrudePath.computeFrenetFrames( steps, isClosed ); - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + // log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); binormal = new Vector3(); normal = new Vector3(); @@ -62004,7 +63459,7 @@ class ExtrudeGeometry extends BufferGeometry { function scalePt2( pt, vec, size ) { - if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); + if ( ! vec ) error( 'ExtrudeGeometry: vec does not exist' ); return pt.clone().addScaledVector( vec, size ); @@ -62119,14 +63574,14 @@ class ExtrudeGeometry extends BufferGeometry { if ( direction_eq ) { - // console.log("Warning: lines are a straight sequence"); + // log("Warning: lines are a straight sequence"); v_trans_x = - v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt( v_prev_lensq ); } else { - // console.log("Warning: lines are a straight spike"); + // log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt( v_prev_lensq / 2 ); @@ -62148,7 +63603,7 @@ class ExtrudeGeometry extends BufferGeometry { if ( k === il ) k = 0; // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) + // log('i,j,k', i, j , k) contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); @@ -62445,7 +63900,7 @@ class ExtrudeGeometry extends BufferGeometry { let k = i - 1; if ( k < 0 ) k = contour.length - 1; - //console.log('b', i,j, i-1, k,vertices.length); + //log('b', i,j, i-1, k,vertices.length); for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { @@ -62685,6 +64140,7 @@ function toJSON$1( shapes, options, data ) { * ``` * * @augments BufferGeometry + * @demo scenes/geometry-browser.html#SphereGeometry */ class SphereGeometry extends BufferGeometry { @@ -62847,9 +64303,9 @@ class SphereGeometry extends BufferGeometry { * A standard physically based material, using Metallic-Roughness workflow. * * Physically based rendering (PBR) has recently become the standard in many - * 3D applications, such as [Unity]{@link https://blogs.unity3d.com/2014/10/29/physically-based-shading-in-unity-5-a-primer/}, - * [Unreal]{@link https://docs.unrealengine.com/latest/INT/Engine/Rendering/Materials/PhysicallyBased/} and - * [3D Studio Max]{@link http://area.autodesk.com/blogs/the-3ds-max-blog/what039s-new-for-rendering-in-3ds-max-2017}. + * 3D applications, such as [Unity](https://blogs.unity3d.com/2014/10/29/physically-based-shading-in-unity-5-a-primer/), + * [Unreal](https://docs.unrealengine.com/latest/INT/Engine/Rendering/Materials/PhysicallyBased/) and + * [3D Studio Max](http://area.autodesk.com/blogs/the-3ds-max-blog/what039s-new-for-rendering-in-3ds-max-2017). * * This approach differs from older approaches in that instead of using * approximations for the way in which light interacts with a surface, a @@ -62865,16 +64321,17 @@ class SphereGeometry extends BufferGeometry { * Note that for best results you should always specify an environment map when using this material. * * For a non-technical introduction to the concept of PBR and how to set up a - * PBR material, check out these articles by the people at [marmoset]{@link https://www.marmoset.co}: + * PBR material, check out these articles by the people at [marmoset](https://www.marmoset.co): * - * - [Basic Theory of Physically Based Rendering]{@link https://www.marmoset.co/posts/basic-theory-of-physically-based-rendering/} - * - [Physically Based Rendering and You Can Too]{@link https://www.marmoset.co/posts/physically-based-rendering-and-you-can-too/} + * - [Basic Theory of Physically Based Rendering](https://www.marmoset.co/posts/basic-theory-of-physically-based-rendering/) + * - [Physically Based Rendering and You Can Too](https://www.marmoset.co/posts/physically-based-rendering-and-you-can-too/) * * Technical details of the approach used in three.js (and most other PBR systems) can be found is this - * [paper from Disney]{@link https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf} + * [paper from Disney](https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf) * (pdf), by Brent Burley. * * @augments Material + * @demo scenes/material-browser.html#MeshStandardMaterial */ class MeshStandardMaterial extends Material { @@ -63284,6 +64741,7 @@ class MeshStandardMaterial extends Material { * best results, always specify an environment map when using this material. * * @augments MeshStandardMaterial + * @demo scenes/material-browser.html#MeshPhysicalMaterial */ class MeshPhysicalMaterial extends MeshStandardMaterial { @@ -63580,7 +65038,7 @@ class MeshPhysicalMaterial extends MeshStandardMaterial { } /** - * The anisotropy strength. + * The anisotropy strength, from `0.0` to `1.0`. * * @type {number} * @default 0 @@ -63792,7 +65250,7 @@ class MeshPhysicalMaterial extends MeshStandardMaterial { /** * A material for shiny surfaces with specular highlights. * - * The material uses a non-physically based [Blinn-Phong]{@link https://en.wikipedia.org/wiki/Blinn-Phong_shading_model} + * The material uses a non-physically based [Blinn-Phong](https://en.wikipedia.org/wiki/Blinn-Phong_shading_model) * model for calculating reflectance. Unlike the Lambertian model used in the * {@link MeshLambertMaterial} this can simulate shiny surfaces with specular * highlights (such as varnished wood). `MeshPhongMaterial` uses per-fragment shading. @@ -63802,6 +65260,7 @@ class MeshPhysicalMaterial extends MeshStandardMaterial { * some graphical accuracy. * * @augments Material + * @demo scenes/material-browser.html#MeshPhongMaterial */ class MeshPhongMaterial extends Material { @@ -64068,6 +65527,14 @@ class MeshPhongMaterial extends Material { */ this.reflectivity = 1; + /** + * Scales the effect of the environment map by multiplying its color. + * + * @type {number} + * @default 1 + */ + this.envMapIntensity = 1.0; + /** * The index of refraction (IOR) of air (approximately 1) divided by the * index of refraction of the material. It is used with environment mapping @@ -64176,6 +65643,7 @@ class MeshPhongMaterial extends Material { this.envMapRotation.copy( source.envMapRotation ); this.combine = source.combine; this.reflectivity = source.reflectivity; + this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; @@ -64197,6 +65665,7 @@ class MeshPhongMaterial extends Material { * A material implementing toon shading. * * @augments Material + * @demo scenes/material-browser.html#MeshToonMaterial */ class MeshToonMaterial extends Material { @@ -64511,6 +65980,7 @@ class MeshToonMaterial extends Material { * A material that maps the normal vectors to RGB colors. * * @augments Material + * @demo scenes/material-browser.html#MeshNormalMaterial */ class MeshNormalMaterial extends Material { @@ -64679,7 +66149,7 @@ class MeshNormalMaterial extends Material { /** * A material for non-shiny surfaces, without specular highlights. * - * The material uses a non-physically based [Lambertian]{@link https://en.wikipedia.org/wiki/Lambertian_reflectance} + * The material uses a non-physically based [Lambertian](https://en.wikipedia.org/wiki/Lambertian_reflectance) * model for calculating reflectance. This can simulate some surfaces (such * as untreated wood or stone) well, but cannot simulate shiny surfaces with * specular highlights (such as varnished wood). `MeshLambertMaterial` uses per-fragment @@ -64691,6 +66161,7 @@ class MeshNormalMaterial extends Material { * {@link MeshPhysicalMaterial}, at the cost of some graphical accuracy. * * @augments Material + * @demo scenes/material-browser.html#MeshLambertMaterial */ class MeshLambertMaterial extends Material { @@ -64938,6 +66409,14 @@ class MeshLambertMaterial extends Material { */ this.reflectivity = 1; + /** + * Scales the effect of the environment map by multiplying its color. + * + * @type {number} + * @default 1 + */ + this.envMapIntensity = 1.0; + /** * The index of refraction (IOR) of air (approximately 1) divided by the * index of refraction of the material. It is used with environment mapping @@ -65044,6 +66523,7 @@ class MeshLambertMaterial extends Material { this.envMapRotation.copy( source.envMapRotation ); this.combine = source.combine; this.reflectivity = source.reflectivity; + this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; @@ -65071,6 +66551,7 @@ class MeshLambertMaterial extends Material { * shadows. * * @augments Material + * @demo scenes/material-browser.html#MeshMatcapMaterial */ class MeshMatcapMaterial extends Material { @@ -65222,6 +66703,24 @@ class MeshMatcapMaterial extends Material { */ this.alphaMap = null; + /** + * Renders the geometry as a wireframe. + * + * @type {boolean} + * @default false + */ + this.wireframe = false; + + /** + * Controls the thickness of the wireframe. + * + * Can only be used with {@link SVGRenderer}. + * + * @type {number} + * @default 1 + */ + this.wireframeLinewidth = 1; + /** * Whether the material is rendered with flat shading or not. * @@ -65268,6 +66767,9 @@ class MeshMatcapMaterial extends Material { this.alphaMap = source.alphaMap; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.flatShading = source.flatShading; this.fog = source.fog; @@ -65398,7 +66900,9 @@ const Cache = { if ( this.enabled === false ) return; - // console.log( 'THREE.Cache', 'Adding key:', key ); + if ( isBlobURL( key ) ) return; + + // log( 'Cache', 'Adding key:', key ); this.files[ key ] = file; @@ -65415,7 +66919,9 @@ const Cache = { if ( this.enabled === false ) return; - // console.log( 'THREE.Cache', 'Checking key:', key ); + if ( isBlobURL( key ) ) return; + + // log( 'Cache', 'Checking key:', key ); return this.files[ key ]; @@ -65446,6 +66952,31 @@ const Cache = { }; +/** + * Returns true if the given cache key contains the blob: scheme. + * + * @private + * @param {string} key - The cache key. + * @return {boolean} Whether the given cache key contains the blob: scheme or not. + */ +function isBlobURL( key ) { + + try { + + const urlString = key.slice( key.indexOf( ':' ) + 1 ); // remove type identifier + + const url = new URL( urlString ); + return url.protocol === 'blob:'; + + } catch ( e ) { + + // If the string is not a valid URL, it throws an error + return false; + + } + +} + /** * Handles and keeps track of loaded and pending data. A default global * instance of this class is created and used by loaders if not supplied @@ -65520,9 +67051,10 @@ class LoadingManager { /** * Used for aborting ongoing requests in loaders using this manager. * - * @type {AbortController} + * @private + * @type {AbortController | null} */ - this.abortController = new AbortController(); + this._abortController = null; /** * This should be called by any loader using the manager when the loader @@ -65733,8 +67265,9 @@ class LoadingManager { */ this.abort = function () { + this.abortController.abort(); - this.abortController = new AbortController(); + this._abortController = null; return this; @@ -65742,6 +67275,26 @@ class LoadingManager { } + // TODO: Revert this back to a single member variable once this issue has been fixed + // https://github.com/cloudflare/workerd/issues/3657 + + /** + * Used for aborting ongoing requests in loaders using this manager. + * + * @type {AbortController} + */ + get abortController() { + + if ( ! this._abortController ) { + + this._abortController = new AbortController(); + + } + + return this._abortController; + + } + } /** @@ -65806,13 +67359,19 @@ class Loader { this.resourcePath = ''; /** - * The [request header]{@link https://developer.mozilla.org/en-US/docs/Glossary/Request_header} + * The [request header](https://developer.mozilla.org/en-US/docs/Glossary/Request_header) * used in HTTP request. * * @type {Object} */ this.requestHeader = {}; + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); + + } + } /** @@ -65871,7 +67430,7 @@ class Loader { /** * Whether the XMLHttpRequest uses credentials such as cookies, authorization - * headers or TLS client certificates, see [XMLHttpRequest.withCredentials]{@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials}. + * headers or TLS client certificates, see [XMLHttpRequest.withCredentials](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials). * * Note: This setting has no effect if you are loading files locally or from the same domain. * @@ -65914,7 +67473,7 @@ class Loader { /** * Sets the given request header. * - * @param {Object} requestHeader - A [request header]{@link https://developer.mozilla.org/en-US/docs/Glossary/Request_header} + * @param {Object} requestHeader - A [request header](https://developer.mozilla.org/en-US/docs/Glossary/Request_header) * for configuring the HTTP request. * @return {Loader} A reference to this instance. */ @@ -65976,7 +67535,7 @@ const _loading = new WeakMap(); * ``` * Please note that `ImageLoader` has dropped support for progress * events in `r84`. For an `ImageLoader` that supports progress events, see - * [this thread]{@link https://github.com/mrdoob/three.js/issues/10439#issuecomment-275785639}. + * [this thread](https://github.com/mrdoob/three.js/issues/10439#issuecomment-275785639). * * @augments Loader */ @@ -66139,7 +67698,7 @@ class ImageLoader extends Loader { * ``` * Please note that `TextureLoader` has dropped support for progress * events in `r84`. For a `TextureLoader` that supports progress events, see - * [this thread]{@link https://github.com/mrdoob/three.js/issues/10439#issuecomment-293260145}. + * [this thread](https://github.com/mrdoob/three.js/issues/10439#issuecomment-293260145). * * @augments Loader */ @@ -66248,7 +67807,7 @@ class Light extends Object3D { */ dispose() { - // Empty here in base class; some subclasses override. + this.dispatchEvent( { type: 'dispose' } ); } @@ -66270,16 +67829,6 @@ class Light extends Object3D { data.object.color = this.color.getHex(); data.object.intensity = this.intensity; - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - if ( this.target !== undefined ) data.object.target = this.target.uuid; - return data; } @@ -66345,11 +67894,21 @@ class HemisphereLight extends Light { } + toJSON( meta ) { + + const data = super.toJSON( meta ); + + data.object.groundColor = this.groundColor.getHex(); + + return data; + + } + } -const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); -const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); -const _lookTarget$1 = /*@__PURE__*/ new Vector3(); +const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); +const _lightPositionWorld = /*@__PURE__*/ new Vector3(); +const _lookTarget = /*@__PURE__*/ new Vector3(); /** * Abstract base class for light shadow classes. These classes @@ -66394,6 +67953,16 @@ class LightShadow { */ this.bias = 0; + /** + * A node version of `bias`. Only supported with `WebGPURenderer`. + * + * If a bias node is defined, `bias` has no effect. + * + * @type {?Node} + * @default null + */ + this.biasNode = null; + /** * Defines how much the position used to query the shadow map is offset along * the object normal. The default is `0`. Increasing this value can be used to @@ -66411,9 +67980,6 @@ class LightShadow { * map size will allow for a higher value to be used here before these effects * become visible. * - * The property has no effect when the shadow map type is `PCFSoftShadowMap` and - * and it is recommended to increase softness by decreasing the shadow map size instead. - * * The property has no effect when the shadow map type is `BasicShadowMap`. * * @type {number} @@ -66538,22 +68104,22 @@ class LightShadow { const shadowCamera = this.camera; const shadowMatrix = this.matrix; - _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( _lightPositionWorld$1 ); + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld ); - _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _lookTarget$1 ); + _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget ); shadowCamera.updateMatrixWorld(); - _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromProjectionMatrix( _projScreenMatrix$1, shadowCamera.coordinateSystem, shadowCamera.reversedDepth ); + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix, shadowCamera.coordinateSystem, shadowCamera.reversedDepth ); - if ( shadowCamera.reversedDepth ) { + if ( shadowCamera.coordinateSystem === WebGPUCoordinateSystem || shadowCamera.reversedDepth ) { shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0, 0.0, // Identity Z (preserving the correct [0, 1] range from the projection matrix) 0.0, 0.0, 0.0, 1.0 ); @@ -66568,7 +68134,7 @@ class LightShadow { } - shadowMatrix.multiply( _projScreenMatrix$1 ); + shadowMatrix.multiply( _projScreenMatrix ); } @@ -66637,6 +68203,8 @@ class LightShadow { this.mapSize.copy( source.mapSize ); + this.biasNode = source.biasNode; + return this; } @@ -66782,6 +68350,8 @@ class DirectionalLight extends Light { dispose() { + super.dispose(); + this.shadow.dispose(); } @@ -66797,6 +68367,17 @@ class DirectionalLight extends Light { } + toJSON( meta ) { + + const data = super.toJSON( meta ); + + data.object.shadow = this.shadow.toJSON(); + data.object.target = this.target.uuid; + + return data; + + } + } /** @@ -66873,7 +68454,7 @@ class Raycaster { this.near = near; /** - * All results returned are further away than near. Near can't be negative. + * All results returned are closer than far. Far can't be lower than near. * * @type {number} * @default Infinity @@ -66967,7 +68548,7 @@ class Raycaster { } else { - console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); + error( 'Raycaster: Unsupported camera type: ' + camera.type ); } @@ -67002,7 +68583,7 @@ class Raycaster { * @property {Object3D} object - The 3D object that has been intersected. * @property {Vector2} uv - U,V coordinates at point of intersection. * @property {Vector2} uv1 - Second set of U,V coordinates at point of intersection. - * @property {Vector3} uv1 - Interpolated normal vector at point of intersection. + * @property {Vector3} normal - Interpolated normal vector at point of intersection. * @property {number} instanceId - The index number of the instance where the ray * intersects the {@link InstancedMesh}. */ @@ -67095,135 +68676,191 @@ function intersect( object, raycaster, intersects, recursive ) { } /** - * Class for keeping track of time. + * This class is an alternative to {@link Clock} with a different API design and behavior. + * The goal is to avoid the conceptual flaws that became apparent in `Clock` over time. + * + * - `Timer` has an `update()` method that updates its internal state. That makes it possible to + * call `getDelta()` and `getElapsed()` multiple times per simulation step without getting different values. + * - The class can make use of the Page Visibility API to avoid large time delta values when the app + * is inactive (e.g. tab switched or browser hidden). + * + * ```js + * const timer = new Timer(); + * timer.connect( document ); // use Page Visibility API + * ``` */ -class Clock { +class Timer { /** - * Constructs a new clock. + * Constructs a new timer. + */ + constructor() { + + this._previousTime = 0; + this._currentTime = 0; + this._startTime = performance.now(); + + this._delta = 0; + this._elapsed = 0; + + this._timescale = 1; + + this._document = null; + this._pageVisibilityHandler = null; + + } + + /** + * Connect the timer to the given document.Calling this method is not mandatory to + * use the timer but enables the usage of the Page Visibility API to avoid large time + * delta values. * - * @param {boolean} [autoStart=true] - Whether to automatically start the clock when - * `getDelta()` is called for the first time. + * @param {Document} document - The document. */ - constructor( autoStart = true ) { + connect( document ) { - /** - * If set to `true`, the clock starts automatically when `getDelta()` is called - * for the first time. - * - * @type {boolean} - * @default true - */ - this.autoStart = autoStart; + this._document = document; - /** - * Holds the time at which the clock's `start()` method was last called. - * - * @type {number} - * @default 0 - */ - this.startTime = 0; + // use Page Visibility API to avoid large time delta values - /** - * Holds the time at which the clock's `start()`, `getElapsedTime()` or - * `getDelta()` methods were last called. - * - * @type {number} - * @default 0 - */ - this.oldTime = 0; + if ( document.hidden !== undefined ) { - /** - * Keeps track of the total time that the clock has been running. - * - * @type {number} - * @default 0 - */ - this.elapsedTime = 0; + this._pageVisibilityHandler = handleVisibilityChange.bind( this ); - /** - * Whether the clock is running or not. - * - * @type {boolean} - * @default true - */ - this.running = false; + document.addEventListener( 'visibilitychange', this._pageVisibilityHandler, false ); + + } } /** - * Starts the clock. When `autoStart` is set to `true`, the method is automatically - * called by the class. + * Disconnects the timer from the DOM and also disables the usage of the Page Visibility API. */ - start() { + disconnect() { - this.startTime = performance.now(); + if ( this._pageVisibilityHandler !== null ) { - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; + this._document.removeEventListener( 'visibilitychange', this._pageVisibilityHandler ); + this._pageVisibilityHandler = null; + + } + + this._document = null; } /** - * Stops the clock. + * Returns the time delta in seconds. + * + * @return {number} The time delta in second. */ - stop() { + getDelta() { - this.getElapsedTime(); - this.running = false; - this.autoStart = false; + return this._delta / 1000; } /** * Returns the elapsed time in seconds. * - * @return {number} The elapsed time. + * @return {number} The elapsed time in second. */ - getElapsedTime() { + getElapsed() { - this.getDelta(); - return this.elapsedTime; + return this._elapsed / 1000; } /** - * Returns the delta time in seconds. + * Returns the timescale. * - * @return {number} The delta time. + * @return {number} The timescale. */ - getDelta() { + getTimescale() { - let diff = 0; + return this._timescale; - if ( this.autoStart && ! this.running ) { + } - this.start(); - return 0; + /** + * Sets the given timescale which scale the time delta computation + * in `update()`. + * + * @param {number} timescale - The timescale to set. + * @return {Timer} A reference to this timer. + */ + setTimescale( timescale ) { - } + this._timescale = timescale; - if ( this.running ) { + return this; - const newTime = performance.now(); + } + + /** + * Resets the time computation for the current simulation step. + * + * @return {Timer} A reference to this timer. + */ + reset() { + + this._currentTime = performance.now() - this._startTime; + + return this; + + } - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; + /** + * Can be used to free all internal resources. Usually called when + * the timer instance isn't required anymore. + */ + dispose() { - this.elapsedTime += diff; + this.disconnect(); + + } + + /** + * Updates the internal state of the timer. This method should be called + * once per simulation step and before you perform queries against the timer + * (e.g. via `getDelta()`). + * + * @param {number} timestamp - The current time in milliseconds. Can be obtained + * from the `requestAnimationFrame` callback argument. If not provided, the current + * time will be determined with `performance.now`. + * @return {Timer} A reference to this timer. + */ + update( timestamp ) { + + if ( this._pageVisibilityHandler !== null && this._document.hidden === true ) { + + this._delta = 0; + + } else { + + this._previousTime = this._currentTime; + this._currentTime = ( timestamp !== undefined ? timestamp : performance.now() ) - this._startTime; + + this._delta = ( this._currentTime - this._previousTime ) * this._timescale; + this._elapsed += this._delta; // _elapsed is the accumulation of all previous deltas } - return diff; + return this; } } +function handleVisibilityChange() { + + if ( this._document.hidden === false ) this.reset(); + +} + /** * This class can be used to represent points in 3D space as - * [Spherical coordinates]{@link https://en.wikipedia.org/wiki/Spherical_coordinate_system}. + * [Spherical coordinates](https://en.wikipedia.org/wiki/Spherical_coordinate_system). */ class Spherical { @@ -68026,12 +69663,10 @@ class Line3 { } - c1.copy( p1 ).add( _d1.multiplyScalar( s ) ); - c2.copy( p2 ).add( _d2.multiplyScalar( t ) ); + c1.copy( p1 ).addScaledVector( _d1, s ); + c2.copy( p2 ).addScaledVector( _d2, t ); - c1.sub( c2 ); - - return c1.dot( c1 ); + return c1.distanceToSquared( c2 ); } @@ -68396,7 +70031,7 @@ class ShapePath { let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); holesFirst = isCCW ? ! holesFirst : holesFirst; - // console.log("Holes first", holesFirst); + // log("Holes first", holesFirst); const betterShapeHoles = []; const newShapes = []; @@ -68424,13 +70059,13 @@ class ShapePath { if ( holesFirst ) mainIdx ++; newShapeHoles[ mainIdx ] = []; - //console.log('cw', i); + //log('cw', i); } else { newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); - //console.log('ccw', i); + //log('ccw', i); } @@ -68515,7 +70150,7 @@ class ShapePath { } - //console.log("shape", shapes); + //log("shape", shapes); return shapes; @@ -68535,7 +70170,7 @@ class Controls extends EventDispatcher { * Constructs a new controls instance. * * @param {Object3D} object - The object that is managed by the controls. - * @param {?HTMLDOMElement} domElement - The HTML element used for event listeners. + * @param {?HTMLElement} domElement - The HTML element used for event listeners. */ constructor( object, domElement = null ) { @@ -68551,7 +70186,7 @@ class Controls extends EventDispatcher { /** * The HTML element used for event listeners. * - * @type {?HTMLDOMElement} + * @type {?HTMLElement} * @default null */ this.domElement = domElement; @@ -68601,13 +70236,13 @@ class Controls extends EventDispatcher { * Connects the controls to the DOM. This method has so called "side effects" since * it adds the module's event listeners to the DOM. * - * @param {HTMLDOMElement} element - The DOM element to connect to. + * @param {HTMLElement} element - The DOM element to connect to. */ connect( element ) { if ( element === undefined ) { - console.warn( 'THREE.Controls: connect() now requires an element.' ); // @deprecated, the warning can be removed with r185 + warn( 'Controls: connect() now requires an element.' ); // @deprecated, the warning can be removed with r185 return; } @@ -68677,7 +70312,7 @@ if ( typeof window !== 'undefined' ) { * * See the {@link FontLoader} page for additional details. * - * `TextGeometry` uses [typeface.json]{@link http://gero3.github.io/facetype.js/} generated fonts. + * `TextGeometry` uses [typeface.json](http://gero3.github.io/facetype.js/) generated fonts. * Some existing fonts can be found located in `/examples/fonts`. * * ```js @@ -68712,7 +70347,7 @@ class TextGeometry extends ExtrudeGeometry { } else { - const shapes = font.generateShapes( text, parameters.size ); + const shapes = font.generateShapes( text, parameters.size, parameters.direction ); // defaults @@ -68769,12 +70404,13 @@ class Font { * * @param {string} text - The text. * @param {number} [size=100] - The text size. + * @param {string} [direction='ltr'] - Char direction: ltr(left to right), rtl(right to left) & tb(top bottom). * @return {Array} An array of shapes representing the text. */ - generateShapes( text, size = 100 ) { + generateShapes( text, size = 100, direction = 'ltr' ) { const shapes = []; - const paths = createPaths( text, size, this.data ); + const paths = createPaths( text, size, this.data, direction ); for ( let p = 0, pl = paths.length; p < pl; p ++ ) { @@ -68788,7 +70424,7 @@ class Font { } -function createPaths( text, size, data ) { +function createPaths( text, size, data, direction ) { const chars = Array.from( text ); const scale = size / data.resolution; @@ -68798,6 +70434,12 @@ function createPaths( text, size, data ) { let offsetX = 0, offsetY = 0; + if ( direction == 'rtl' || direction == 'tb' ) { + + chars.reverse(); + + } + for ( let i = 0; i < chars.length; i ++ ) { const char = chars[ i ]; @@ -68810,7 +70452,18 @@ function createPaths( text, size, data ) { } else { const ret = createPath( char, scale, offsetX, offsetY, data ); - offsetX += ret.offsetX; + + if ( direction == 'tb' ) { + + offsetX = 0; + offsetY += data.ascender * scale; + + } else { + + offsetX += ret.offsetX; + + } + paths.push( ret.path ); } @@ -68979,7 +70632,7 @@ class OrbitControls extends Controls { * Constructs a new controls instance. * * @param {Object3D} object - The object that is managed by the controls. - * @param {?HTMLDOMElement} domElement - The HTML element used for event listeners. + * @param {?HTMLElement} domElement - The HTML element used for event listeners. */ constructor( object, domElement = null ) { @@ -69279,6 +70932,8 @@ class OrbitControls extends Controls { */ this.zoom0 = this.object.zoom; + this._cursorStyle = 'auto'; + // the target DOM element for key events this._domElementKeyEvents = null; @@ -69350,6 +71005,34 @@ class OrbitControls extends Controls { } + /** + * Defines the visual representation of the cursor. + * + * @type {('auto'|'grab')} + * @default 'auto' + */ + set cursorStyle( type ) { + + this._cursorStyle = type; + + if ( type === 'grab' ) { + + this.domElement.style.cursor = 'grab'; + + } else { + + this.domElement.style.cursor = 'auto'; + + } + + } + + get cursorStyle() { + + return this._cursorStyle; + + } + connect( element ) { super.connect( element ); @@ -69370,8 +71053,8 @@ class OrbitControls extends Controls { disconnect() { this.domElement.removeEventListener( 'pointerdown', this._onPointerDown ); - this.domElement.removeEventListener( 'pointermove', this._onPointerMove ); - this.domElement.removeEventListener( 'pointerup', this._onPointerUp ); + this.domElement.ownerDocument.removeEventListener( 'pointermove', this._onPointerMove ); + this.domElement.ownerDocument.removeEventListener( 'pointerup', this._onPointerUp ); this.domElement.removeEventListener( 'pointercancel', this._onPointerUp ); this.domElement.removeEventListener( 'wheel', this._onMouseWheel ); @@ -69429,7 +71112,7 @@ class OrbitControls extends Controls { * Adds key event listeners to the given DOM element. * `window` is a recommended argument for using this method. * - * @param {HTMLDOMElement} domElement - The DOM element + * @param {HTMLElement} domElement - The DOM element */ listenToKeyEvents( domElement ) { @@ -69482,6 +71165,67 @@ class OrbitControls extends Controls { } + /** + * Programmatically pan the camera. + * + * @param {number} deltaX - The horizontal pan amount in pixels. + * @param {number} deltaY - The vertical pan amount in pixels. + */ + pan( deltaX, deltaY ) { + + this._pan( deltaX, deltaY ); + this.update(); + + } + + /** + * Programmatically dolly in (zoom in for perspective camera). + * + * @param {number} dollyScale - The dolly scale factor. + */ + dollyIn( dollyScale ) { + + this._dollyIn( dollyScale ); + this.update(); + + } + + /** + * Programmatically dolly out (zoom out for perspective camera). + * + * @param {number} dollyScale - The dolly scale factor. + */ + dollyOut( dollyScale ) { + + this._dollyOut( dollyScale ); + this.update(); + + } + + /** + * Programmatically rotate the camera left (around the vertical axis). + * + * @param {number} angle - The rotation angle in radians. + */ + rotateLeft( angle ) { + + this._rotateLeft( angle ); + this.update(); + + } + + /** + * Programmatically rotate the camera up (around the horizontal axis). + * + * @param {number} angle - The rotation angle in radians. + */ + rotateUp( angle ) { + + this._rotateUp( angle ); + this.update(); + + } + update( deltaTime = null ) { const position = this.object.position; @@ -70346,8 +72090,8 @@ function onPointerDown( event ) { this.domElement.setPointerCapture( event.pointerId ); - this.domElement.addEventListener( 'pointermove', this._onPointerMove ); - this.domElement.addEventListener( 'pointerup', this._onPointerUp ); + this.domElement.ownerDocument.addEventListener( 'pointermove', this._onPointerMove ); + this.domElement.ownerDocument.addEventListener( 'pointerup', this._onPointerUp ); } @@ -70369,6 +72113,12 @@ function onPointerDown( event ) { } + if ( this._cursorStyle === 'grab' ) { + + this.domElement.style.cursor = 'grabbing'; + + } + } function onPointerMove( event ) { @@ -70397,13 +72147,19 @@ function onPointerUp( event ) { this.domElement.releasePointerCapture( event.pointerId ); - this.domElement.removeEventListener( 'pointermove', this._onPointerMove ); - this.domElement.removeEventListener( 'pointerup', this._onPointerUp ); + this.domElement.ownerDocument.removeEventListener( 'pointermove', this._onPointerMove ); + this.domElement.ownerDocument.removeEventListener( 'pointerup', this._onPointerUp ); this.dispatchEvent( _endEvent ); this.state = _STATE.NONE; + if ( this._cursorStyle === 'grab' ) { + + this.domElement.style.cursor = 'grab'; + + } + break; case 1: @@ -71411,12 +73167,12 @@ class EffectComposer { this.copyPass.material.blending = NoBlending; /** - * The internal clock for managing time data. + * The internal timer for managing time data. * * @private - * @type {Clock} + * @type {Timer} */ - this.clock = new Clock(); + this.timer = new Timer(); } @@ -71505,9 +73261,11 @@ class EffectComposer { // deltaTime value is in seconds + this.timer.update(); + if ( deltaTime === undefined ) { - deltaTime = this.clock.getDelta(); + deltaTime = this.timer.getDelta(); } @@ -71740,6 +73498,16 @@ class RenderPass extends Pass { * @default false */ this.needsSwap = false; + + /** + * This flag indicates that this pass renders the scene itself. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isRenderPass = true; + this._oldClearColor = new Color(); } @@ -71897,7 +73665,7 @@ const LuminosityHighPassShader = { * When using this pass, tone mapping must be enabled in the renderer settings. * * Reference: - * - [Bloom in Unreal Engine]{@link https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/} + * - [Bloom in Unreal Engine](https://docs.unrealengine.com/latest/INT/Engine/Rendering/PostProcessEffects/Bloom/) * * ```js * const resolution = new THREE.Vector2( window.innerWidth, window.innerHeight ); @@ -71931,7 +73699,7 @@ class UnrealBloomPass extends Pass { this.strength = strength; /** - * The Bloom radius. + * The Bloom radius. Must be in the range `[0,1]`. * * @type {number} */ @@ -72020,7 +73788,9 @@ class UnrealBloomPass extends Pass { // gaussian blur materials this.separableBlurMaterials = []; - const kernelSizeArray = [ 3, 5, 7, 9, 11 ]; + // These sizes have been changed to account for the altered coefficients-calculation to avoid blockiness, + // while retaining the same blur-strength. For details see https://github.com/mrdoob/three.js/pull/31528 + const kernelSizeArray = [ 6, 10, 14, 18, 22 ]; resx = Math.round( this.resolution.x / 2 ); resy = Math.round( this.resolution.y / 2 ); @@ -72060,6 +73830,7 @@ class UnrealBloomPass extends Pass { uniforms: this.copyUniforms, vertexShader: CopyShader.vertexShader, fragmentShader: CopyShader.fragmentShader, + premultipliedAlpha: true, blending: AdditiveBlending, depthTest: false, depthWrite: false, @@ -72251,10 +74022,11 @@ class UnrealBloomPass extends Pass { _getSeparableBlurMaterial( kernelRadius ) { const coefficients = []; + const sigma = kernelRadius / 3; for ( let i = 0; i < kernelRadius; i ++ ) { - coefficients.push( 0.39894 * Math.exp( -0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius ); + coefficients.push( 0.39894 * Math.exp( -0.5 * i * i / ( sigma * sigma ) ) / sigma ); } @@ -72271,34 +74043,46 @@ class UnrealBloomPass extends Pass { 'gaussianCoefficients': { value: coefficients } // precomputed Gaussian coefficients }, - vertexShader: - `varying vec2 vUv; + vertexShader: /* glsl */` + + varying vec2 vUv; + void main() { + vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`, - fragmentShader: - `#include + fragmentShader: /* glsl */` + + #include + varying vec2 vUv; + uniform sampler2D colorTexture; uniform vec2 invSize; uniform vec2 direction; uniform float gaussianCoefficients[KERNEL_RADIUS]; void main() { + float weightSum = gaussianCoefficients[0]; vec3 diffuseSum = texture2D( colorTexture, vUv ).rgb * weightSum; - for( int i = 1; i < KERNEL_RADIUS; i ++ ) { - float x = float(i); + + for ( int i = 1; i < KERNEL_RADIUS; i ++ ) { + + float x = float( i ); float w = gaussianCoefficients[i]; vec2 uvOffset = direction * invSize * x; vec3 sample1 = texture2D( colorTexture, vUv + uvOffset ).rgb; vec3 sample2 = texture2D( colorTexture, vUv - uvOffset ).rgb; - diffuseSum += (sample1 + sample2) * w; - weightSum += 2.0 * w; + diffuseSum += ( sample1 + sample2 ) * w; + } - gl_FragColor = vec4(diffuseSum/weightSum, 1.0); + + gl_FragColor = vec4( diffuseSum, 1.0 ); + }` } ); @@ -72324,15 +74108,21 @@ class UnrealBloomPass extends Pass { 'bloomRadius': { value: 0.0 } }, - vertexShader: - `varying vec2 vUv; + vertexShader: /* glsl */` + + varying vec2 vUv; + void main() { + vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + }`, - fragmentShader: - `varying vec2 vUv; + fragmentShader: /* glsl */` + + varying vec2 vUv; + uniform sampler2D blurTexture1; uniform sampler2D blurTexture2; uniform sampler2D blurTexture3; @@ -72343,17 +74133,27 @@ class UnrealBloomPass extends Pass { uniform float bloomFactors[NUM_MIPS]; uniform vec3 bloomTintColors[NUM_MIPS]; - float lerpBloomFactor(const in float factor) { + float lerpBloomFactor( const in float factor ) { + float mirrorFactor = 1.2 - factor; - return mix(factor, mirrorFactor, bloomRadius); + return mix( factor, mirrorFactor, bloomRadius ); + } void main() { - gl_FragColor = bloomStrength * ( lerpBloomFactor(bloomFactors[0]) * vec4(bloomTintColors[0], 1.0) * texture2D(blurTexture1, vUv) + - lerpBloomFactor(bloomFactors[1]) * vec4(bloomTintColors[1], 1.0) * texture2D(blurTexture2, vUv) + - lerpBloomFactor(bloomFactors[2]) * vec4(bloomTintColors[2], 1.0) * texture2D(blurTexture3, vUv) + - lerpBloomFactor(bloomFactors[3]) * vec4(bloomTintColors[3], 1.0) * texture2D(blurTexture4, vUv) + - lerpBloomFactor(bloomFactors[4]) * vec4(bloomTintColors[4], 1.0) * texture2D(blurTexture5, vUv) ); + + // 3.0 for backwards compatibility with previous alpha-based intensity + vec3 bloom = 3.0 * bloomStrength * ( + lerpBloomFactor( bloomFactors[ 0 ] ) * bloomTintColors[ 0 ] * texture2D( blurTexture1, vUv ).rgb + + lerpBloomFactor( bloomFactors[ 1 ] ) * bloomTintColors[ 1 ] * texture2D( blurTexture2, vUv ).rgb + + lerpBloomFactor( bloomFactors[ 2 ] ) * bloomTintColors[ 2 ] * texture2D( blurTexture3, vUv ).rgb + + lerpBloomFactor( bloomFactors[ 3 ] ) * bloomTintColors[ 3 ] * texture2D( blurTexture4, vUv ).rgb + + lerpBloomFactor( bloomFactors[ 4 ] ) * bloomTintColors[ 4 ] * texture2D( blurTexture5, vUv ).rgb + ); + + float bloomAlpha = max( bloom.r, max( bloom.g, bloom.b ) ); + gl_FragColor = vec4( bloom, bloomAlpha ); + }` } ); @@ -72493,7 +74293,7 @@ class Projector { _face, _faceCount, _facePoolLength = 0, _line, _lineCount, _linePoolLength = 0, _sprite, _spriteCount, _spritePoolLength = 0, - _modelMatrix; + _modelMatrix, _clipInput = [], _clipOutput = []; const @@ -72513,7 +74313,19 @@ class Projector { _frustum = new Frustum(), - _objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = []; + _objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = [], + + _clipVertexPool = [], + _clipPos1 = new Vector4(), + _clipPos2 = new Vector4(), + _clipPos3 = new Vector4(), + _screenVertexPool = [], + _clipInputVertices = [ null, null, null ], + + _clipPlanes = [ + { sign: 1 }, + { sign: -1 } + ]; // @@ -72652,48 +74464,165 @@ class Projector { const v2 = _vertexPool[ b ]; const v3 = _vertexPool[ c ]; - if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; + // Get homogeneous clip space positions (before perspective divide) + _clipPos1.copy( v1.positionWorld ).applyMatrix4( _viewProjectionMatrix ); + _clipPos2.copy( v2.positionWorld ).applyMatrix4( _viewProjectionMatrix ); + _clipPos3.copy( v3.positionWorld ).applyMatrix4( _viewProjectionMatrix ); + + // Check if triangle needs clipping + const nearDist1 = _clipPos1.z + _clipPos1.w; + const nearDist2 = _clipPos2.z + _clipPos2.w; + const nearDist3 = _clipPos3.z + _clipPos3.w; + const farDist1 = - _clipPos1.z + _clipPos1.w; + const farDist2 = - _clipPos2.z + _clipPos2.w; + const farDist3 = - _clipPos3.z + _clipPos3.w; + + // Check if completely outside + if ( ( nearDist1 < 0 && nearDist2 < 0 && nearDist3 < 0 ) || + ( farDist1 < 0 && farDist2 < 0 && farDist3 < 0 ) ) { + + return; // Triangle completely clipped + + } + + // Check if completely inside (no clipping needed) + if ( nearDist1 >= 0 && nearDist2 >= 0 && nearDist3 >= 0 && + farDist1 >= 0 && farDist2 >= 0 && farDist3 >= 0 ) { - if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { + // No clipping needed - use original path + if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; - _face = getNextFaceInPool(); + if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { - _face.id = object.id; - _face.v1.copy( v1 ); - _face.v2.copy( v2 ); - _face.v3.copy( v3 ); - _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; - _face.renderOrder = object.renderOrder; + _face = getNextFaceInPool(); - // face normal - _vector3.subVectors( v3.position, v2.position ); - _vector4.subVectors( v1.position, v2.position ); - _vector3.cross( _vector4 ); - _face.normalModel.copy( _vector3 ); - _face.normalModel.applyMatrix3( normalMatrix ).normalize(); + _face.id = object.id; + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; + _face.renderOrder = object.renderOrder; - for ( let i = 0; i < 3; i ++ ) { + // face normal + _vector3.subVectors( v3.position, v2.position ); + _vector4.subVectors( v1.position, v2.position ); + _vector3.cross( _vector4 ); + _face.normalModel.copy( _vector3 ); + _face.normalModel.applyMatrix3( normalMatrix ).normalize(); - const normal = _face.vertexNormalsModel[ i ]; - normal.fromArray( normals, arguments[ i ] * 3 ); - normal.applyMatrix3( normalMatrix ).normalize(); + for ( let i = 0; i < 3; i ++ ) { - const uv = _face.uvs[ i ]; - uv.fromArray( uvs, arguments[ i ] * 2 ); + const normal = _face.vertexNormalsModel[ i ]; + normal.fromArray( normals, arguments[ i ] * 3 ); + normal.applyMatrix3( normalMatrix ).normalize(); + + const uv = _face.uvs[ i ]; + uv.fromArray( uvs, arguments[ i ] * 2 ); + + } + + _face.vertexNormalsLength = 3; + + _face.material = material; + + if ( material.vertexColors ) { + + _face.color.fromArray( colors, a * 3 ); + + } + + _renderData.elements.push( _face ); } - _face.vertexNormalsLength = 3; + return; + + } + + // Triangle needs clipping + _clipInputVertices[ 0 ] = _clipPos1; + _clipInputVertices[ 1 ] = _clipPos2; + _clipInputVertices[ 2 ] = _clipPos3; + const clippedCount = clipTriangle( _clipInputVertices ); + + if ( clippedCount < 3 ) return; // Triangle completely clipped + + // Perform perspective divide on clipped vertices and create screen vertices + for ( let i = 0; i < clippedCount; i ++ ) { - _face.material = material; + const cv = _clipInput[ i ]; - if ( material.vertexColors ) { + // Get or create renderable vertex from pool + let sv = _screenVertexPool[ i ]; + if ( ! sv ) { - _face.color.fromArray( colors, a * 3 ); + sv = new RenderableVertex(); + _screenVertexPool[ i ] = sv; } - _renderData.elements.push( _face ); + // Perform perspective divide + const invW = 1 / cv.w; + sv.positionScreen.set( cv.x * invW, cv.y * invW, cv.z * invW, 1 ); + + // Interpolate world position (simplified - using weighted average based on barycentric-like coords) + // For a proper implementation, we'd need to track interpolation weights + sv.positionWorld.copy( v1.positionWorld ); + + sv.visible = true; + + } + + // Triangulate the clipped polygon (simple fan triangulation) + for ( let i = 1; i < clippedCount - 1; i ++ ) { + + const tv1 = _screenVertexPool[ 0 ]; + const tv2 = _screenVertexPool[ i ]; + const tv3 = _screenVertexPool[ i + 1 ]; + + if ( material.side === DoubleSide || checkBackfaceCulling( tv1, tv2, tv3 ) === true ) { + + _face = getNextFaceInPool(); + + _face.id = object.id; + _face.v1.copy( tv1 ); + _face.v2.copy( tv2 ); + _face.v3.copy( tv3 ); + _face.z = ( tv1.positionScreen.z + tv2.positionScreen.z + tv3.positionScreen.z ) / 3; + _face.renderOrder = object.renderOrder; + + // face normal - use original triangle's normal + _vector3.subVectors( v3.position, v2.position ); + _vector4.subVectors( v1.position, v2.position ); + _vector3.cross( _vector4 ); + _face.normalModel.copy( _vector3 ); + _face.normalModel.applyMatrix3( normalMatrix ).normalize(); + + // Use original vertex normals and UVs (simplified - proper impl would interpolate) + for ( let j = 0; j < 3; j ++ ) { + + const normal = _face.vertexNormalsModel[ j ]; + normal.fromArray( normals, arguments[ j ] * 3 ); + normal.applyMatrix3( normalMatrix ).normalize(); + + const uv = _face.uvs[ j ]; + uv.fromArray( uvs, arguments[ j ] * 2 ); + + } + + _face.vertexNormalsLength = 3; + + _face.material = material; + + if ( material.vertexColors ) { + + _face.color.fromArray( colors, a * 3 ); + + } + + _renderData.elements.push( _face ); + + } } @@ -72802,7 +74731,7 @@ class Projector { if ( sortObjects === true ) { - _renderData.objects.sort( painterSort ); + painterSortStable( _renderData.objects, 0, _renderData.objects.length ); } @@ -73068,7 +74997,7 @@ class Projector { if ( sortElements === true ) { - _renderData.elements.sort( painterSort ); + painterSortStable( _renderData.elements, 0, _renderData.elements.length ); } @@ -73212,6 +75141,115 @@ class Projector { } + function painterSortStable( array, start, length ) { + + // A stable insertion sort for sorting render items + // This avoids the GC overhead of Array.prototype.sort() + + for ( let i = start + 1; i < start + length; i ++ ) { + + const item = array[ i ]; + let j = i - 1; + + while ( j >= start && painterSort( array[ j ], item ) > 0 ) { + + array[ j + 1 ] = array[ j ]; + j --; + + } + + array[ j + 1 ] = item; + + } + + } + + // Sutherland-Hodgman triangle clipping in homogeneous clip space + // Returns count of vertices in clipped polygon (0 if completely clipped, 3+ if partially clipped) + // Result vertices are in _clipInput array + function clipTriangle( vertices ) { + + // Initialize input with the three input vertices + _clipInput[ 0 ] = vertices[ 0 ]; + _clipInput[ 1 ] = vertices[ 1 ]; + _clipInput[ 2 ] = vertices[ 2 ]; + + let inputCount = 3; + let outputCount = 0; + + for ( let p = 0; p < _clipPlanes.length; p ++ ) { + + const plane = _clipPlanes[ p ]; + outputCount = 0; + + if ( inputCount === 0 ) break; + + for ( let i = 0; i < inputCount; i ++ ) { + + const v1 = _clipInput[ i ]; + const v2 = _clipInput[ ( i + 1 ) % inputCount ]; + + const d1 = plane.sign * v1.z + v1.w; + const d2 = plane.sign * v2.z + v2.w; + + const v1Inside = d1 >= 0; + const v2Inside = d2 >= 0; + + if ( v1Inside && v2Inside ) { + + // Both inside - add v1 + _clipOutput[ outputCount ++ ] = v1; + + } else if ( v1Inside && ! v2Inside ) { + + // v1 inside, v2 outside - add v1 and intersection + _clipOutput[ outputCount ++ ] = v1; + + const t = d1 / ( d1 - d2 ); + let intersection = _clipVertexPool[ outputCount ]; + if ( ! intersection ) { + + intersection = new Vector4(); + _clipVertexPool[ outputCount ] = intersection; + + } + + intersection.lerpVectors( v1, v2, t ); + _clipOutput[ outputCount ++ ] = intersection; + + } else if ( ! v1Inside && v2Inside ) { + + // v1 outside, v2 inside - add intersection only + const t = d1 / ( d1 - d2 ); + let intersection = _clipVertexPool[ outputCount ]; + if ( ! intersection ) { + + intersection = new Vector4(); + _clipVertexPool[ outputCount ] = intersection; + + } + + intersection.lerpVectors( v1, v2, t ); + _clipOutput[ outputCount ++ ] = intersection; + + } + + // Both outside - add nothing + + } + + // Swap input/output + const temp = _clipInput; + _clipInput = _clipOutput; + _clipOutput = temp; + inputCount = outputCount; + + } + + return inputCount; + + } + function clipLine( s1, s2 ) { let alpha1 = 0, alpha2 = 1; @@ -73322,6 +75360,8 @@ class SVGRenderer { _svgNode, _pathCount = 0, + _svgObjectCount = 0, + _renderListCount = 0, _precision = null, _quality = 1, @@ -73348,6 +75388,8 @@ class SVGRenderer { _viewProjectionMatrix = new Matrix4(), _svgPathPool = [], + _svgObjectsPool = [], + _renderListPool = [], _projector = new Projector(), _svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); @@ -73355,7 +75397,7 @@ class SVGRenderer { /** * The DOM where the renderer appends its child-elements. * - * @type {DOMElement} + * @type {SVGSVGElement} */ this.domElement = _svg; @@ -73418,8 +75460,8 @@ class SVGRenderer { }; /** - * Sets the render quality. Setting to `high` means This value indicates that the browser - * tries to improve the SVG quality over rendering speed and geometric precision. + * Sets the render quality. Setting to `high` makes the browser improve SVG quality + * over rendering speed and geometric precision. * * @param {('low'|'high')} quality - The quality. */ @@ -73510,6 +75552,49 @@ class SVGRenderer { } + function renderSort( a, b ) { + + const aOrder = a.data.renderOrder !== undefined ? a.data.renderOrder : 0; + const bOrder = b.data.renderOrder !== undefined ? b.data.renderOrder : 0; + + if ( aOrder !== bOrder ) { + + return aOrder - bOrder; + + } else { + + const aZ = a.data.z !== undefined ? a.data.z : 0; + const bZ = b.data.z !== undefined ? b.data.z : 0; + + return bZ - aZ; // Painter's algorithm: far to near + + } + + } + + function arraySortStable( array, start, length ) { + + // A stable insertion sort for sorting the render list + // This avoids the GC overhead of Array.prototype.sort() + + for ( let i = start + 1; i < start + length; i ++ ) { + + const item = array[ i ]; + let j = i - 1; + + while ( j >= start && renderSort( array[ j ], item ) > 0 ) { + + array[ j + 1 ] = array[ j ]; + j --; + + } + + array[ j + 1 ] = item; + + } + + } + /** * Performs a manual clear with the defined clear color. */ @@ -73562,10 +75647,7 @@ class SVGRenderer { calculateLights( _lights ); - // reset accumulated path - - _currentPath = ''; - _currentStyle = ''; + _renderListCount = 0; for ( let e = 0, el = _elements.length; e < el; e ++ ) { @@ -73574,88 +75656,126 @@ class SVGRenderer { if ( material === undefined || material.opacity === 0 ) continue; - _elemBox.makeEmpty(); + getRenderItem( _renderListCount ++, 'element', element, material ); - if ( element instanceof RenderableSprite ) { + } - _v1 = element; - _v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf; + _svgObjectCount = 0; - renderSprite( _v1, element, material ); + scene.traverseVisible( function ( object ) { - } else if ( element instanceof RenderableLine ) { + if ( object.isSVGObject ) { - _v1 = element.v1; _v2 = element.v2; + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyMatrix4( _viewProjectionMatrix ); - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; + if ( _vector3.z < -1 || _vector3.z > 1 ) return; - _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); + const x = _vector3.x * _svgWidthHalf; + const y = - _vector3.y * _svgHeightHalf; - if ( _clipBox.intersectsBox( _elemBox ) === true ) { + const svgObject = getSVGObjectData( _svgObjectCount ++ ); - renderLine( _v1, _v2, material ); + svgObject.node = object.node; + svgObject.x = x; + svgObject.y = y; + svgObject.z = _vector3.z; + svgObject.renderOrder = object.renderOrder; - } + getRenderItem( _renderListCount ++, 'svgObject', svgObject, null ); - } else if ( element instanceof RenderableFace ) { + } - _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + } ); - if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue; - if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue; - if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue; + if ( this.sortElements ) { - _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; - _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; - _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; + arraySortStable( _renderListPool, 0, _renderListCount ); - if ( this.overdraw > 0 ) { + } - expand( _v1.positionScreen, _v2.positionScreen, this.overdraw ); - expand( _v2.positionScreen, _v3.positionScreen, this.overdraw ); - expand( _v3.positionScreen, _v1.positionScreen, this.overdraw ); + // Reset accumulated path + _currentPath = ''; + _currentStyle = ''; - } + // Render in sorted order + for ( let i = 0; i < _renderListCount; i ++ ) { - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen, - _v3.positionScreen - ] ); + const item = _renderListPool[ i ]; - if ( _clipBox.intersectsBox( _elemBox ) === true ) { + if ( item.type === 'svgObject' ) { - renderFace3( _v1, _v2, _v3, element, material ); + flushPath(); // Flush any accumulated paths before inserting SVG node - } + const svgObject = item.data; + const node = svgObject.node; + node.setAttribute( 'transform', 'translate(' + svgObject.x + ',' + svgObject.y + ')' ); + _svg.appendChild( node ); - } + } else { - } + const element = item.data; + const material = item.material; - flushPath(); // just to flush last svg:path + _elemBox.makeEmpty(); - scene.traverseVisible( function ( object ) { + if ( element instanceof RenderableSprite ) { - if ( object.isSVGObject ) { + _v1 = element; + _v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf; - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyMatrix4( _viewProjectionMatrix ); + renderSprite( _v1, element, material ); - if ( _vector3.z < -1 || _vector3.z > 1 ) return; + } else if ( element instanceof RenderableLine ) { - const x = _vector3.x * _svgWidthHalf; - const y = - _vector3.y * _svgHeightHalf; + _v1 = element.v1; _v2 = element.v2; - const node = object.node; - node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' ); + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; - _svg.appendChild( node ); + _elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); + + if ( _clipBox.intersectsBox( _elemBox ) === true ) { + + renderLine( _v1, _v2, material ); + + } + + } else if ( element instanceof RenderableFace ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; + _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; + + if ( this.overdraw > 0 ) { + + expand( _v1.positionScreen, _v2.positionScreen, this.overdraw ); + expand( _v2.positionScreen, _v3.positionScreen, this.overdraw ); + expand( _v3.positionScreen, _v1.positionScreen, this.overdraw ); + + } + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen, + _v3.positionScreen + ] ); + + if ( _clipBox.intersectsBox( _elemBox ) === true ) { + + renderFace3( _v1, _v2, _v3, element, material ); + + } + + } } - } ); + } + + flushPath(); // Flush any remaining paths }; @@ -73895,21 +76015,71 @@ class SVGRenderer { function getPathNode( id ) { - if ( _svgPathPool[ id ] == null ) { + let path = _svgPathPool[ id ]; - _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); + if ( path === undefined ) { + + path = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); if ( _quality == 0 ) { - _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + path.setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed } - return _svgPathPool[ id ]; + _svgPathPool[ id ] = path; + + } + + return path; + + } + + function getSVGObjectData( id ) { + + let svgObject = _svgObjectsPool[ id ]; + + if ( svgObject === undefined ) { + + svgObject = { + node: null, + x: 0, + y: 0, + z: 0, + renderOrder: 0 + }; + + _svgObjectsPool[ id ] = svgObject; } - return _svgPathPool[ id ]; + return svgObject; + + } + + function getRenderItem( id, type, data, material ) { + + let item = _renderListPool[ id ]; + + if ( item === undefined ) { + + item = { + type: type, + data: data, + material: material + }; + + _renderListPool[ id ] = item; + + return item; + + } + + item.type = type; + item.data = data; + item.material = material; + + return item; } @@ -73941,9 +76111,9 @@ async function importThreeJs(original) { if (!isNodeJs() || (THREE.REVISION <= 162)) return THREE; - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(h1 => { + return import('three').then(h1 => { Object.assign(THREE, h1); - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }); + return import('three/addons'); }).then(h2 => { Object.assign(THREE, h2); return THREE; @@ -74431,12 +76601,12 @@ async function createRender3D(width, height, render3d, args) { promise = Promise.resolve(r); } else if (isNodeJs()) { // try to use WebGL inside node.js - need to create headless context - promise = Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(node_canvas => { + promise = import('canvas').then(node_canvas => { args.canvas = node_canvas.default.createCanvas(width, height); args.canvas.addEventListener = () => {}; // dummy args.canvas.removeEventListener = () => {}; // dummy args.canvas.style = {}; - return internals._node_gl || Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }); + return internals._node_gl || import('gl'); }).then(node_gl => { internals._node_gl = node_gl; const gl = node_gl?.default(width, height, { preserveDrawingBuffer: true }); @@ -75483,7 +77653,7 @@ class PointsCreator { let promise; if (isNodeJs()) { - promise = Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(handle => handle.default.loadImage(dataUrl).then(img => { + promise = import('canvas').then(handle => handle.default.loadImage(dataUrl).then(img => { const canvas = handle.default.createCanvas(64, 64), ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, 64, 64); @@ -75831,7 +78001,6 @@ createLatexGeometry: createLatexGeometry */ /* eslint-disable curly */ -/* eslint-disable no-loss-of-precision */ /* eslint-disable no-useless-assignment */ /* eslint-disable no-use-before-define */ /* eslint-disable no-else-return */ @@ -78629,7 +80798,7 @@ async function saveFile(filename, content) { if (isFunc(_saveFileFunc)) return _saveFileFunc(filename, getBinFileContent(content)); if (isNodeJs()) { - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(fs => { + return import('fs').then(fs => { fs.writeFileSync(filename, getBinFileContent(content)); return true; }); @@ -79020,6 +81189,20 @@ class JSRootMenu { this.endsub(); } + /** @summary Add log scale selection for pad + * @protected */ + addPadLogMenu(kind, value, func) { + this.sub('SetLog ' + kind, + () => this.input('Enter log kind: 0 - off, 1 - log10, 2 - log2, 3 - ln, ...', value, 'int', 0, 10000).then(func)); + this.addchk(value === 0, 'linear', () => func(0)); + this.addchk(value === 1, 'log10', () => func(1)); + this.addchk(value === 2, 'log2', () => func(2)); + this.addchk(value === 3, 'ln', () => func(3)); + this.addchk(value === 4, 'log4', () => func(4)); + this.addchk(value === 8, 'log8', () => func(8)); + this.endsub(); + } + /** @summary Add palette menu entries * @protected */ addPaletteMenu(curr, set_func) { @@ -79465,38 +81648,40 @@ class JSRootMenu { faxis.fNdivisions = val; painter.interactiveRedraw('pad', `exec:SetNdivisions(${val})`, kind); })); - this.sub('Labels'); - this.addchk(faxis.TestBit(EAxisBits.kCenterLabels), 'Center', - arg => { faxis.SetBit(EAxisBits.kCenterLabels, arg); painter.interactiveRedraw('pad', `exec:CenterLabels(${arg})`, kind); }); - this.addchk(faxis.TestBit(EAxisBits.kLabelsVert), 'Rotate', - arg => { faxis.SetBit(EAxisBits.kLabelsVert, arg); painter.interactiveRedraw('pad', `exec:SetBit(TAxis::kLabelsVert,${arg})`, kind); }); - this.addColorMenu('Color', faxis.fLabelColor, - arg => { faxis.fLabelColor = arg; painter.interactiveRedraw('pad', getColorExec(arg, 'SetLabelColor'), kind); }); - this.addSizeMenu('Offset', -0.02, 0.1, 0.01, faxis.fLabelOffset, - arg => { faxis.fLabelOffset = arg; painter.interactiveRedraw('pad', `exec:SetLabelOffset(${arg})`, kind); }); - let a = faxis.fLabelSize >= 1; - this.addSizeMenu('Size', a ? 2 : 0.02, a ? 30 : 0.11, a ? 2 : 0.01, faxis.fLabelSize, - arg => { faxis.fLabelSize = arg; painter.interactiveRedraw('pad', `exec:SetLabelSize(${arg})`, kind); }); - - if (frame_painter && (axis_painter?.kind === kAxisLabels) && (faxis.fNbins > 20)) { - this.add('Find label', () => this.input('Label id').then(id => { - if (!id) - return; - for (let bin = 0; bin < faxis.fNbins; ++bin) { - const lbl = axis_painter.formatLabels(bin); - if (lbl === id) - return frame_painter.zoomSingle(kind, Math.max(0, bin - 4), Math.min(faxis.fNbins, bin + 5)); - } - }), 'Zoom into region around specific label'); - } - if (frame_painter && faxis.fLabels) { - const ignore = `${kind}_ignore_labels`; - this.addchk(!frame_painter[ignore], 'Custom', flag => { - frame_painter[ignore] = !flag; - painter.interactiveRedraw('pad'); - }, `Use of custom labels in axis ${kind}`); + if (kind !== 'v') { + this.sub('Labels'); + this.addchk(faxis.TestBit(EAxisBits.kCenterLabels), 'Center', + arg => { faxis.SetBit(EAxisBits.kCenterLabels, arg); painter.interactiveRedraw('pad', `exec:CenterLabels(${arg})`, kind); }); + this.addchk(faxis.TestBit(EAxisBits.kLabelsVert), 'Rotate', + arg => { faxis.SetBit(EAxisBits.kLabelsVert, arg); painter.interactiveRedraw('pad', `exec:SetBit(TAxis::kLabelsVert,${arg})`, kind); }); + this.addColorMenu('Color', faxis.fLabelColor, + arg => { faxis.fLabelColor = arg; painter.interactiveRedraw('pad', getColorExec(arg, 'SetLabelColor'), kind); }); + this.addSizeMenu('Offset', -0.02, 0.1, 0.01, faxis.fLabelOffset, + arg => { faxis.fLabelOffset = arg; painter.interactiveRedraw('pad', `exec:SetLabelOffset(${arg})`, kind); }); + const a = faxis.fLabelSize >= 1; + this.addSizeMenu('Size', a ? 2 : 0.02, a ? 30 : 0.11, a ? 2 : 0.01, faxis.fLabelSize, + arg => { faxis.fLabelSize = arg; painter.interactiveRedraw('pad', `exec:SetLabelSize(${arg})`, kind); }); + + if (frame_painter && (axis_painter?.kind === kAxisLabels) && (faxis.fNbins > 20)) { + this.add('Find label', () => this.input('Label id').then(id => { + if (!id) + return; + for (let bin = 0; bin < faxis.fNbins; ++bin) { + const lbl = axis_painter.formatLabels(bin); + if (lbl === id) + return frame_painter.zoomSingle(kind, Math.max(0, bin - 4), Math.min(faxis.fNbins, bin + 5)); + } + }), 'Zoom into region around specific label'); + } + if (frame_painter && faxis.fLabels) { + const ignore = `${kind}_ignore_labels`; + this.addchk(!frame_painter[ignore], 'Custom', flag => { + frame_painter[ignore] = !flag; + painter.interactiveRedraw('pad'); + }, `Use of custom labels in axis ${kind}`); + } + this.endsub(); } - this.endsub(); this.sub('Title'); this.add('SetTitle', () => { @@ -79523,8 +81708,8 @@ class JSRootMenu { }); this.addSizeMenu('Offset', 0, 3, 0.2, faxis.fTitleOffset, arg => { faxis.fTitleOffset = arg; painter.interactiveRedraw('pad', `exec:SetTitleOffset(${arg})`, kind); }); - a = faxis.fTitleSize >= 1; - this.addSizeMenu('Size', a ? 2 : 0.02, a ? 30 : 0.11, a ? 2 : 0.01, faxis.fTitleSize, + const p = faxis.fTitleSize >= 1; + this.addSizeMenu('Size', p ? 2 : 0.02, p ? 30 : 0.11, p ? 2 : 0.01, faxis.fTitleSize, arg => { faxis.fTitleSize = arg; painter.interactiveRedraw('pad', `exec:SetTitleSize(${arg})`, kind); }); this.endsub(); @@ -79626,6 +81811,9 @@ class JSRootMenu { this.addchk(settings.StripAxisLabels, 'Strip labels', flag => { settings.StripAxisLabels = flag; }, 'Provide shorter labels like 10^0 -> 1'); this.addchk(settings.CutAxisLabels, 'Cut labels', flag => { settings.CutAxisLabels = flag; }, 'Remove labels which may exceed graphical range'); this.add(`Tilt angle ${settings.AxisTiltAngle}`, () => this.input('Axis tilt angle', settings.AxisTiltAngle, 'int', 0, 180).then(val => { settings.AxisTiltAngle = val; })); + this.add(`X format ${settings.XValuesFormat ?? gStyle.fStatFormat}`, () => this.input('X axis format', settings.XValuesFormat).then(val => { settings.XValuesFormat = val; })); + this.add(`Y format ${settings.YValuesFormat ?? gStyle.fStatFormat}`, () => this.input('Y axis format', settings.YValuesFormat).then(val => { settings.YValuesFormat = val; })); + this.add(`Z format ${settings.ZValuesFormat ?? gStyle.fStatFormat}`, () => this.input('Z axis format', settings.ZValuesFormat).then(val => { settings.ZValuesFormat = val; })); this.endsub(); this.addSelectMenu('Latex', ['Off', 'Symbols', 'Normal', 'MathJax', 'Force MathJax'], settings.Latex, value => { settings.Latex = value; }); this.addSelectMenu('3D rendering', ['Default', 'WebGL', 'Image'], settings.Render3D, value => { settings.Render3D = value; }); @@ -79824,9 +82012,14 @@ class JSRootMenu { * @param {string} [kind] - use 'text' (default), 'number', 'float' or 'int' * @protected */ async input(title, value, kind, min, max) { + let onchange = null; + if (isFunc(kind)) { + onchange = kind; + kind = ''; + } if (!kind) kind = 'text'; - const inp_type = (kind === 'int') ? 'number' : 'text'; + const inp_type = (kind === 'int') ? 'number' : 'text', value0 = value; let ranges = ''; if ((value === undefined) || (value === null)) value = ''; @@ -79839,24 +82032,33 @@ class JSRootMenu { const main_content = '
' + - `` + - '
'; + `` + + '', oninit = !onchange ? null : elem => { + const inp = elem.querySelector('.jsroot_dlginp'); + if (inp) + inp.oninput = () => onchange(inp.value); + }; return new Promise(resolveFunc => { - this.runModal(title, main_content, { btns: true, height: 150, width: 400 }).then(element => { - if (!element) + this.runModal(title, main_content, { btns: true, height: 150, width: 400, oninit }).then(element => { + if (!element) { + if (onchange) + onchange(value0); return; - let val = element.querySelector('.jsroot_dlginp').value; + } + let val = element.querySelector('.jsroot_dlginp').value, isok = true; if (kind === 'float') { val = Number.parseFloat(val); - if (Number.isFinite(val)) - resolveFunc(val); + isok = Number.isFinite(val); } else if (kind === 'int') { val = parseInt(val); - if (Number.isInteger(val)) - resolveFunc(val); - } else + isok = Number.isInteger(val); + } + if (isok) { + if (onchange) + onchange(val); resolveFunc(val); + } }); }); } @@ -80315,25 +82517,25 @@ class StandaloneMenu extends JSRootMenu { select(`#${dlg_id}`).remove(); select(`#${dlg_id}_block`).remove(); - const w = Math.min(args.width || 450, Math.round(0.9 * browser.screenWidth)); - modal.block = select('body').append('div') - .attr('id', `${dlg_id}_block`) - .attr('class', 'jsroot_dialog_block') - .attr('style', 'z-index: 100000; position: absolute; left: 0px; top: 0px; bottom: 0px; right: 0px; opacity: 0.2; background-color: white'); - modal.element = select('body') - .append('div') - .attr('id', dlg_id) - .attr('class', 'jsroot_dialog') - .style('position', 'absolute') - .style('width', `${w}px`) - .style('left', '50%') - .style('top', '50%') - .style('z-index', 100001) - .attr('tabindex', '0'); + const w = Math.min(args.width || 450, Math.round(0.9 * browser.screenWidth)), + b = select('body'); + modal.block = b.append('div') + .attr('id', `${dlg_id}_block`) + .attr('class', 'jsroot_dialog_block') + .attr('style', 'z-index: 100000; position: absolute; left: 0px; top: 0px; bottom: 0px; right: 0px; opacity: 0.2; background-color: white'); + modal.element = b.append('div') + .attr('id', dlg_id) + .attr('class', 'jsroot_dialog') + .style('position', 'absolute') + .style('width', `${w}px`) + .style('left', '50%') + .style('top', '50%') + .style('z-index', 100001) + .attr('tabindex', '0'); modal.element.html( '
' + - `
${title}
` + + `
${title}
` + `
${main_content}
` + '
' + `` + @@ -80341,6 +82543,16 @@ class StandaloneMenu extends JSRootMenu { '
' ); + const drag_move = drag().on('start', () => { modal.y0 = 0; }).on('drag', evnt => { + if (!modal.y0) + modal.y0 = pointer(evnt, modal.element.node())[1]; + let p0 = Math.max(0, pointer(evnt, b.node())[1] - modal.y0); + if (b.node().clientHeight) + p0 = Math.min(p0, 0.8 * b.node().clientHeight); + modal.element.style('top', `${p0}px`); + }); + modal.element.select('.jsroot_dialog_title').call(drag_move); + modal.done = function(res) { if (this._done) return; @@ -80383,6 +82595,8 @@ class StandaloneMenu extends JSRootMenu { f = modal.element.select('.jsroot_dialog_footer').select('button'); if (!f.empty()) f.node().focus(); + if (isFunc(args.oninit)) + args.oninit(modal.element.node()); return modal; } @@ -81021,7 +83235,7 @@ class TAxisPainter extends ObjectPainter { if (this.kind === kAxisFunc) this.func = this.createFuncHandle(opts.axis_func, this.logbase, smin, smax); else - this.func = log().base(this.logbase).domain([smin, smax]); + this.func = log$1().base(this.logbase).domain([smin, smax]); } else if (this.symlog) { let v = Math.max(Math.abs(smin), Math.abs(smax)); if (Number.isInteger(this.symlog) && (this.symlog > 0)) @@ -81526,7 +83740,7 @@ class TAxisPainter extends ObjectPainter { if (handle.kind === 1) { // if not showing labels, not show large tick - // FIXME: for labels last tick is smaller, + // FIXME: for labels last tick is smaller if (!this.isExtraLogTick(handle.tick) && (this.format(handle.tick, true) !== null)) h1 = tickSize; this.ticks.push(handle.grpos); // keep graphical positions of major ticks @@ -84867,21 +87081,9 @@ class TFramePainter extends FrameInteractive { } menu.endsub(); - if (pad) { - const member = 'fLog' + kind[0]; - menu.sub('SetLog ' + kind[0], () => { - menu.input('Enter log kind: 0 - off, 1 - log10, 2 - log2, 3 - ln, ...', pad[member], 'int', 0, 10000).then(v => { - this.changeAxisLog(kind[0], v); - }); - }); - menu.addchk(pad[member] === 0, 'linear', () => this.changeAxisLog(kind[0], 0)); - menu.addchk(pad[member] === 1, 'log10', () => this.changeAxisLog(kind[0], 1)); - menu.addchk(pad[member] === 2, 'log2', () => this.changeAxisLog(kind[0], 2)); - menu.addchk(pad[member] === 3, 'ln', () => this.changeAxisLog(kind[0], 3)); - menu.addchk(pad[member] === 4, 'log4', () => this.changeAxisLog(kind[0], 4)); - menu.addchk(pad[member] === 8, 'log8', () => this.changeAxisLog(kind[0], 8)); - menu.endsub(); - } + if (pad) + menu.addPadLogMenu(kind[0], pad[`fLog${kind[0]}`], v => this.changeAxisLog(kind[0], v)); + menu.addchk(faxis.TestBit(EAxisBits.kMoreLogLabels), 'More log', flag => { faxis.SetBit(EAxisBits.kMoreLogLabels, flag); if (hist_painter?.getSnapId() && (kind.length === 1)) @@ -91032,6 +93234,20 @@ class TCanvasPainter extends TPadPainter { this.getWebsocket()?.resizeWindow(fullW, fullH); } + /** @summary create three.js object for TCanvas */ + static async build3d(can, opt, get_painter) { + const painter = new TCanvasPainter(null, can, opt, true); + painter.checkSpecialsInPrimitives(can, true); + + const fp = new TFramePainter(null, null); + // return dummy frame painter as result + painter.getFramePainter = () => fp; + + return painter.drawPrimitives().then(() => { + return get_painter ? painter : fp.create3DScene(-1, true); + }); + } + /** @summary draw TCanvas */ static async draw(dom, can, opt) { const nocanvas = !can; @@ -91202,18 +93418,26 @@ class TPavePainter extends ObjectPainter { tm = pad?.fTopMargin ?? gStyle.fPadTopMargin, bm = pad?.fBottomMargin ?? gStyle.fPadBottomMargin; - return svgToImage(svg_code).then(canvas => { - if (!canvas) + return svgToImage(svg_code, 'rgba').then(image => { + if (!image) + return false; + + let arr = image.data; + const width = image.width, height = image.height; + + if (!arr && isFunc(image.getContext) && image.getContext('2d')) + arr = image.getContext('2d').getImageData(0, 0, width, height).data; + + if (!arr) return false; let nX = 100, nY = 100; - const context = canvas.getContext('2d'), - arr = context.getImageData(0, 0, canvas.width, canvas.height).data, - boxW = Math.floor(canvas.width / nX), boxH = Math.floor(canvas.height / nY), + const boxW = Math.floor(width / nX), + boxH = Math.floor(height / nY), raster = new Array(nX * nY); - if (arr.length !== canvas.width * canvas.height * 4) { - console.log(`Image size missmatch in TLegend autoplace ${arr.length} expected ${canvas.width * canvas.height * 4}`); + if (arr.length !== width * height * 4) { + console.log(`Image size missmatch in TLegend autoplace ${arr.length} expected ${width * height * 4}`); nX = nY = 0; } @@ -91225,7 +93449,7 @@ class TPavePainter extends ObjectPainter { for (let x = px1; (x < px2) && !filled; ++x) { for (let y = py1; y < py2; ++y) { - const indx = (y * canvas.width + x) * 4; + const indx = (y * width + x) * 4; if (arr[indx] || arr[indx + 1] || arr[indx + 2] || arr[indx + 3]) { filled = 1; break; @@ -91303,6 +93527,11 @@ class TPavePainter extends ObjectPainter { opt = this.getPaveDrawOption().toUpperCase(), fp = this.getFramePainter(), pp = this.getPadPainter(), pad = pp.getRootPad(true); + + // special handling of dummy frame painter + if (fp?.getDrawDom() === null) + return this; + let interactive_element, width, height; if (pt.fInit === 0) { @@ -91909,7 +94138,7 @@ class TPavePainter extends ObjectPainter { nlines = legend.fPrimitives.arr.length, ncols = Math.max(1, legend.fNColumns); let nrows = Math.round(nlines / ncols), - any_text = false, + any_text = false, has_header = false, custom_textg = false; // each text entry has own attributes if (nrows * ncols < nlines) @@ -91919,6 +94148,8 @@ class TPavePainter extends ObjectPainter { for (let ii = 0; ii < nlines; ++ii) { const entry = legend.fPrimitives.arr[ii]; + if ((ii === 0) && (entry.fOption === 'h')) + has_header = true; if (isEmpty(entry)) { if (ncols === 1) nrows--; @@ -91930,6 +94161,9 @@ class TPavePainter extends ObjectPainter { } } + if (has_header && (ncols > 1) && ((nrows - 1) * ncols < nlines - 1)) + nrows++; + if (nrows < 1) nrows = 1; @@ -91992,8 +94226,10 @@ class TPavePainter extends ObjectPainter { if (ncols === 1) ++i; - else + else if (!has_header || ii === 0) i = ii; + else + i = ii - 1 + ncols; const lopt = entry.fOption.toLowerCase(), icol = i % ncols, irow = (i - icol) / ncols, @@ -92098,17 +94334,19 @@ class TPavePainter extends ObjectPainter { .call(painter.lineatt.func); } - let pos_x = tpos_x; + let pos_x = tpos_x, arg_width = Math.round(column_pos[icol + 1] - pos_x); if (isStr(lopt) && (lopt.toLowerCase() !== 'h')) any_opt = true; - else if (!any_opt) - pos_x = x0; + else if (!any_opt) { + pos_x = padding_x; + arg_width = w - 2 * padding_x; + } if (entry.fLabel) { const textatt = this.createAttText({ attr: entry, std: false, attr_alt: legend }), arg = { draw_g: this.getG(), align: textatt.align, - x: pos_x, width: Math.round(column_pos[icol + 1] - pos_x), + x: pos_x, width: arg_width, y: y0, height: Math.round(row_height), scale: (custom_textg && !entry.fTextSize) || !legend.fTextSize, text: entry.fLabel, color: textatt.color @@ -92148,10 +94386,10 @@ class TPavePainter extends ObjectPainter { height = pp.getPadHeight(), pad = pp.getRootPad(true), main = palette.$main_painter || this.getMainPainter(), + is_th3 = isFunc(main.getDimension) && (main.getDimension() === 3), fp = this.getFramePainter(), - contour = main.getContour(false), + contour = main.getContour(is_th3), levels = contour?.getLevels(), - is_th3 = isFunc(main.getDimension) && (main.getDimension() === 3), is_scatter = isFunc(main.getZaxis), log = pad?.fLogv ?? (is_th3 ? false : pad?.fLogz), draw_palette = main.getHistPalette(), @@ -92161,6 +94399,7 @@ class TPavePainter extends ObjectPainter { let zmin = 0, zmax = 100, gzmin, gzmax, axis_transform, axis_second = 0; this.#palette_vertical = (palette.fX2NDC - palette.fX1NDC) < (palette.fY2NDC - palette.fY1NDC); + this.is_th3 = is_th3; axis.fTickSize = 0.03; // adjust axis ticks size @@ -92197,12 +94436,16 @@ class TPavePainter extends ObjectPainter { } } else if ((main.gmaxbin !== undefined) && (main.gminbin !== undefined)) { // this is case of TH2 (needs only for size adjustment) - zmin = main.gminbin; - zmax = main.gmaxbin; + gzmin = zmin = main.gminbin; + gzmax = zmax = main.gmaxbin; + if (contour?.colzmin !== undefined && contour?.colzmax !== undefined) { + zmin = contour.colzmin; + zmax = contour.colzmax; + } } else if ((main.hmin !== undefined) && (main.hmax !== undefined)) { // this is case of TH1 - zmin = main.hmin; - zmax = main.hmax; + gzmin = zmin = main.hmin; + gzmax = zmax = main.hmax; } g.selectAll('rect').style('fill', 'white'); @@ -92374,6 +94617,17 @@ class TPavePainter extends ObjectPainter { zoom_rect.attr('x', Math.min(sel1, sel2)) .attr('width', Math.abs(sel2 - sel1)); } + }, zoomPalette = (z1, z2) => { + if (!this.is_th3) + return this.getFramePainter().zoomSingle('z', z1, z2, true); + const maino = this.getMainPainter().options; + if (z1 === z2) + maino.minimum = maino.maximum = kNoZoom; + else { + maino.minimum = z1; + maino.maximum = z2; + } + this.interactiveRedraw('pad'); }, endRectSel = evnt => { if (!doing_zoom) return; @@ -92385,10 +94639,13 @@ class TPavePainter extends ObjectPainter { zoom_rect = null; doing_zoom = false; + if (sel1 === sel2) + return; + const z1 = this.z_handle.revertPoint(sel1), z2 = this.z_handle.revertPoint(sel2); - this.getFramePainter().zoomSingle('z', Math.min(z1, z2), Math.max(z1, z2), true); + zoomPalette(Math.min(z1, z2), Math.max(z1, z2)); }, startRectSel = evnt => { // ignore when touch selection is activated if (doing_zoom) @@ -92423,7 +94680,7 @@ class TPavePainter extends ObjectPainter { if (settings.Zooming) { this.getG().selectAll('.axis_zoom') .on('mousedown', startRectSel) - .on('dblclick', () => this.getFramePainter().zoomSingle('z', 0, 0, true)); + .on('dblclick', () => zoomPalette(0, 0)); } if (settings.ZoomWheel) { @@ -92432,7 +94689,7 @@ class TPavePainter extends ObjectPainter { coord = this.#palette_vertical ? (1 - pos[1] / s_height) : pos[0] / s_width, item = this.z_handle.analyzeWheelEvent(evnt, coord); if (item?.changed) - this.getFramePainter().zoomSingle('z', item.min, item.max, true); + zoomPalette(item.min, item.max); }); } } @@ -92545,9 +94802,9 @@ class TPavePainter extends ObjectPainter { }); const addStatOpt = (pos, name) => { let sopt = (pos < 10) ? pave.fOptStat : pave.fOptFit; - sopt = parseInt(parseInt(sopt) / parseInt(Math.pow(10, pos % 10))) % 10; + sopt = Math.floor(parseInt(sopt) / parseInt(Math.pow(10, pos % 10))) % 10; menu.addchk(sopt, name, sopt * 100 + pos, arg => { - const oldopt = parseInt(arg / 100); + const oldopt = Math.floor(arg / 100); let newopt = (arg % 100 < 10) ? pave.fOptStat : pave.fOptFit; newopt -= (oldopt > 0 ? oldopt : -1) * parseInt(Math.pow(10, arg % 10)); if (arg % 100 < 10) { @@ -92637,12 +94894,51 @@ class TPavePainter extends ObjectPainter { /** @summary Show pave context menu */ paveContextMenu(evnt) { - if (this.z_handle) { + if (!this.z_handle) + return showPainterMenu(evnt, this); + if (!this.is_th3) { const fp = this.getFramePainter(); if (isFunc(fp?.showContextMenu)) fp.showContextMenu('pal', evnt); - } else - showPainterMenu(evnt, this); + return; + } + + const pp = this.getPadPainter(), + pad = pp?.getRootPad(true), + faxis = this.z_handle.getObject(), + hist_painter = this.z_handle.hist_painter || this.getMainPainter(true); + + if (!pad || !hist_painter) + return; + + if (isFunc(evnt?.stopPropagation)) { + evnt.preventDefault(); + evnt.stopPropagation(); // disable main context menu + } + + createMenu(evnt, this).then(menu => { + menu.header('V axis', `${urlClassPrefix}${clTAxis}.html`); + + menu.addPadLogMenu('v', pad.fLogv || 0, v => { + pad.fLogv = v; + this.interactiveRedraw('pad', 'log'); + }); + + menu.addchk(faxis.TestBit(EAxisBits.kMoreLogLabels), 'More log', flag => { + faxis.SetBit(EAxisBits.kMoreLogLabels, flag); + this.interactiveRedraw('pad'); + }); + menu.addchk(faxis.TestBit(EAxisBits.kNoExponent), 'No exponent', flag => { + faxis.SetBit(EAxisBits.kNoExponent, flag); + this.interactiveRedraw('pad'); + }); + + hist_painter.fillPaletteMenu(menu, false); + + menu.addTAxisMenu(EAxisBits, hist_painter || this, faxis, 'v', this.z_handle, null); + + menu.show(); + }); } /** @summary Returns true when stat box is drawn */ @@ -93014,6 +95310,7 @@ kPosTitle: kPosTitle const kCARTESIAN = 1, kPOLAR = 2, kCYLINDRICAL = 3, kSPHERICAL = 4, kRAPIDITY = 5, kNormal$1 = 0, kPoisson = 1, kPoisson2 = 2, kOnlyCheck = 'only-check'; + /** * @summary Class to decode histograms draw options * @desc All options started from capital letter are major drawing options @@ -94010,7 +96307,7 @@ class FunctionsHandler { #painter; // object painter to which functions belongs #pad_painter; // pad painter - constructor(painter, pp, funcs, statpainter) { + constructor(painter, pp, funcs, statpainter, update_statpainter) { this.#painter = painter; this.#pad_painter = pp; @@ -94069,6 +96366,8 @@ class FunctionsHandler { const indx = painters.indexOf(statpainter); if (indx >= 0) painters.splice(indx, 1); + if (update_statpainter && (update_painters.indexOf(statpainter) < 0)) + update_painters.push(statpainter); } // remove all function which are not found in new list of functions @@ -94139,6 +96438,7 @@ class THistPainter extends ObjectPainter { #auto_exec; // can be reused when sending option back to server #funcs_handler; // special instance for functions drawing #contour; // histogram colors contour + #create_stats; // if stats was created by painter /** @summary Constructor * @param {object|string} dom - DOM element for drawing or element id @@ -94442,7 +96742,7 @@ class THistPainter extends ObjectPainter { histo.fBins = obj.fBins; // remove old functions, update existing, prepare to draw new one - this.#funcs_handler = new FunctionsHandler(this, pp, obj.fFunctions, statpainter); + this.#funcs_handler = new FunctionsHandler(this, pp, obj.fFunctions, statpainter, this.#create_stats); const changed_opt = (histo.fOption !== obj.fOption); histo.fOption = obj.fOption; @@ -94767,7 +97067,7 @@ class THistPainter extends ObjectPainter { /** @summary Returns true if stats box fill can be ignored */ isIgnoreStatsFill() { - return !this.getObject() || (!this.draw_content && !this.create_stats && !this.hasSnapId()); + return !this.getObject() || (!this.draw_content && !this.#create_stats && !this.hasSnapId()); } /** @summary Create stat box for histogram if required */ @@ -94806,7 +97106,7 @@ class THistPainter extends ObjectPainter { if (!stats && !optstat && !optfit) return null; - this.create_stats = true; + this.#create_stats = true; if (stats) return stats; @@ -95281,6 +97581,12 @@ class THistPainter extends ObjectPainter { return cntr; } + /** @summary Reset contour object + * @private */ + resetContour() { + this.#contour = undefined; + } + /** @summary Return Z-scale ranges to create contour */ #getContourRanges(main, fp) { const o = this.getOptions(), @@ -95861,7 +98167,7 @@ class THistPainter extends ObjectPainter { this.maxbin = this.minbin = 0; // force recalculation of z levels - this.#contour = undefined; + this.resetContour(); if (args.zrange) Object.assign(res, this.#getContourRanges(this.getMainPainter(), this.getFramePainter())); @@ -99175,11 +101481,11 @@ let TH2Painter$2 = class TH2Painter extends THistPainter { this.assignChordCircInteractive(midx, midy); - const chord$1 = chord() + const chord = chord_default() .padAngle(10 / innerRadius) .sortSubgroups(d3_descending) .sortChords(d3_descending), - chords = chord$1(data), + chords = chord(data), group = g.append('g') .attr('font-size', 10) .attr('font-family', 'sans-serif') @@ -99187,7 +101493,7 @@ let TH2Painter$2 = class TH2Painter extends THistPainter { .data(chords.groups) .join('g'), arc$1 = arc().innerRadius(innerRadius).outerRadius(outerRadius), - ribbon = ribbon$1().radius(innerRadius - 1).padAngle(1 / innerRadius); + ribbon = ribbon_default().radius(innerRadius - 1).padAngle(1 / innerRadius); function ticks({ startAngle, endAngle, value }) { const k = (endAngle - startAngle) / value, @@ -104524,6 +106830,9 @@ class TH3Painter extends THistPainter { const histo = this.getHisto(), fp = this.getFramePainter(); + // ensure proper colors + this.resetContour(); + let use_lambert = false, use_helper = false, use_colors = false, use_opacity = 1, exclude_content = -1, logv = this.getPadPainter()?.getRootPad()?.fLogv, @@ -104597,19 +106906,6 @@ class TH3Painter extends THistPainter { } else if (use_scale) use_scale = (this.gminbin || this.gmaxbin) ? 1 / Math.max(Math.abs(this.gminbin), Math.abs(this.gmaxbin)) : 1; - const get_bin_weight = content => { - if ((exclude_content >= 0) && (content < exclude_content)) - return 0; - if (!use_scale) - return 1; - if (logv) { - if (content <= 0) - return 0; - content = Math.log(content) - scale_offset; - } - return Math.pow(Math.abs(content * use_scale), 0.3333); - }; - // eslint-disable-next-line one-var const i1 = this.getSelectIndex('x', 'left', 0.5), i2 = this.getSelectIndex('x', 'right', 0), j1 = this.getSelectIndex('y', 'left', 0.5), @@ -104623,7 +106919,19 @@ class TH3Painter extends THistPainter { const cntr = use_colors ? this.getContour() : null, palette = use_colors ? this.getHistPalette() : null, bins_matrixes = [], bins_colors = [], bins_ids = [], negative_matrixes = [], bin_opacities = [], - transfer = (this.transferFunc && proivdeEvalPar(this.transferFunc, true)) ? this.transferFunc : null; + transfer = (this.transferFunc && proivdeEvalPar(this.transferFunc, true)) ? this.transferFunc : null, + get_bin_weight = content => { + if ((exclude_content >= 0) && (content < exclude_content)) + return 0; + if (!use_scale) + return 1; + if (logv) { + if (content <= 0) + return 0; + content = Math.log(content) - scale_offset; + } + return Math.pow(Math.abs(content * use_scale), 0.3333); + }; for (let i = i1; i < i2; ++i) { const grx1 = fp.grx(histo.fXaxis.GetBinLowEdge(i + 1)), @@ -105029,11 +107337,14 @@ let TGraphPainter$1 = class TGraphPainter extends ObjectPainter { return true; let is_normal = false; - if (check_axis !== 'y') - is_normal ||= (histo.fXaxis.fXmin !== 0.0011) || (histo.fXaxis.fXmax !== 1.1); + if (check_axis !== 'y') { + is_normal ||= ((histo.fXaxis.fXmin !== 0.0011) && (histo.fXaxis.fXmin !== 0)) || + ((histo.fXaxis.fXmax !== 1.1) && (histo.fXaxis.fXmax !== 1)); + } if (check_axis !== 'x') { - is_normal ||= (histo.fYaxis.fXmin !== 0.0011) || (histo.fYaxis.fXmax !== 1.1) || + is_normal ||= ((histo.fYaxis.fXmin !== 0.0011) && (histo.fYaxis.fXmin !== 0)) || + ((histo.fYaxis.fXmax !== 1.1) && (histo.fYaxis.fXmax !== 1)) || (histo.fMinimum !== 0.0011) || (histo.fMaximum !== 1.1); } @@ -106464,7 +108775,7 @@ let TGraphPainter$1 = class TGraphPainter extends ObjectPainter { } /** @summary Complete moving */ - moveEnd(not_changed) { + moveEnd(not_changed, evnt) { const graph = this.getGraph(), last = graph?.fNpoints - 1; let exec = ''; @@ -106494,6 +108805,17 @@ let TGraphPainter$1 = class TGraphPainter extends ObjectPainter { else this.drawGraph(); } + } else if (not_changed) { + const fp = this.getFramePainter(), + m = pointer(evnt, fp.getFrameSvg().node()), + fw = fp.getFrameWidth(), fh = fp.getFrameHeight(), + valid_x = (m[0] >= 0) && (m[0] <= fw), + valid_y = (m[1] >= 0) && (m[1] <= fh); + if (valid_x && valid_y) { + evnt.preventDefault(); + evnt.stopPropagation(); + fp.processFrameClick({ x: m[0], y: m[1] }); + } } else { changeBin(this.#move_bin); this.#move_binindx = undefined; @@ -106693,8 +109015,6 @@ let TGraphPainter$1 = class TGraphPainter extends ObjectPainter { if (!st.fOptFit || this.getCanvPainter()?.getRootPad(true)?.fPrimitives?.arr.length) return null; - this.create_stats = true; - stats = create$1(clTPaveStats); Object.assign(stats, { fName: 'stats', fOptStat: 0, fOptFit: st.fOptFit, fBorderSize: 1, @@ -109829,7 +112149,6 @@ function createMatrix(matrix) { break; case 'TGeoGenTrans': scale = matrix.fScale; // no break, translation and rotation follows - // eslint-disable-next-line no-fallthrough case 'TGeoCombiTrans': translation = matrix.fTranslation; rotation = matrix.fRotation?.fRotationMatrix; @@ -110199,7 +112518,6 @@ createGeometry = function(shape, limit = 0) { case clTGeoHalfSpace: if (limit < 0) return 1; // half space if just plane used in composite - // eslint-disable-next-line no-fallthrough default: geoWarn(`unsupported shape type ${shape._typename}`); } @@ -110339,7 +112657,6 @@ function provideObjectInfo(obj) { break; case clTGeoTrd2: info.push(`Dy1=${conv(shape.fDy1)} Dy2=${conv(shape.fDy1)}`); // no break - // eslint-disable-next-line no-fallthrough case clTGeoTrd1: info.push(`Dx1=${conv(shape.fDx1)} Dx2=${conv(shape.fDx1)}`); break; @@ -110350,7 +112667,6 @@ function provideObjectInfo(obj) { break; case clTGeoConeSeg: info.push(`Phi1=${shape.fPhi1} Phi2=${shape.fPhi2}`); - // eslint-disable-next-line no-fallthrough case clTGeoCone: info.push(`Rmin1=${conv(shape.fRmin1)} Rmax1=${conv(shape.fRmax1)}`, `Rmin2=${conv(shape.fRmin2)} Rmax2=${conv(shape.fRmax2)}`); @@ -110358,7 +112674,6 @@ function provideObjectInfo(obj) { case clTGeoCtub: case clTGeoTubeSeg: info.push(`Phi1=${shape.fPhi1} Phi2=${shape.fPhi2}`); - // eslint-disable-next-line no-fallthrough case clTGeoEltu: case clTGeoTube: info.push(`Rmin=${conv(shape.fRmin)} Rmax=${conv(shape.fRmax)}`); @@ -110399,7 +112714,6 @@ function provideObjectInfo(obj) { break; case clTGeoGtra: info.push(`TwistAngle=${conv(shape.fTwistAngle)}`); - // eslint-disable-next-line no-fallthrough case clTGeoTrap: info.push(`Phi=${conv(shape.fPhi)} Theta=${conv(shape.fTheta)}`); break; @@ -117583,7 +119897,6 @@ class TGeoPainter extends ObjectPainter { break; case 'mix': this.#camera.add(new THREE.AmbientLight(0xefefef, p)); - // eslint-disable-next-line no-fallthrough default: // 6 point lights for (let n = 0; n < 6; ++n) { const l = new THREE.DirectionalLight(0xefefef, p); @@ -119327,7 +121640,7 @@ class TGeoPainter extends ObjectPainter { let pr; if (isNodeJs()) { - pr = Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(h => { + pr = import('node:worker_threads').then(h => { const wrk = new h.Worker(exports.source_dir.slice(7) + 'modules/geom/nodeworker.mjs', { type: 'module' }); wrk.on('message', msg => this.processWorkerReply(msg)); return wrk; @@ -121143,7 +123456,7 @@ const clTStreamerElement = 'TStreamerElement', clTStreamerObject = 'TStreamerObj clTQObject = 'TQObject', clTBasket = 'TBasket', clTDatime = 'TDatime', nameStreamerInfo = 'StreamerInfo', - kChar = 1, kShort = 2, kInt = 3, kLong = 4, kFloat = 5, kCounter = 6, + kChar$1 = 1, kShort = 2, kInt = 3, kLong = 4, kFloat = 5, kCounter = 6, kCharStar = 7, kDouble = 8, kDouble32 = 9, kLegacyChar = 10, kUChar = 11, kUShort = 12, kUInt = 13, kULong = 14, kBits = 15, kLong64 = 16, kULong64 = 17, kBool = 18, kFloat16 = 19, @@ -121179,7 +123492,13 @@ const clTStreamerElement = 'TStreamerElement', clTStreamerObject = 'TStreamerObj StlNames = ['', 'vector', 'list', 'deque', 'map', 'multimap', 'set', 'multiset', 'bitset'], // TObject bits - kIsReferenced = BIT(4), kHasUUID = BIT(5); + kIsReferenced = BIT(4), kHasUUID = BIT(5), + + // gap in http which can be merged into single http request + kMinimalHttpGap = 128, + + // temporary name assigned for file derived from binary buffer + kTmpFileName = 'localfile.root'; /** @summary Custom streamers for root classes @@ -121783,7 +124102,7 @@ function getTypeId(typname, norecursion) { case 'Bool_t': return kBool; case 'char': case 'signed char': - case 'Char_t': return kChar; + case 'Char_t': return kChar$1; case 'Color_t': case 'Style_t': case 'Width_t': @@ -121844,7 +124163,7 @@ function getArrayKind(type_name) { case 'D': return kDouble; case 'F': return kFloat; case 'S': return kShort; - case 'C': return kChar; + case 'C': return kChar$1; case 'L': return kLong; default: return -1; } @@ -122109,7 +124428,7 @@ function readMapElement(buf) { /** @summary create member entry for streamer element * @desc used for reading of data * @private */ -function createMemberStreamer(element, file) { +function createMemberStreamer(element, file, no_string) { const member = { name: element.fName, type: element.fType, fArrayLength: element.fArrayLength, @@ -122175,6 +124494,7 @@ function createMemberStreamer(element, file) { case kOffsetL + kInt: case kOffsetL + kCounter: case kOffsetL + kDouble: + case kOffsetL + kChar$1: case kOffsetL + kUChar: case kOffsetL + kShort: case kOffsetL + kUShort: @@ -122187,36 +124507,35 @@ function createMemberStreamer(element, file) { case kOffsetL + kFloat: if (element.fArrayDim < 2) { member.arrlength = element.fArrayLength; - member.func = function(buf, obj) { - obj[this.name] = buf.readFastArray(this.arrlength, this.type - kOffsetL); - }; + if ((member.type !== kOffsetL + kChar$1) || no_string) { + member.func = function(buf, obj) { + obj[this.name] = buf.readFastArray(this.arrlength, this.type - kOffsetL); + }; + } else { + member.func = function(buf, obj) { + obj[this.name] = buf.readFastString(this.arrlength); + }; + } } else { - member.arrlength = element.fMaxIndex[element.fArrayDim - 1]; member.minus1 = true; - member.func = function(buf, obj) { - obj[this.name] = buf.readNdimArray(this, (buf2, handle) => - buf2.readFastArray(handle.arrlength, handle.type - kOffsetL)); - }; - } - break; - case kOffsetL + kChar: - if (element.fArrayDim < 2) { - member.arrlength = element.fArrayLength; - member.func = function(buf, obj) { - obj[this.name] = buf.readFastString(this.arrlength); - }; - } else { - member.minus1 = true; // one dimension used for char* member.arrlength = element.fMaxIndex[element.fArrayDim - 1]; - member.func = function(buf, obj) { - obj[this.name] = buf.readNdimArray(this, (buf2, handle) => - buf2.readFastString(handle.arrlength)); - }; + if ((member.type !== kOffsetL + kChar$1) || no_string) { + member.func = function(buf, obj) { + obj[this.name] = buf.readNdimArray(this, (buf2, handle) => + buf2.readFastArray(handle.arrlength, handle.type - kOffsetL)); + }; + } else { + member.func = function(buf, obj) { + obj[this.name] = buf.readNdimArray(this, (buf2, handle) => + buf2.readFastString(handle.arrlength)); + }; + } } break; case kOffsetP + kBool: case kOffsetP + kInt: case kOffsetP + kDouble: + case kOffsetP + kChar$1: case kOffsetP + kUChar: case kOffsetP + kShort: case kOffsetP + kUShort: @@ -122228,21 +124547,20 @@ function createMemberStreamer(element, file) { case kOffsetP + kLong64: case kOffsetP + kFloat: member.cntname = element.fCountName; - member.func = function(buf, obj) { - obj[this.name] = (buf.ntou1() === 1) ? buf.readFastArray(obj[this.cntname], this.type - kOffsetP) : []; - }; - break; - case kOffsetP + kChar: - member.cntname = element.fCountName; - member.func = function(buf, obj) { - obj[this.name] = (buf.ntou1() === 1) ? buf.readFastString(obj[this.cntname]) : null; - }; + if ((member.type !== kOffsetP + kChar$1) || no_string) { + member.func = function(buf, obj) { + obj[this.name] = (buf.ntou1() === 1) ? buf.readFastArray(obj[this.cntname], this.type - kOffsetP) : []; + }; + } else { + member.func = function(buf, obj) { + obj[this.name] = (buf.ntou1() === 1) ? buf.readFastString(obj[this.cntname]) : null; + }; + } break; case kDouble32: case kOffsetL + kDouble32: case kOffsetP + kDouble32: member.double32 = true; - // eslint-disable-next-line no-fallthrough case kFloat16: case kOffsetL + kFloat16: case kOffsetP + kFloat16: @@ -122250,20 +124568,17 @@ function createMemberStreamer(element, file) { member.factor = 1 / element.fFactor; member.min = element.fXmin; member.read = function(buf) { return buf.ntou4() * this.factor + this.min; }; - } else - if ((element.fXmin === 0) && member.double32) - member.read = function(buf) { return buf.ntof(); }; - else { - member.nbits = Math.round(element.fXmin); - if (member.nbits === 0) - member.nbits = 12; - member.dv = new DataView(new ArrayBuffer(8), 0); // used to cast from uint32 to float32 - member.read = function(buf) { - const theExp = buf.ntou1(), theMan = buf.ntou2(); - this.dv.setUint32(0, (theExp << 23) | ((theMan & ((1 << (this.nbits + 1)) - 1)) << (23 - this.nbits))); - return ((1 << (this.nbits + 1) & theMan) ? -1 : 1) * this.dv.getFloat32(0); - }; - } + } else if ((element.fXmin === 0) && member.double32) + member.read = function(buf) { return buf.ntof(); }; + else { + member.nbits = Math.round(element.fXmin) || 12; + member.dv = new DataView(new ArrayBuffer(8), 0); // used to cast from uint32 to float32 + member.read = function(buf) { + const theExp = buf.ntou1(), theMan = buf.ntou2(); + this.dv.setUint32(0, (theExp << 23) | ((theMan & ((1 << (this.nbits + 1)) - 1)) << (23 - this.nbits))); + return ((1 << (this.nbits + 1) & theMan) ? -1 : 1) * this.dv.getFloat32(0); + }; + } member.readarr = function(buf, len) { const arr = this.double32 ? new Float64Array(len) : new Float32Array(len); @@ -122274,23 +124589,21 @@ function createMemberStreamer(element, file) { if (member.type < kOffsetL) member.func = function(buf, obj) { obj[this.name] = this.read(buf); }; - else - if (member.type > kOffsetP) { - member.cntname = element.fCountName; - member.func = function(buf, obj) { - obj[this.name] = (buf.ntou1() === 1) ? this.readarr(buf, obj[this.cntname]) : null; - }; - } else - if (element.fArrayDim < 2) { - member.arrlength = element.fArrayLength; - member.func = function(buf, obj) { obj[this.name] = this.readarr(buf, this.arrlength); }; - } else { - member.arrlength = element.fMaxIndex[element.fArrayDim - 1]; - member.minus1 = true; - member.func = function(buf, obj) { - obj[this.name] = buf.readNdimArray(this, (buf2, handle) => handle.readarr(buf2, handle.arrlength)); - }; - } + else if (member.type > kOffsetP) { + member.cntname = element.fCountName; + member.func = function(buf, obj) { + obj[this.name] = (buf.ntou1() === 1) ? this.readarr(buf, obj[this.cntname]) : null; + }; + } else if (element.fArrayDim < 2) { + member.arrlength = element.fArrayLength; + member.func = function(buf, obj) { obj[this.name] = this.readarr(buf, this.arrlength); }; + } else { + member.arrlength = element.fMaxIndex[element.fArrayDim - 1]; + member.minus1 = true; + member.func = function(buf, obj) { + obj[this.name] = buf.readNdimArray(this, (buf2, handle) => handle.readarr(buf2, handle.arrlength)); + }; + } break; case kAnyP: @@ -122352,7 +124665,7 @@ function createMemberStreamer(element, file) { }; break; } - case kChar: + case kChar$1: member.func = function(buf, obj) { obj[this.name] = buf.ntoi1(); }; break; case kCharStar: @@ -123379,7 +125692,7 @@ async function R__unzip(arr, tgtsize, noalert, src_shift) { promise = new Promise(resolveFunc => { internals._ZstdInit.push(resolveFunc); }); else { internals._ZstdInit = []; - promise = (isNodeJs() ? Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }) : Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; })) + promise = (isNodeJs() ? import('@oneidentity/zstd-js') : import('./base/zstd.mjs')) .then(({ ZstdInit }) => ZstdInit()) .then(({ ZstdStream }) => { internals._ZstdStream = ZstdStream; @@ -123401,7 +125714,7 @@ async function R__unzip(arr, tgtsize, noalert, src_shift) { return nextPortion(); }); } else if (fmt === 'LZMA') { - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(lzma => { + return import('./base/lzma.mjs').then(lzma => { const expected_len = (getCode(curr + 6) & 0xff) | ((getCode(curr + 7) & 0xff) << 8) | ((getCode(curr + 8) & 0xff) << 16), reslen = lzma.decompress(uint8arr, tgt8arr, expected_len); fullres += reslen; @@ -123671,7 +125984,7 @@ class TBuffer { for (; i < n; ++i, o += 2) array[i] = view.getUint16(o); break; - case kChar: + case kChar$1: array = new Int8Array(n); for (; i < n; ++i) array[i] = view.getInt8(o++); @@ -124132,8 +126445,14 @@ class TFile { this.fAcceptRanges = false; } - const pos = Math.max(this.fURL.lastIndexOf('/'), this.fURL.lastIndexOf('\\')); - this.fFileName = pos >= 0 ? this.fURL.slice(pos + 1) : this.fURL; + this.assignFileName(this.fURL); + } + + assignFileName(url) { + if (isStr(url)) { + const pos = Math.max(url.lastIndexOf('/'), url.lastIndexOf('\\')); + this.fFileName = (pos >= 0) && (pos < url.length - 2) ? url.slice(pos + 1) : url; + } } /** @summary Set timeout for File instance @@ -124172,6 +126491,63 @@ class TFile { * @private */ async _open() { return this.readKeys(); } + /** @summary check if requested segments can be reordered or merged + * @private */ + #checkNeedReorder(place) { + let res = false, resort = false; + for (let n = 0; n < place.length - 2; n += 2) { + if (place[n] > place[n + 2]) + res = resort = true; + if (place[n] + place[n + 1] > place[n + 2] - kMinimalHttpGap) + res = true; + } + if (!res) { + return { + place, + blobs: [], + expectedSize(indx) { return this.place[indx + 1]; }, + addBuffer(indx, buf, o) { + this.blobs[indx / 2] = new DataView(buf, o, this.place[indx + 1]); + } + }; + } + + res = { place, reorder: [], place_new: [], blobs: [] }; + + for (let n = 0; n < place.length; n += 2) + res.reorder.push({ pos: place[n], len: place[n + 1], indx: [n] }); + + if (resort) + res.reorder.sort((a, b) => { return a.pos - b.pos; }); + + for (let n = 0; n < res.reorder.length - 1; n++) { + const curr = res.reorder[n], + next = res.reorder[n + 1]; + if (curr.pos + curr.len + kMinimalHttpGap > next.pos) { + curr.indx.push(...next.indx); + curr.len = next.pos + next.len - curr.pos; + res.reorder.splice(n + 1, 1); // remove segment + n--; + } + } + + res.reorder.forEach(elem => res.place_new.push(elem.pos, elem.len)); + + res.expectedSize = function(indx) { + return this.reorder[indx / 2].len; + }; + + res.addBuffer = function(indx, buf, o) { + const elem = this.reorder[indx / 2], + pos0 = elem.pos; + elem.indx.forEach(indx0 => { + this.blobs[indx0 / 2] = new DataView(buf, o + this.place[indx0] - pos0, this.place[indx0 + 1]); + }); + }; + + return res; + } + /** @summary read buffer(s) from the file * @return {Promise} with read buffers * @private */ @@ -124179,10 +126555,13 @@ class TFile { if ((this.fFileContent !== null) && !filename && (!this.fAcceptRanges || this.fFileContent.canExtract(place))) return this.fFileContent.extract(place); + const reorder = this.#checkNeedReorder(place); + if (reorder?.place_new) + place = reorder?.place_new; + let resolveFunc, rejectFunc; const file = this, first_block = (place[0] === 0) && (place.length === 2), - blobs = [], // array of requested segments promise = new Promise((resolve, reject) => { resolveFunc = resolve; rejectFunc = reject; @@ -124208,12 +126587,15 @@ class TFile { } } - function send_new_request(increment) { - if (increment) { + function send_new_request(arg) { + if (arg === 'noranges') { + file.fMaxRanges = 1; + last = Math.min(last, first + file.fMaxRanges * 2); + } else if (arg) { first = last; last = Math.min(first + file.fMaxRanges * 2, place.length); if (first >= place.length) - return resolveFunc(blobs); + return resolveFunc(reorder.blobs.length === 1 ? reorder.blobs[0] : reorder.blobs); } let fullurl = fileurl, ranges = 'bytes', totalsz = 0; @@ -124230,17 +126612,14 @@ class TFile { // when read first block, allow to read more - maybe ranges are not supported and full file content will be returned if (file.fAcceptRanges && first_block) - totalsz = Math.max(totalsz, 1e7); + totalsz = Math.max(totalsz, 1e5); - return createHttpRequest(fullurl, 'buf', read_callback, undefined, true).then(xhr => { + return createHttpRequest(fullurl, 'buf', read_callback, undefined, true, file.fTimeout).then(xhr => { if (file.fAcceptRanges) { xhr.setRequestHeader('Range', ranges); xhr.expected_size = Math.max(Math.round(1.1 * totalsz), totalsz + 200); // 200 if offset for the potential gzip } - if (file.fTimeout) - xhr.timeout = file.fTimeout; - if (isFunc(progress_callback) && isFunc(xhr.addEventListener)) { let sum1 = 0, sum2 = 0, sum_total = 0; for (let n = 1; n < place.length; n += 2) { @@ -124348,70 +126727,34 @@ class TFile { // if only single segment requested, return result as is if (last - first === 2) { - const b = new DataView(res); - if (place.length === 2) - return resolveFunc(b); - blobs.push(b); + reorder.addBuffer(first, res, 0); return send_new_request(true); } // object to access response data - const hdr = this.getResponseHeader('Content-Type'), - ismulti = isStr(hdr) && (hdr.indexOf('multipart') >= 0), - view = new DataView(res); - - if (!ismulti) { - // server may returns simple buffer, which combines all segments together - - const hdr_range = this.getResponseHeader('Content-Range'); - let segm_start = 0, segm_last = -1; - - if (isStr(hdr_range) && hdr_range.indexOf('bytes') >= 0) { - const parts = hdr_range.slice(hdr_range.indexOf('bytes') + 6).split(/[\s-/]+/); - if (parts.length === 3) { - segm_start = Number.parseInt(parts[0]); - segm_last = Number.parseInt(parts[1]); - if (!Number.isInteger(segm_start) || !Number.isInteger(segm_last) || (segm_start > segm_last)) { - segm_start = 0; - segm_last = -1; - } - } - } - - let canbe_single_segment = (segm_start <= segm_last); - for (let n = first; n < last; n += 2) { - if ((place[n] < segm_start) || (place[n] + place[n + 1] - 1 > segm_last)) - canbe_single_segment = false; - } + const hdr = this.getResponseHeader('Content-Type'); - if (canbe_single_segment) { - for (let n = first; n < last; n += 2) - blobs.push(new DataView(res, place[n] - segm_start, place[n + 1])); - return send_new_request(true); - } - - if ((file.fMaxRanges === 1) || !first) - return rejectFunc(Error('Server returns normal response when multipart was requested, disable multirange support')); - - file.fMaxRanges = 1; - last = Math.min(last, file.fMaxRanges * 2); - - return send_new_request(); + if (!isStr(hdr) || (hdr.indexOf('multipart') < 0)) { + console.error('Did not found multipart in content-type - fallback to single range request'); + return send_new_request('noranges'); } // multipart messages requires special handling const indx = hdr.indexOf('boundary='); - let boundary = '', n = first, o = 0, normal_order = true; - if (indx > 0) { - boundary = hdr.slice(indx + 9); - if ((boundary[0] === '"') && (boundary.at(-1) === '"')) - boundary = boundary.slice(1, boundary.length - 1); - boundary = '--' + boundary; - } else - console.error('Did not found boundary id in the response header'); + if (indx <= 0) { + console.error('Did not found boundary id in the response header - fallback to single range request'); + return send_new_request('noranges'); + } + + let boundary = hdr.slice(indx + 9); + if ((boundary[0] === '"') && (boundary.at(-1) === '"')) + boundary = boundary.slice(1, boundary.length - 1); + boundary = '--' + boundary; - while (n < last) { + const view = new DataView(res); + + for (let n = first, o = 0; n < last; n += 2) { let code1, code2 = view.getUint8(o), nline = 0, line = '', finish_header = false, segm_start = 0, segm_last = -1; @@ -124430,6 +126773,7 @@ class TFile { if (parts.length === 3) { segm_start = Number.parseInt(parts[0]); segm_last = Number.parseInt(parts[1]); + // TODO: check for consistency if (!Number.isInteger(segm_start) || !Number.isInteger(segm_last) || (segm_start > segm_last)) { segm_start = 0; segm_last = -1; @@ -124452,44 +126796,16 @@ class TFile { o++; } - if (!finish_header) - return rejectFunc(Error('Cannot decode header in multipart message')); - - if (segm_start > segm_last) { - // fall-back solution, believe that segments same as requested - blobs.push(new DataView(res, o, place[n + 1])); - o += place[n + 1]; - n += 2; - } else if (normal_order) { - const n0 = n; - while ((n < last) && (place[n] >= segm_start) && (place[n] + place[n + 1] - 1 <= segm_last)) { - blobs.push(new DataView(res, o + place[n] - segm_start, place[n + 1])); - n += 2; - } + const segm_size = segm_last - segm_start + 1; - if (n > n0) - o += (segm_last - segm_start + 1); - else - normal_order = false; + if (!finish_header || (segm_size <= 0) || (reorder.expectedSize(n) !== segm_size)) { + console.error('Failure decoding multirange header - fallback to single range request'); + return send_new_request('noranges'); } - if (!normal_order) { - // special situation when server reorder segments in the reply - let isany = false; - for (let n1 = n; n1 < last; n1 += 2) { - if ((place[n1] >= segm_start) && (place[n1] + place[n1 + 1] - 1 <= segm_last)) { - blobs[n1 / 2] = new DataView(res, o + place[n1] - segm_start, place[n1 + 1]); - isany = true; - } - } - if (!isany) - return rejectFunc(Error(`Provided fragment ${segm_start} - ${segm_last} out of requested multi-range request`)); - - while (blobs[n / 2]) - n += 2; + reorder.addBuffer(n, res, o); - o += (segm_last - segm_start + 1); - } + o += segm_size; } send_new_request(true); @@ -124788,10 +127104,14 @@ class TFile { // this part typically read from the header, no need to optimize return this.readBuffer([this.fBEGIN, Math.max(300, nbytes)]); }).then(blob3 => { - const buf3 = new TBuffer(blob3, 0, this); + const buf3 = new TBuffer(blob3, 0, this), + key = buf3.readTKey(); - // keep only title from TKey data - this.fTitle = buf3.readTKey().fTitle; + this.fTitle = key.fTitle; + if (this.fURL === kTmpFileName) { + this.fURL = this.fFullURL = key.fName; + this.assignFileName(key.fName); + } buf3.locate(this.fNbytesName); @@ -125012,9 +127332,8 @@ class TLocalFile extends TFile { this.fUseStampPar = false; this.fLocalFile = file; this.fEND = file.size; - this.fFullURL = file.name; - this.fURL = file.name; - this.fFileName = file.name; + this.fFullURL = this.fURL = file.name; + this.assignFileName(file.name); } /** @summary Open local file @@ -125071,22 +127390,21 @@ class TNodejsFile extends TFile { super(null); this.fUseStampPar = false; this.fEND = 0; - this.fFullURL = filename; - this.fURL = filename; - this.fFileName = filename; + this.fFullURL = this.fURL = filename; + this.assignFileName(filename); } /** @summary Open file in node.js * @return {Promise} after file keys are read */ async _open() { - return Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(fs => { + return import('fs').then(fs => { this.fs = fs; return new Promise((resolve, reject) => { - this.fs.open(this.fFileName, 'r', (status, fd) => { + this.fs.open(this.fFullURL, 'r', (status, fd) => { if (status) { console.log(status.message); - reject(Error(`Not possible to open ${this.fFileName} inside node.js`)); + reject(Error(`Not possible to open ${this.fFullURL} inside node.js`)); } else { const stats = this.fs.fstatSync(fd); this.fEND = stats.size; @@ -125108,7 +127426,7 @@ class TNodejsFile extends TFile { } if (!this.fs || !this.fd) { - reject(Error(`File is not opened ${this.fFileName}`)); + reject(Error(`File is not opened ${this.fFullURL}`)); return; } @@ -125151,6 +127469,7 @@ class FileProxy { getFileName() { return ''; } getFileSize() { return 0; } async readBuffer(/* pos, sz */) { return null; } + closeFile() {} } // class FileProxy @@ -125177,12 +127496,8 @@ class TProxyFile extends TFile { if (!res) return false; this.fEND = this.proxy.getFileSize(); - this.fFullURL = this.fURL = this.fFileName = this.proxy.getFileName(); - if (isStr(this.fFileName)) { - const p = this.fFileName.lastIndexOf('/'); - if ((p > 0) && (p < this.fFileName.length - 4)) - this.fFileName = this.fFileName.slice(p + 1); - } + this.fFullURL = this.fURL = this.proxy.getFileName(); + this.assignFileName(this.fURL); return this.readKeys(); }); } @@ -125194,7 +127509,7 @@ class TProxyFile extends TFile { return Promise.reject(Error(`Cannot access other file ${filename}`)); if (!this.proxy) - return Promise.reject(Error(`File is not opened ${this.fFileName}`)); + return Promise.reject(Error(`File is not opened ${this.fFullURL}`)); if (isFunc(this.proxy.readBuffers)) { return this.proxy.readBuffers(place).then(arr => { @@ -125211,6 +127526,15 @@ class TProxyFile extends TFile { return Promise.all(arr); } + /** @summary Fully cleanup TProxyFile data + * @private */ + delete() { + super.delete(); + if (isFunc(this.proxy?.closeFile)) + this.proxy.closeFile(); + delete this.proxy; + } + } // class TProxyFile @@ -125245,7 +127569,7 @@ function openFile(arg, opts) { file = new TProxyFile(arg); if (!file && isObject(arg) && (arg instanceof ArrayBuffer)) { - file = new TFile('localfile.root'); + file = new TFile(kTmpFileName); file.assignFileContent(arg); } @@ -126182,6 +128506,10 @@ class TDrawSelector extends TSelector { case 'dump': args.dump = true; break; + case 'dumpall': + args.dump = true; + args.numentries = this.getNumEntries(tree); + break; case 'staged': args.staged = true; break; @@ -126228,7 +128556,10 @@ class TDrawSelector extends TSelector { } if (harg === 'dump') args.dump = true; - else if (harg === 'elist') + else if (harg === 'dumpall') { + args.dump = true; + args.numentries = this.getNumEntries(tree); + } else if (harg === 'elist') args.dump_entries = true; else if (harg.indexOf('Graph') === 0) args.graph = true; @@ -126404,7 +128735,7 @@ class TDrawSelector extends TSelector { this.leaf = args.leaf; // branch object remains, therefore we need to copy fields to see them all - this.copy_fields = ((args.branch.fLeaves?.arr.length > 1) || args.branch.fBranches?.arr.length) && !args.leaf; + this.copy_fields = args.copy_fields ?? (((args.branch.fLeaves?.arr.length > 1) || args.branch.fBranches?.arr.length) && !args.leaf); this.addBranch(branch, 'br0', args.direct_branch); // add branch @@ -127100,10 +129431,11 @@ async function treeProcess(tree, selector, args) { case 'TLeafF': datakind = kFloat; break; case 'TLeafD': datakind = kDouble; break; case 'TLeafO': datakind = kBool; break; - case 'TLeafB': datakind = leaf.fIsUnsigned ? kUChar : kChar; break; + case 'TLeafB': datakind = leaf.fIsUnsigned ? kUChar : kChar$1; break; case 'TLeafS': datakind = leaf.fIsUnsigned ? kUShort : kShort; break; case 'TLeafI': datakind = leaf.fIsUnsigned ? kUInt : kInt; break; case 'TLeafL': datakind = leaf.fIsUnsigned ? kULong64 : kLong64; break; + case 'TLeafG': datakind = leaf.fIsUnsigned ? kULong : kLong; break; case 'TLeafC': datakind = kTString; break; default: return null; } @@ -127752,7 +130084,7 @@ async function treeProcess(tree, selector, args) { for (let k = 0; k < handle.arr.length; ++k) { const elem = handle.arr[k]; - if ((elem.type <= 0) || (elem.type >= kOffsetL) || (elem.type === kCharStar)) + if ((elem.type <= 0) || (elem.type >= kOffsetL) || (elem.type === kCharStar) || (elem.type === kFloat16) || (elem.type === kDouble32)) handle.process_arrays = false; } @@ -127770,7 +130102,7 @@ async function treeProcess(tree, selector, args) { elem.fArrayDim = 1; elem.fMaxIndex[0] = 10; // 10 if artificial number, will be replaced during reading - item.arrmember = createMemberStreamer(elem, handle.file); + item.arrmember = createMemberStreamer(elem, handle.file, true); } } } else @@ -162989,7 +165321,7 @@ async function makePDF(svg, args) { return res; }; - pr = Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(handle => { + pr = import('canvas').then(handle => { globalThis.Image = handle.Image; }); } @@ -163189,22 +165521,22 @@ const drawFuncs = { lst: [ { name: 'kind:Command', icon: 'img_execute', execute: true }, { name: 'TFolder', icon: 'img_folder', icon2: 'img_folderopen', noinspect: true, get_expand: () => import_h().then(h => h.folderHierarchy) }, { name: 'TTask', icon: 'img_task', get_expand: () => import_h().then(h => h.taskHierarchy), for_derived: true }, - { name: clTTree, icon: 'img_tree', get_expand: () => Promise.resolve().then(function () { return tree; }).then(h => h.treeHierarchy), draw: () => import_tree().then(h => h.drawTree), dflt: 'expand', opt: 'player;testio', shift: kInspect, pm: true }, + { name: clTTree, icon: 'img_tree', get_expand: () => Promise.resolve().then(function () { return tree; }).then(h => h.treeHierarchy), draw: () => import_tree().then(h => h.drawTree), dflt: 'expand', opt: 'player;testio', shift: kInspect, pm: true, transform: true }, { name: 'TNtuple', sameas: clTTree }, { name: 'TNtupleD', sameas: clTTree }, - { name: clTBranchFunc, icon: 'img_leaf_method', draw: () => import_tree().then(h => h.drawTree), opt: ';dump', noinspect: true }, - { name: /^TBranch/, icon: 'img_branch', draw: () => import_tree().then(h => h.drawTree), dflt: 'expand', opt: ';dump', ctrl: 'dump', shift: kInspect, ignore_online: true, always_draw: true }, - { name: /^TLeaf/, icon: 'img_leaf', noexpand: true, draw: () => import_tree().then(h => h.drawTree), opt: ';dump', ctrl: 'dump', ignore_online: true, always_draw: true }, - { name: 'ROOT::RNTuple', icon: 'img_tree', get_expand: () => Promise.resolve().then(function () { return rntuple; }).then(h => h.tupleHierarchy), draw: () => Promise.resolve().then(function () { return RNTuple; }).then(h => h.drawRNTuple), dflt: 'expand', pm: true }, - { name: 'ROOT::RNTupleField', icon: 'img_leaf', draw: () => Promise.resolve().then(function () { return RNTuple; }).then(h => h.drawRNTuple), opt: ';dump', ctrl: 'dump', shift: kInspect, ignore_online: true, always_draw: true }, + { name: clTBranchFunc, icon: 'img_leaf_method', draw: () => import_tree().then(h => h.drawTree), opt: ';dump;dumpall', noinspect: true, transform: true }, + { name: /^TBranch/, icon: 'img_branch', draw: () => import_tree().then(h => h.drawTree), dflt: 'expand', opt: ';dump;dumpall', ctrl: 'dump', shift: kInspect, ignore_online: true, always_draw: true, transform: true }, + { name: /^TLeaf/, icon: 'img_leaf', noexpand: true, draw: () => import_tree().then(h => h.drawTree), opt: ';dump;dumpall', ctrl: 'dump', ignore_online: true, always_draw: true, transform: true }, + { name: 'ROOT::RNTuple', icon: 'img_tree', get_expand: () => Promise.resolve().then(function () { return rntuple; }).then(h => h.tupleHierarchy), draw: () => Promise.resolve().then(function () { return RNTuple; }).then(h => h.drawRNTuple), dflt: 'expand', pm: true, transform: true }, + { name: 'ROOT::RNTupleField', icon: 'img_leaf', draw: () => Promise.resolve().then(function () { return RNTuple; }).then(h => h.drawRNTuple), opt: ';dump;dumpall', ctrl: 'dump', shift: kInspect, ignore_online: true, always_draw: true, transform: true }, { name: clTList, icon: 'img_list', draw: () => import_h().then(h => h.drawList), get_expand: () => import_h().then(h => h.listHierarchy), dflt: 'expand' }, { name: clTHashList, sameas: clTList }, { name: clTObjArray, sameas: clTList }, { name: clTClonesArray, sameas: clTList }, { name: clTMap, sameas: clTList }, { name: clTColor, icon: 'img_color' }, - { name: clTFile, icon: 'img_file', noinspect: true }, - { name: 'TMemFile', icon: 'img_file', noinspect: true }, + { name: clTFile, icon: 'img_file', noinspect: true, pm: true }, + { name: 'TMemFile', icon: 'img_file', noinspect: true, pm: true }, { name: clTStyle, icon: 'img_question', noexpand: true }, { name: 'Session', icon: 'img_globe' }, { name: 'kind:TopFolder', icon: 'img_base' }, @@ -163228,19 +165560,28 @@ const drawFuncs = { lst: [ /** @summary Register draw function for the class - * @desc List of supported draw options could be provided, separated with ';' * @param {object} args - arguments - * @param {string|regexp} args.name - class name or regexp pattern + * @param {string|regexp} args.name - class name or regexp pattern or '*' * @param {function} [args.func] - draw function + * @param {string} [args.sameas] - let behave same as specified class * @param {function} [args.draw] - async function to load draw function * @param {function} [args.class] - async function to load painter class with static draw function * @param {boolean} [args.direct] - if true, function is just Redraw() method of ObjectPainter * @param {string} [args.opt] - list of supported draw options (separated with semicolon) like 'col;scat;' * @param {string} [args.icon] - icon name shown for the class in hierarchy browser * @param {string} [args.draw_field] - draw only data member from object, like fHistogram + * @param {string} [args.noinspect] - disable inspect + * @param {string} [args.noexpand] - disable expand + * @param {string} [args.pm] - always show plus or minus sign even when no child items exists + * @desc List of supported draw options could be provided, separated with ';' + * If args.name parameter is '*', function will be invoked before object drawing. + * If such function does not return value - normal drawing will be continued. * @protected */ function addDrawFunc(args) { - drawFuncs.lst.push(args); + if (args?.name === '*') + internals._alt_draw = isFunc(args.func) ? args.func : null; + else + drawFuncs.lst.push(args); return args; } @@ -163451,6 +165792,12 @@ async function draw(dom, obj, opt) { if (handle.draw_field && obj[handle.draw_field]) return draw(dom, obj[handle.draw_field], opt || handle.draw_field_opt); + if (internals._alt_draw && !handle.transform) { + const v = internals._alt_draw(dom, obj, opt); + if (v) + return v; + } + if (!canDrawHandle(handle)) { if (opt && (opt.indexOf('same') >= 0)) { const main_painter = getElementMainPainter(dom); @@ -164388,7 +166735,11 @@ function objectHierarchy(top, obj, args = undefined) { } } } - } else if ((typeof fld === 'number') || (typeof fld === 'boolean') || (typeof fld === 'bigint')) { + } else if (typeof fld === 'bigint') { + simple = true; + item._value = fld.toString() + 'n'; + item._vclass = cssValueNum; + } else if ((typeof fld === 'number') || (typeof fld === 'boolean')) { simple = true; if (key === 'fBits') item._value = '0x' + fld.toString(16); @@ -164588,7 +166939,6 @@ function parseAsArray(val) { nbr--; break; } - // eslint-disable-next-line no-fallthrough case ',': if (nbr === 0) { let sub = val.substring(last, indx).trim(); @@ -165248,6 +167598,9 @@ class HierarchyPainter extends BasePainter { if (!element_title) element_title = element_name; + if (hitem._filter) + element_name += ' *'; + d3a.attr('title', element_title) .text(element_name + ('_value' in hitem ? ':' : '')) .style('background', hitem._background ? hitem._background : null); @@ -165267,6 +167620,8 @@ class HierarchyPainter extends BasePainter { for (let i = 0; i < hitem._childs.length; ++i) { const chld = hitem._childs[i]; chld._parent = hitem; + if (hitem._filter && chld._name && chld._name.indexOf(hitem._filter) < 0) + continue; if (!this.addItemHtml(chld, d3chlds, i)) break; // if too many items, skip rest } @@ -165696,21 +168051,21 @@ class HierarchyPainter extends BasePainter { /** @summary alternative context menu, used in the object inspector * @private */ direct_contextmenu(evnt, elem) { - evnt.preventDefault(); const itemname = select(elem.parentNode.parentNode).attr('item'), hitem = this.findItem(itemname); - if (!hitem) + if (!hitem || !isFunc(this.fill_context)) return; - if (isFunc(this.fill_context)) { - createMenu(evnt, this).then(menu => { - this.fill_context(menu, hitem); - if (menu.size() > 0) { - menu.tree_node = elem.parentNode; - menu.show(); - } - }); - } + evnt.preventDefault(); + evnt.stopPropagation(); + + createMenu(evnt, this).then(menu => { + this.fill_context(menu, hitem); + if (menu.size() > 0) { + menu.tree_node = elem.parentNode; + menu.show(); + } + }); } /** @summary Fills settings menu items @@ -165756,11 +168111,12 @@ class HierarchyPainter extends BasePainter { /** @summary Handle context menu in the hierarchy * @private */ tree_contextmenu(evnt, elem) { - evnt.preventDefault(); const itemname = select(elem.parentNode.parentNode).attr('item'), hitem = this.findItem(itemname); if (!hitem) return; + evnt.preventDefault(); + evnt.stopPropagation(); const onlineprop = this.getOnlineProp(itemname), fileprop = this.getFileProp(itemname); @@ -165930,6 +168286,15 @@ class HierarchyPainter extends BasePainter { if (hitem._childs === undefined) menu.add('Expand', () => this.expandItem(itemname), 'Expand content of object'); else { + if (sett.handle?.pm || (hitem._childs.length > 25)) { + menu.add('Filter...', () => menu.input('Enter items to select', hitem._filter, f => { + const changed = hitem._filter !== f; + hitem._filter = f; + if (changed) + this.updateTreeNode(hitem); + }), 'Filter out items based on input pattern'); + } + menu.add('Unexpand', () => { hitem._more = true; delete hitem._childs; @@ -166107,10 +168472,8 @@ class HierarchyPainter extends BasePainter { if (use_dflt_opt && !drawopt && handle?.dflt && (handle.dflt !== kExpand)) drawopt = handle.dflt; - if (dom) { - const func = updating ? redraw : draw; - return func(dom, obj, drawopt).then(p => complete(p)).catch(err => complete(null, err)); - } + if (dom) + return (updating ? redraw : draw)(dom, obj, drawopt).then(p => complete(p)).catch(err => complete(null, err)); let did_activate = false; const arr = []; @@ -166155,8 +168518,8 @@ class HierarchyPainter extends BasePainter { mdi.activateFrame(frame); return draw(frame, obj, drawopt) - .then(p => complete(p)) - .catch(err => complete(null, err)); + .then(p => complete(p)) + .catch(err => complete(null, err)); }); }); } @@ -166718,8 +169081,9 @@ class HierarchyPainter extends BasePainter { if ((hitem._more === false) || (!hitem._parent && hitem._childs)) return; - if (hitem._childs && hitem._isopen) { - hitem._isopen = false; + // for the file expand always just toggle isopen flag + if (hitem._childs && (hitem._isopen || hitem._file)) { + hitem._isopen = !hitem._isopen; if (!silent) this.updateTreeNode(hitem, d3cont); return; @@ -167113,7 +169477,7 @@ class HierarchyPainter extends BasePainter { handleAfterRequest(findFunction(item._after_request)); // v6 support } else handleAfterRequest(draw_handle?.after_request); - }, undefined, true).then(xhr => { + }, undefined, true, settings.ServerTimeout).then(xhr => { itemreq = xhr; xhr.send(null); }); @@ -167429,7 +169793,7 @@ class HierarchyPainter extends BasePainter { } // check that we can found frame where drawing should be done - if (!document.getElementById(this.disp_frameid)) + if (!this.disp_frameid || !document.getElementById(this.disp_frameid)) return null; if (isBatchMode()) @@ -167629,8 +169993,8 @@ class HierarchyPainter extends BasePainter { if (!browser_configured && (browser.screenWidth <= 640)) browser_kind = 'float'; - this.no_select = getOption('noselect'); - this.top_info = getOption('info'); + this.no_select ??= getOption('noselect'); + this.top_info ??= getOption('info'); if (getOption('files_monitoring') !== null) this.files_monitoring = true; @@ -167890,7 +170254,7 @@ class HierarchyPainter extends BasePainter { ''; } else if (!this.no_select) { const myDiv = select('#' + this.gui_div), - files = myDiv.attr('files') || '../files/hsimple.root', + files = myDiv.attr('files') || 'https://root.cern/js/files/hsimple.root', path = decodeUrl().get('path') || myDiv.attr('path') || '', arrFiles = files.split(';'); @@ -167924,6 +170288,7 @@ class HierarchyPainter extends BasePainter { const title_elem = this.brlayout.setBrowserTitle(this.top_info || (this.is_online ? 'ROOT online server' : 'Read a ROOT file')); title_elem?.on('contextmenu', evnt => { evnt.preventDefault(); + evnt.stopPropagation(); createMenu(evnt).then(menu => { this.fillSettingsMenu(menu, true); menu.show(); @@ -168251,6 +170616,12 @@ function readStyleFromURL(url) { if (d.has('prefer_saved_points')) settings.PreferSavedPoints = true; + if (d.has('tmout')) + settings.ServerTimeout = parseFloat(d.get('tmout')); + + if (d.has('ftmout')) + settings.FilesTimeout = parseFloat(d.get('ftmout')); + const tf1_style = d.get('tf1'); if (tf1_style === 'curve') settings.FuncAsCurve = true; @@ -168442,7 +170813,7 @@ async function buildGUI(gui_element, gui_kind = '') { myDiv.html(''); // clear element - const d = decodeUrl(), getSize = name => { + const nb = (gui_kind === 'notebook'), d = decodeUrl(), getSize = name => { const res = d.has(name) ? d.get(name).split('x') : []; if (res.length !== 2) return null; @@ -168457,7 +170828,7 @@ async function buildGUI(gui_element, gui_kind = '') { else if ((gui_kind === 'nobrowser') || d.has('nobrowser') || (myDiv.attr('nobrowser') && myDiv.attr('nobrowser') !== 'false')) nobrowser = true; - if (myDiv.attr('ignoreurl') === 'true') + if (nb || (myDiv.attr('ignoreurl') === 'true')) settings.IgnoreUrlOptions = true; readStyleFromURL(); @@ -168488,6 +170859,10 @@ async function buildGUI(gui_element, gui_kind = '') { if (drawing || isBatchMode()) hpainter.exclude_browser = true; hpainter.start_without_browser = nobrowser; + if (nb) { + hpainter.no_select = true; + hpainter.top_info = 'ROOT notebook'; + } return hpainter.startGUI(myDiv).then(() => { if (!nobrowser) @@ -168505,13 +170880,6 @@ async function buildGUI(gui_element, gui_kind = '') { }).then(() => hpainter); } -var _rollup_plugin_ignore_empty_module_placeholder = {}; - -var _rollup_plugin_ignore_empty_module_placeholder$1 = /*#__PURE__*/Object.freeze({ -__proto__: null, -default: _rollup_plugin_ignore_empty_module_placeholder -}); - /** @summary Draw TEllipse * @private */ function drawEllipse() { @@ -168893,6 +171261,12 @@ async function drawTreeDrawResult(dom, obj, opt) { if (!typ || !isStr(typ)) return Promise.reject(Error('Object without type cannot be draw with TTree')); + if (internals._alt_draw) { + const v = internals._alt_draw(dom, obj, opt); + if (v) + return v; + } + if (typ.indexOf(clTH1) === 0) return TH1Painter.draw(dom, obj, opt); if (typ.indexOf(clTH2) === 0) @@ -169345,6 +171719,11 @@ class TTextPainter extends ObjectPainter { pp = this.getPadPainter(), fp = this.getFramePainter(), is_url = text.fName.startsWith('http://') || text.fName.startsWith('https://'); + + // special handling of dummy frame painter + if (fp?.getDrawDom() === null) + return this; + let fact = 1, use_frame = false; this.createAttText({ attr: text }); @@ -169828,7 +172207,11 @@ let THStackPainter$2 = class THStackPainter extends ObjectPainter { o.auto += ' ' + f; }); - o.pads = d.check('PADS'); + if (d.check('PADS', true)) { + o.pads = true; + o.pads_columns = d.partAsInt(); + } + if (o.pads) o.nostack = true; @@ -170045,7 +172428,12 @@ let THStackPainter$2 = class THStackPainter extends ObjectPainter { if (o.pads) { pr = ensureTCanvas(this, false).then(() => { pad_painter = this.getPadPainter(); - return pad_painter.divide(o.nhist, 0, true); + let nx = o.nhist, ny = 0; + if (o.pads_columns) { + nx = o.pads_columns; + ny = Math.ceil(o.nhist / nx); + } + return pad_painter.divide(nx, ny, true); }); } else { if (!o.nostack) @@ -174052,6 +176440,7 @@ let TMultiGraphPainter$2 = class TMultiGraphPainter extends ObjectPainter { #auto; // extra options for auto colors #is3d; // if 3d drawing #pads; // pads draw option + #pads_columns; // number pads columns /** @summary Create painter * @param {object|string} dom - DOM element for drawing or element id @@ -174345,7 +176734,9 @@ let TMultiGraphPainter$2 = class TMultiGraphPainter extends ObjectPainter { this.#is3d = d.check('3D'); this.#auto = ''; - this.#pads = d.check('PADS'); + this.#pads = d.check('PADS', true); + if (this.#pads) + this.#pads_columns = d.partAsInt(); ['PFC', 'PLC', 'PMC'].forEach(f => { if (d.check(f)) this.#auto += ' ' + f; @@ -174365,7 +176756,12 @@ let TMultiGraphPainter$2 = class TMultiGraphPainter extends ObjectPainter { if (this.#pads) { promise = ensureTCanvas(this, false).then(() => { pad_painter = this.getPadPainter(); - return pad_painter.divide(mgraph.fGraphs.arr.length, 0, true); + let nx = mgraph.fGraphs.arr.length, ny = 0; + if (this.#pads_columns) { + ny = Math.ceil(nx / this.#pads_columns); + nx = this.#pads_columns; + } + return pad_painter.divide(nx, ny, true); }); } else if (d.check('A') || !this.getMainPainter()) { const histo = this.scanGraphsRange(mgraph.fGraphs, mgraph.fHistogram, this.getPadPainter()?.getRootPad(true)); @@ -176511,7 +178907,7 @@ class TGaxisPainter extends TAxisPainter { const res = function(v) { return res.toGraph(v); }; res._func = func; res._domain = [smin, smax]; - res._scale = logbase ? log().base(logbase) : linear(); + res._scale = logbase ? log$1().base(logbase) : linear(); res._scale.domain(res._domain).range([0, 100]); res.eval = function(v) { try { @@ -176827,7 +179223,7 @@ class TASImagePainter extends ObjectPainter { const z = this.getImageZoomRange(fp, obj.fConstRatio, obj.fWidth, obj.fHeight), pr = isNodeJs() - ? Promise.resolve().then(function () { return _rollup_plugin_ignore_empty_module_placeholder$1; }).then(h => h.default.createCanvas(z.xmax - z.xmin, z.ymax - z.ymin)) + ? import('canvas').then(h => h.default.createCanvas(z.xmax - z.xmin, z.ymax - z.ymin)) : new Promise(resolveFunc => { const c = document.createElement('canvas'); c.width = z.xmax - z.xmin; @@ -177184,7 +179580,40 @@ __proto__: null, TASImagePainter: TASImagePainter }); -const LITTLE_ENDIAN = true; +// ENTupleColumnType - supported column types + +const kBit = 0x00, + kByte = 0x01, + kChar = 0x02, + kInt8 = 0x03, + kUInt8 = 0x04, + kInt16 = 0x05, + kUInt16 = 0x06, + kInt32 = 0x07, + kUInt32 = 0x08, + kInt64 = 0x09, + kUInt64 = 0x0A, + kReal16 = 0x0B, + kReal32 = 0x0C, + kReal64 = 0x0D, + kIndex32 = 0x0E, + kIndex64 = 0x0F, + kSwitch = 0x10, + kSplitInt16 = 0x11, + kSplitUInt16 = 0x12, + kSplitInt32 = 0x13, + kSplitUInt32 = 0x14, + kSplitInt64 = 0x15, + kSplitUInt64 = 0x16, + kSplitReal16 = 0x17, + kSplitReal32 = 0x18, + kSplitReal64 = 0x19, + kSplitIndex32 = 0x1A, + kSplitIndex64 = 0x1B, + kReal32Trunc = 0x1C, + kReal32Quant = 0x1D, + LITTLE_ENDIAN = true; + class RBufferReader { constructor(buffer) { @@ -177296,192 +179725,99 @@ class RBufferReader { } -const ENTupleColumnType = { - kBit: 0x00, - kByte: 0x01, - kChar: 0x02, - kInt8: 0x03, - kUInt8: 0x04, - kInt16: 0x05, - kUInt16: 0x06, - kInt32: 0x07, - kUInt32: 0x08, - kInt64: 0x09, - kUInt64: 0x0A, - kReal16: 0x0B, - kReal32: 0x0C, - kReal64: 0x0D, - kIndex32: 0x0E, - kIndex64: 0x0F, - kSplitInt16: 0x11, - kSplitUInt16: 0x12, - kSplitInt32: 0x13, - kSplitUInt32: 0x14, - kSplitInt64: 0x15, - kSplitUInt64: 0x16, - kSplitReal16: 0x17, - kSplitReal32: 0x18, - kSplitReal64: 0x19, - kSplitIndex32: 0x1A, - kSplitIndex64: 0x1B}; - - -/** - * @summary Rearrange bytes from split format to normal format (row-wise) for decoding - */ -function recontructUnsplitBuffer(blob, columnDescriptor) { - const { coltype } = columnDescriptor; - - if ( - coltype === ENTupleColumnType.kSplitUInt16 || - coltype === ENTupleColumnType.kSplitUInt32 || - coltype === ENTupleColumnType.kSplitUInt64 || - coltype === ENTupleColumnType.kSplitReal16 || - coltype === ENTupleColumnType.kSplitReal32 || - coltype === ENTupleColumnType.kSplitReal64 || - coltype === ENTupleColumnType.kSplitIndex32 || - coltype === ENTupleColumnType.kSplitIndex64 || - coltype === ENTupleColumnType.kSplitInt16 || - coltype === ENTupleColumnType.kSplitInt32 || - coltype === ENTupleColumnType.kSplitInt64 - ) { - // Determine byte size based on column type - let byteSize; - switch (coltype) { - case ENTupleColumnType.kSplitReal64: - case ENTupleColumnType.kSplitInt64: - case ENTupleColumnType.kSplitUInt64: - case ENTupleColumnType.kSplitIndex64: - byteSize = 8; - break; - case ENTupleColumnType.kSplitReal32: - case ENTupleColumnType.kSplitInt32: - case ENTupleColumnType.kSplitIndex32: - case ENTupleColumnType.kSplitUInt32: - byteSize = 4; - break; - case ENTupleColumnType.kSplitInt16: - case ENTupleColumnType.kSplitUInt16: - case ENTupleColumnType.kSplitReal16: - byteSize = 2; - break; - default: - throw new Error(`Unsupported split coltype: ${coltype} (0x${coltype.toString(16).padStart(2, '0')})`); - } - const splitView = new DataView(blob.buffer, blob.byteOffset, blob.byteLength), - count = blob.byteLength / byteSize, - outBuffer = new ArrayBuffer(blob.byteLength), - outBytes = new Uint8Array(outBuffer); +/** @summary Rearrange bytes from split format to normal format (row-wise) for decoding + * @private */ +function recontructUnsplitBuffer(view, coltype) { + // Determine byte size based on column type + let byteSize; + switch (coltype) { + case kSplitReal64: + case kSplitInt64: + case kSplitUInt64: + case kSplitIndex64: + byteSize = 8; + break; + case kSplitReal32: + case kSplitInt32: + case kSplitIndex32: + case kSplitUInt32: + byteSize = 4; + break; + case kSplitInt16: + case kSplitUInt16: + case kSplitReal16: + byteSize = 2; + break; + default: + return view; + } - for (let i = 0; i < count; ++i) { - for (let b = 0; b < byteSize; ++b) { - const splitIndex = b * count + i, - byte = splitView.getUint8(splitIndex), - writeIndex = i * byteSize + b; - outBytes[writeIndex] = byte; - } - } + const count = view.byteLength / byteSize, + outBuffer = new ArrayBuffer(view.byteLength), + outView = new DataView(outBuffer); - // Return updated blob and remapped coltype - const newBlob = outBuffer; - let newColtype; - switch (coltype) { - case ENTupleColumnType.kSplitUInt16: - newColtype = ENTupleColumnType.kUInt16; - break; - case ENTupleColumnType.kSplitUInt32: - newColtype = ENTupleColumnType.kUInt32; - break; - case ENTupleColumnType.kSplitUInt64: - newColtype = ENTupleColumnType.kUInt64; - break; - case ENTupleColumnType.kSplitIndex32: - newColtype = ENTupleColumnType.kIndex32; - break; - case ENTupleColumnType.kSplitIndex64: - newColtype = ENTupleColumnType.kIndex64; - break; - case ENTupleColumnType.kSplitReal16: - newColtype = ENTupleColumnType.kReal16; - break; - case ENTupleColumnType.kSplitReal32: - newColtype = ENTupleColumnType.kReal32; - break; - case ENTupleColumnType.kSplitReal64: - newColtype = ENTupleColumnType.kReal64; - break; - case ENTupleColumnType.kSplitInt16: - newColtype = ENTupleColumnType.kInt16; - break; - case ENTupleColumnType.kSplitInt32: - newColtype = ENTupleColumnType.kInt32; - break; - case ENTupleColumnType.kSplitInt64: - newColtype = ENTupleColumnType.kInt64; - break; - default: - throw new Error(`Unsupported split coltype for reassembly: ${coltype}`); + for (let i = 0; i < count; ++i) { + for (let b = 0; b < byteSize; ++b) { + const splitIndex = b * count + i, + byte = view.getUint8(splitIndex), + writeIndex = i * byteSize + b; + outView.setUint8(writeIndex, byte); } - - return { blob: newBlob, coltype: newColtype }; } - // If no split type, return original blob and coltype - return { blob, coltype }; + return outView; } +/** @summary Decode a 32 bit intex buffer + * @private */ +function decodeIndex32(view) { + for (let o = 0, prev = 0; o < view.byteLength; o += 4) { + const v = prev + view.getInt32(o, LITTLE_ENDIAN); + view.setInt32(o, v, LITTLE_ENDIAN); + prev = v; + } +} -/** - * @summary Decode a reconstructed index buffer (32- or 64-bit deltas to absolute indices) - */ -function DecodeDeltaIndex(blob, coltype) { - let deltas, result; - - if (coltype === ENTupleColumnType.kIndex32) { - deltas = new Int32Array(blob.buffer || blob, blob.byteOffset || 0, blob.byteLength / 4); - result = new Int32Array(deltas.length); - } else if (coltype === ENTupleColumnType.kIndex64) { - deltas = new BigInt64Array(blob.buffer || blob, blob.byteOffset || 0, blob.byteLength / 8); - result = new BigInt64Array(deltas.length); - } else - throw new Error(`DecodeDeltaIndex: unsupported column type ${coltype}`); +/** @summary Decode a 64 bit intex buffer + * @private */ +function decodeIndex64(view, shift) { + for (let o = 0, prev = 0n; o < view.byteLength; o += (8 + shift)) { + const v = prev + view.getBigInt64(o, LITTLE_ENDIAN); + view.setBigInt64(o, v, LITTLE_ENDIAN); + prev = v; + } +} - if (deltas.length > 0) - result[0] = deltas[0]; - for (let i = 1; i < deltas.length; ++i) - result[i] = result[i - 1] + deltas[i]; - return { blob: result, coltype }; +/** @summary Decode a reconstructed 16bit signed integer buffer using ZigZag encoding + * @private */ +function decodeZigzag16(view) { + for (let o = 0; o < view.byteLength; o += 2) { + const x = view.getUint16(o, LITTLE_ENDIAN); + view.setInt16(o, (x >>> 1) ^ (-(x & 1)), LITTLE_ENDIAN); + } } -/** - * @summary Decode a reconstructed signed integer buffer using ZigZag encoding - */ -function decodeZigzag(blob, coltype) { - let zigzag, result; - - if (coltype === ENTupleColumnType.kInt16) { - zigzag = new Uint16Array(blob.buffer || blob, blob.byteOffset || 0, blob.byteLength / 2); - result = new Int16Array(zigzag.length); - } else if (coltype === ENTupleColumnType.kInt32) { - zigzag = new Uint32Array(blob.buffer || blob, blob.byteOffset || 0, blob.byteLength / 4); - result = new Int32Array(zigzag.length); - } else if (coltype === ENTupleColumnType.kInt64) { - zigzag = new BigUint64Array(blob.buffer || blob, blob.byteOffset || 0, blob.byteLength / 8); - result = new BigInt64Array(zigzag.length); - } else - throw new Error(`decodeZigzag: unsupported column type ${coltype}`); - - for (let i = 0; i < zigzag.length; ++i) { - // ZigZag decode: (x >>> 1) ^ (-(x & 1)) - const x = zigzag[i]; - result[i] = (x >>> 1) ^ (-(x & 1)); +/** @summary Decode a reconstructed 32bit signed integer buffer using ZigZag encoding + * @private */ +function decodeZigzag32(view) { + for (let o = 0; o < view.byteLength; o += 4) { + const x = view.getUint32(o, LITTLE_ENDIAN); + view.setInt32(o, (x >>> 1) ^ (-(x & 1)), LITTLE_ENDIAN); } +} - return { blob: result, coltype }; +/** @summary Decode a reconstructed 64bit signed integer buffer using ZigZag encoding + * @private */ +function decodeZigzag64(view) { + for (let o = 0; o < view.byteLength; o += 8) { + const x = view.getUint64(o, LITTLE_ENDIAN); + view.setInt64(o, (x >>> 1) ^ (-(x & 1)), LITTLE_ENDIAN); + } } + // Envelope Types // TODO: Define usage logic for envelope types in future // const kEnvelopeTypeHeader = 0x01, @@ -177504,13 +179840,9 @@ class RNTupleDescriptorBuilder { return; const reader = new RBufferReader(header_blob), - payloadStart = reader.offset, // Read the envelope metadata - { - envelopeLength - } = this._readEnvelopeMetadata(reader), - + { envelopeLength } = this._readEnvelopeMetadata(reader), // Seek to end of envelope to get checksum checksumPos = payloadStart + envelopeLength - 8, currentPos = reader.offset; @@ -177541,7 +179873,6 @@ class RNTupleDescriptorBuilder { // Read the envelope metadata this._readEnvelopeMetadata(reader); - // Feature flag(32 bits) this._readFeatureFlags(reader); // Header checksum (64-bit xxhash3) @@ -177550,7 +179881,6 @@ class RNTupleDescriptorBuilder { throw new Error('RNTuple corrupted: header checksum does not match footer checksum.'); const schemaExtensionSize = reader.readS64(); - if (schemaExtensionSize < 0) throw new Error('Schema extension frame is not a record frame, which is unexpected.'); @@ -177561,10 +179891,8 @@ class RNTupleDescriptorBuilder { this._readClusterGroups(reader); } - _readEnvelopeMetadata(reader) { const typeAndLength = reader.readU64(), - // Envelope metadata // The 16 bits are the envelope type ID, and the 48 bits are the envelope length envelopeType = Number(typeAndLength & 0xFFFFn), @@ -177590,7 +179918,6 @@ class RNTupleDescriptorBuilder { this.extraTypeInfo = (this.extraTypeInfo || []).concat(newExtra); } - _readFeatureFlags(reader) { this.featureFlags = []; while (true) { @@ -177610,14 +179937,12 @@ class RNTupleDescriptorBuilder { fieldListSize = reader.readS64(), // signed 64-bit fieldListIsList = fieldListSize < 0; - if (!fieldListIsList) throw new Error('Field list frame is not a list frame, which is required.'); const fieldListCount = reader.readU32(), // number of field entries - // List frame: list of field record frames + fieldDescriptors = []; // List frame: list of field record frames - fieldDescriptors = []; for (let i = 0; i < fieldListCount; ++i) { const recordStart = BigInt(reader.offset), fieldRecordSize = reader.readS64(), @@ -177626,7 +179951,6 @@ class RNTupleDescriptorBuilder { parentFieldId = reader.readU32(), structRole = reader.readU16(), flags = reader.readU16(), - fieldName = reader.readString(), typeName = reader.readString(), typeAlias = reader.readString(), @@ -177644,7 +179968,6 @@ class RNTupleDescriptorBuilder { if (flags & kFlagHasTypeChecksum) checksum = reader.readU32(); - fieldDescriptors.push({ fieldVersion, typeVersion, @@ -177693,7 +180016,6 @@ class RNTupleDescriptorBuilder { maxValue = reader.readF64(); } - const column = { coltype, bitsOnStorage, @@ -177718,6 +180040,7 @@ class RNTupleDescriptorBuilder { reader.seek(Number(startOffset - columnListSize)); return columnDescriptors; } + _readAliasColumn(reader) { const startOffset = BigInt(reader.offset), aliasColumnListSize = reader.readS64(), @@ -177740,6 +180063,7 @@ class RNTupleDescriptorBuilder { reader.seek(Number(startOffset - aliasColumnListSize)); return aliasColumns; } + _readExtraTypeInformation(reader) { const startOffset = BigInt(reader.offset), extraTypeInfoListSize = reader.readS64(), @@ -177749,7 +180073,6 @@ class RNTupleDescriptorBuilder { throw new Error('Extra type info frame is not a list frame, which is required.'); const entryCount = reader.readU32(), - extraTypeInfo = []; for (let i = 0; i < entryCount; ++i) { const recordStart = BigInt(reader.offset), @@ -177765,6 +180088,7 @@ class RNTupleDescriptorBuilder { reader.seek(Number(startOffset - extraTypeInfoListSize)); return extraTypeInfo; } + _readClusterGroups(reader) { const startOffset = BigInt(reader.offset), clusterGroupListSize = reader.readS64(), @@ -177772,9 +180096,8 @@ class RNTupleDescriptorBuilder { if (!isList) throw new Error('Cluster group frame is not a list frame'); - const groupCount = reader.readU32(), - - clusterGroups = []; + const groupCount = reader.readU32(); + this.clusterGroups = []; for (let i = 0; i < groupCount; ++i) { const recordStart = BigInt(reader.offset), @@ -177783,24 +180106,12 @@ class RNTupleDescriptorBuilder { entrySpan = reader.readU64(), numClusters = reader.readU32(), pageListLength = reader.readU64(), - - - // Locator method to get the page list locator offset - pageListLocator = this._readLocator(reader), - - - group = { - minEntry, - entrySpan, - numClusters, - pageListLocator, - pageListLength - }; - clusterGroups.push(group); + // Locator method to get the page list locator offset + pageListLocator = this._readLocator(reader); + this.clusterGroups.push({ minEntry, entrySpan, numClusters, pageListLocator, pageListLength }); reader.seek(Number(recordStart + clusterRecordSize)); } reader.seek(Number(startOffset - clusterGroupListSize)); - this.clusterGroups = clusterGroups; } _readLocator(reader) { @@ -177809,11 +180120,9 @@ class RNTupleDescriptorBuilder { throw new Error('Non-standard locators (T=1) not supported yet'); const size = sizeAndType, offset = reader.readU64(); // 8 bytes: offset - return { - size, - offset - }; + return { size, offset }; } + deserializePageList(page_list_blob) { if (!page_list_blob) throw new Error('deserializePageList: received an invalid or empty page list blob'); @@ -177830,39 +180139,34 @@ class RNTupleDescriptorBuilder { clusterSummaryListSize = reader.readS64(); if (clusterSummaryListSize >= 0) throw new Error('Expected a list frame for cluster summaries'); - const clusterSummaryCount = reader.readU32(), - - clusterSummaries = []; + const clusterSummaryCount = reader.readU32(); + this.clusterSummaries = []; for (let i = 0; i < clusterSummaryCount; ++i) { const recordStart = BigInt(reader.offset), clusterSummaryRecordSize = reader.readS64(), firstEntry = reader.readU64(), combined = reader.readU64(), - flags = combined >> 56n; + flags = combined >> 56n, + numEntries = Number(combined & 0x00FFFFFFFFFFFFFFn); if (flags & 0x01n) throw new Error('Cluster summary uses unsupported sharded flag (0x01)'); - const numEntries = Number(combined & 0x00FFFFFFFFFFFFFFn); - clusterSummaries.push({ - firstEntry, - numEntries, - flags - }); + this.clusterSummaries.push({ firstEntry, numEntries, flags }); reader.seek(Number(recordStart + clusterSummaryRecordSize)); } reader.seek(Number(listStartOffset - clusterSummaryListSize)); - this.clusterSummaries = clusterSummaries; this._readNestedFrames(reader); - /* const checksumPagelist = */ reader.readU64(); + reader.readU64(); // checksumPagelist } _readNestedFrames(reader) { - const clusterPageLocations = [], - numListClusters = reader.readS64(); + const numListClusters = reader.readS64(), + numRecordCluster = reader.readU32(); if (numListClusters >= 0) throw new Error('Expected list frame for clusters'); - const numRecordCluster = reader.readU32(); + + this.pageLocations = []; for (let i = 0; i < numRecordCluster; ++i) { const outerListSize = reader.readS64(); @@ -177884,7 +180188,6 @@ class RNTupleDescriptorBuilder { const numElementsWithBit = reader.readS32(), hasChecksum = numElementsWithBit < 0, numElements = BigInt(Math.abs(Number(numElementsWithBit))), - locator = this._readLocator(reader); pages.push({ numElements, @@ -177894,11 +180197,8 @@ class RNTupleDescriptorBuilder { } const elementOffset = reader.readS64(), - isSuppressed = elementOffset < 0; - - let compression = null; - if (!isSuppressed) - compression = reader.readU32(); + isSuppressed = elementOffset < 0, + compression = isSuppressed ? null : reader.readU32(); columns.push({ pages, @@ -177908,113 +180208,43 @@ class RNTupleDescriptorBuilder { }); } - clusterPageLocations.push(columns); + this.pageLocations.push(columns); } - - this.pageLocations = clusterPageLocations; } - // Example Of Deserializing Page Content - deserializePage(blob, columnDescriptor, pageInfo) { - const originalColtype = columnDescriptor.coltype, - { - coltype - } = recontructUnsplitBuffer(blob, columnDescriptor); - let { - blob: processedBlob - } = recontructUnsplitBuffer(blob, columnDescriptor); - + /** @summary Search field by name + * @private */ + findField(name) { + for (let n = 0; n < this.fieldDescriptors.length; ++n) { + const field = this.fieldDescriptors[n]; + if (field.fieldName === name) + return field; + } + } - // Handle split index types - if (originalColtype === ENTupleColumnType.kSplitIndex32 || originalColtype === ENTupleColumnType.kSplitIndex64) { - const { - blob: decodedArray - } = DecodeDeltaIndex(processedBlob, coltype); - processedBlob = decodedArray; - } - - // Handle Split Signed Int types - if (originalColtype === ENTupleColumnType.kSplitInt16 || originalColtype === ENTupleColumnType.kSplitInt32 || originalColtype === ENTupleColumnType.kSplitInt64) { - const { - blob: decodedArray - } = decodeZigzag(processedBlob, coltype); - processedBlob = decodedArray; - } - - const reader = new RBufferReader(processedBlob), - values = [], - - // Use numElements from pageInfo parameter - numValues = Number(pageInfo.numElements), - // Helper for all simple types - extractValues = (readFunc) => { - for (let i = 0; i < numValues; ++i) - values.push(readFunc()); - }; - switch (coltype) { - case ENTupleColumnType.kBit: { - let bitCount = 0; - const totalBitsInBuffer = processedBlob.byteLength * 8; - if (totalBitsInBuffer < numValues) - throw new Error(`kBit: Not enough bits in buffer (${totalBitsInBuffer}) for numValues (${numValues})`); - - for (let byteIndex = 0; byteIndex < processedBlob.byteLength; ++byteIndex) { - const byte = reader.readU8(); - - // Extract 8 bits from this byte - for (let bitPos = 0; bitPos < 8 && bitCount < numValues; ++bitPos, ++bitCount) { - const bitValue = (byte >>> bitPos) & 1, - boolValue = bitValue === 1; - values.push(boolValue); - } - } - break; - } + /** @summary Return all childs of specified field + * @private */ + findChildFields(field) { + const indx = this.fieldDescriptors.indexOf(field), res = []; + for (let n = 0; n < this.fieldDescriptors.length; ++n) { + const fld = this.fieldDescriptors[n]; + if ((fld !== field) && (fld.parentFieldId === indx)) + res.push(fld); + } + return res; + } - case ENTupleColumnType.kReal64: - extractValues(reader.readF64.bind(reader)); - break; - case ENTupleColumnType.kReal32: - extractValues(reader.readF32.bind(reader)); - break; - case ENTupleColumnType.kInt64: - extractValues(reader.readS64.bind(reader)); - break; - case ENTupleColumnType.kUInt64: - extractValues(reader.readU64.bind(reader)); - break; - case ENTupleColumnType.kInt32: - extractValues(reader.readS32.bind(reader)); - break; - case ENTupleColumnType.kUInt32: - extractValues(reader.readU32.bind(reader)); - break; - case ENTupleColumnType.kInt16: - extractValues(reader.readS16.bind(reader)); - break; - case ENTupleColumnType.kUInt16: - extractValues(reader.readU16.bind(reader)); - break; - case ENTupleColumnType.kInt8: - extractValues(reader.readS8.bind(reader)); - break; - case ENTupleColumnType.kUInt8: - case ENTupleColumnType.kByte: - extractValues(reader.readU8.bind(reader)); - break; - case ENTupleColumnType.kChar: - extractValues(() => String.fromCharCode(reader.readS8())); - break; - case ENTupleColumnType.kIndex32: - extractValues(reader.readS32.bind(reader)); - break; - case ENTupleColumnType.kIndex64: - extractValues(reader.readS64.bind(reader)); - break; - default: - throw new Error(`Unsupported column type: ${columnDescriptor.coltype}`); + /** @summary Return array of columns for specified field + * @private */ + findColumns(field) { + const res = []; + if (!field) + return res; + for (const colDesc of this.columnDescriptors) { + if (this.fieldDescriptors[colDesc.fieldId] === field) + res.push(colDesc); } - return values; + return res; } } // class RNTupleDescriptorBuilder @@ -178025,15 +180255,15 @@ class RNTupleDescriptorBuilder { async function readHeaderFooter(tuple) { // if already read - return immediately, make possible to call several times if (tuple?.builder) - return true; + return tuple.builder; - if (!tuple.$file) - return false; + if (!tuple?.$file) + return null; // request header and footer buffers from the file return tuple.$file.readBuffer([tuple.fSeekHeader, tuple.fNBytesHeader, tuple.fSeekFooter, tuple.fNBytesFooter]).then(blobs => { if (blobs?.length !== 2) - return false; + throw new Error('Failure reading header or footer blobs'); // Handle both compressed and uncompressed cases const processBlob = (blob, uncompressedSize) => { @@ -178046,254 +180276,895 @@ async function readHeaderFooter(tuple) { return Promise.all([ processBlob(blobs[0], tuple.fLenHeader), processBlob(blobs[1], tuple.fLenFooter) - ]).then(unzip_blobs => { - const [header_blob, footer_blob] = unzip_blobs; - if (!header_blob || !footer_blob) - return false; - - tuple.builder = new RNTupleDescriptorBuilder; - tuple.builder.deserializeHeader(header_blob); - tuple.builder.deserializeFooter(footer_blob); - - // Build fieldToColumns mapping - tuple.fieldToColumns = {}; - for (const colDesc of tuple.builder.columnDescriptors) { - const fieldDesc = tuple.builder.fieldDescriptors[colDesc.fieldId], - fieldName = fieldDesc.fieldName; - if (!tuple.fieldToColumns[fieldName]) - tuple.fieldToColumns[fieldName] = []; - tuple.fieldToColumns[fieldName].push(colDesc); - } - - // Deserialize Page List - const group = tuple.builder.clusterGroups?.[0]; - if (!group || !group.pageListLocator) - throw new Error('No valid cluster group or page list locator found'); - - const offset = Number(group.pageListLocator.offset), - size = Number(group.pageListLocator.size), - uncompressedSize = Number(group.pageListLength); - - return tuple.$file.readBuffer([offset, size]).then(page_list_blob => { - if (!(page_list_blob instanceof DataView)) - throw new Error(`Expected DataView from readBuffer, got ${Object.prototype.toString.call(page_list_blob)}`); - - // Check if page list data is uncompressed - if (page_list_blob.byteLength === uncompressedSize) { - // Data is uncompressed, use directly - tuple.builder.deserializePageList(page_list_blob); - return true; - } - // Attempt to decompress the page list - return R__unzip(page_list_blob, uncompressedSize).then(unzipped_blob => { - if (!(unzipped_blob instanceof DataView)) - throw new Error(`Unzipped page list is not a DataView, got ${Object.prototype.toString.call(unzipped_blob)}`); - - tuple.builder.deserializePageList(unzipped_blob); - return true; - }); - }); - }); + ]); + }).then(unzip_blobs => { + const [header_blob, footer_blob] = unzip_blobs; + if (!header_blob || !footer_blob) + throw new Error('Failure when uncompress header and footer blobs'); + + tuple.builder = new RNTupleDescriptorBuilder; + tuple.builder.deserializeHeader(header_blob); + tuple.builder.deserializeFooter(footer_blob); + + // Deserialize Page List + const group = tuple.builder.clusterGroups?.[0]; + if (!group || !group.pageListLocator) + throw new Error('No valid cluster group or page list locator found'); + + const offset = Number(group.pageListLocator.offset), + size = Number(group.pageListLocator.size); + + return tuple.$file.readBuffer([offset, size]); + }).then(page_list_blob => { + if (!(page_list_blob instanceof DataView)) + throw new Error(`Expected DataView from readBuffer, got ${Object.prototype.toString.call(page_list_blob)}`); + + const group = tuple.builder.clusterGroups?.[0], + uncompressedSize = Number(group.pageListLength); + + // Check if page list data is uncompressed + if (page_list_blob.byteLength === uncompressedSize) + return page_list_blob; + + // Attempt to decompress the page list + return R__unzip(page_list_blob, uncompressedSize); + }).then(unzipped_blob => { + if (!(unzipped_blob instanceof DataView)) + throw new Error(`Unzipped page list is not a DataView, got ${Object.prototype.toString.call(unzipped_blob)}`); + + tuple.builder.deserializePageList(unzipped_blob); + return tuple.builder; }).catch(err => { console.error('Error during readHeaderFooter execution:', err); - throw err; + return null; }); } -function readEntry(rntuple, fieldName, entryIndex) { - const builder = rntuple.builder, - field = builder.fieldDescriptors.find(f => f.fieldName === fieldName), - fieldData = rntuple._clusterData[fieldName]; - if (!field) - throw new Error(`No descriptor for field ${fieldName}`); - if (!fieldData) - throw new Error(`No data for field ${fieldName}`); +/** @class Base class to read columns/fields from RNtuple + * @private */ + +class ReaderItem { + + constructor(column, name) { + this.column = null; + this.name = name; + this.id = -1; + this.coltype = 0; + this.sz = 0; + this.simple = true; + this.page = -1; // current page for the reading + + if (column?.coltype !== undefined) { + this.column = column; + this.id = column.index; + this.coltype = column.coltype; + + // special handling of split types + if ((this.coltype >= kSplitInt16) && (this.coltype <= kSplitIndex64)) { + this.coltype -= (kSplitInt16 - kInt16); + this.simple = false; + } + } else if (column?.length) + this.items = column; + } + + cleanup() { + this.views = null; + this.view = null; + this.view_len = 0; + } + + init_o() { + this.o = 0; + this.o2 = 0; // for bit count + if (this.column && this.views?.length) { + this.view = this.views.shift(); + this.view_len = this.view.byteLength; + } + } + + reset_extras() {} + + shift_o(sz) { + this.o += sz; + while ((this.o >= this.view_len) && this.view_len) { + this.o -= this.view_len; + if (this.views.length) { + this.view = this.views.shift(); + this.view_len = this.view.byteLength; + } else { + this.view = null; + this.view_len = 0; + } + } + } + + shift(entries) { + if (this.sz && this.simple) + this.shift_o(this.sz * entries); + else { + while (entries-- > 0) + this.func({}); + } + } + + /** @summary Simple column with fixed element size - no vectors, no strings */ + is_simple() { return this.sz && this.simple; } + + set_not_simple() { + this.simple = false; + this.items?.forEach(item => item.set_not_simple()); + } + + assignReadFunc() { + switch (this.coltype) { + case kBit: { + this.func = function(obj) { + if (this.o2 === 0) + this.byte = this.view.getUint8(this.o); + obj[this.name] = ((this.byte >>> this.o2++) & 1) === 1; + if (this.o2 === 8) { + this.o2 = 0; + this.shift_o(1); + } + }; + break; + } + case kReal64: + this.func = function(obj) { + obj[this.name] = this.view.getFloat64(this.o, LITTLE_ENDIAN); + this.shift_o(8); + }; + this.sz = 8; + break; + case kReal32: + this.func = function(obj) { + obj[this.name] = this.view.getFloat32(this.o, LITTLE_ENDIAN); + this.shift_o(4); + }; + this.sz = 4; + break; + case kReal16: + this.func = function(obj) { + const value = this.view.getUint16(this.o, LITTLE_ENDIAN); + this.shift_o(2); + // reimplementing of HalfToFloat + let fbits = (value & 0x8000) << 16, + abs = value & 0x7FFF; + if (abs) { + fbits |= 0x38000000 << (abs >= 0x7C00 ? 1 : 0); + for (; abs < 0x400; abs <<= 1, fbits -= 0x800000); + fbits += abs << 13; + } + this.buf.setUint32(0, fbits, true); + obj[this.name] = this.buf.getFloat32(0, true); + }; + this.sz = 2; + this.buf = new DataView(new ArrayBuffer(4), 0); + break; + case kReal32Trunc: + this.buf = new DataView(new ArrayBuffer(4), 0); + case kReal32Quant: + this.nbits = this.column.bitsOnStorage; + if (!this.buf) { + this.factor = (this.column.maxValue - this.column.minValue) / ((1 << this.nbits) - 1); + this.min = this.column.minValue; + } + + this.func = function(obj) { + let res = 0, len = this.nbits; + // extract nbits from the stream + while (len > 0) { + if (this.o2 === 0) { + this.byte = this.view.getUint8(this.o); + this.o2 = 8; // number of bits in the value + } + const pos = this.nbits - len; // extracted bits + if (len >= this.o2) { + res |= (this.byte & ((1 << this.o2) - 1)) << pos; // get all remaining bits + len -= this.o2; + this.o2 = 0; + this.shift_o(1); + } else { + res |= (this.byte & ((1 << len) - 1)) << pos; // get only len bits from the value + this.o2 -= len; + this.byte >>= len; + len = 0; + } + } + if (this.buf) { + this.buf.setUint32(0, res << (32 - this.nbits), true); + obj[this.name] = this.buf.getFloat32(0, true); + } else + obj[this.name] = res * this.factor + this.min; + }; + break; + case kInt64: + case kIndex64: + this.func = function(obj) { + // FIXME: let process BigInt in the TTree::Draw + obj[this.name] = Number(this.view.getBigInt64(this.o, LITTLE_ENDIAN)); + this.shift_o(8); + }; + this.sz = 8; + break; + case kUInt64: + this.func = function(obj) { + // FIXME: let process BigInt in the TTree::Draw + obj[this.name] = Number(this.view.getBigUint64(this.o, LITTLE_ENDIAN)); + this.shift_o(8); + }; + this.sz = 8; + break; + case kSwitch: + this.func = function(obj) { + // index not used in std::variant, may be in some other usecases + // obj[this.name] = Number(this.view.getBigInt64(this.o, LITTLE_ENDIAN)); + this.shift_o(8); // skip value, not used yet + obj[this.name] = this.view.getInt32(this.o, LITTLE_ENDIAN); + this.shift_o(4); + }; + this.sz = 12; + break; + case kInt32: + case kIndex32: + this.func = function(obj) { + obj[this.name] = this.view.getInt32(this.o, LITTLE_ENDIAN); + this.shift_o(4); + }; + this.sz = 4; + break; + case kUInt32: + this.func = function(obj) { + obj[this.name] = this.view.getUint32(this.o, LITTLE_ENDIAN); + this.shift_o(4); + }; + this.sz = 4; + break; + case kInt16: + this.func = function(obj) { + obj[this.name] = this.view.getInt16(this.o, LITTLE_ENDIAN); + this.shift_o(2); + }; + this.sz = 2; + break; + case kUInt16: + this.func = function(obj) { + obj[this.name] = this.view.getUint16(this.o, LITTLE_ENDIAN); + this.shift_o(2); + }; + this.sz = 2; + break; + case kInt8: + this.func = function(obj) { + obj[this.name] = this.view.getInt8(this.o); + this.shift_o(1); + }; + this.sz = 1; + break; + case kUInt8: + case kByte: + this.func = function(obj) { + obj[this.name] = this.view.getUint8(this.o); + this.shift_o(1); + }; + this.sz = 1; + break; + case kChar: + this.func = function(obj) { + obj[this.name] = String.fromCharCode(this.view.getInt8(this.o)); + this.shift_o(1); + }; + this.sz = 1; + break; + default: + throw new Error(`Unsupported column type: ${this.coltype}`); + } + } + + readStr(len) { + let s = ''; + while (len-- > 0) { + s += String.fromCharCode(this.view.getInt8(this.o)); + this.shift_o(1); + } + return s; + } + + collectPages(cluster_locations, dataToRead, itemsToRead, pagesToRead, emin, emax, elist) { + // no pages without real column id + if (!this.column || (this.id < 0)) + return; + + const pages = cluster_locations[this.id].pages; + + this.views = new Array(pages.length); + + let e0 = 0; + for (let p = 0; p < pages.length; ++p) { + const page = pages[p], + e1 = e0 + Number(page.numElements), + margin = this._is_offset_item ? 1 : 0, // offset for previous entry has to be read as well + is_inside = (e, beg, end) => (e >= beg) && (e < end + margin); + let is_entries_inside = false; + if (elist?.length) + elist.forEach(e => { is_entries_inside ||= is_inside(e, e0, e1); }); + else + is_entries_inside = is_inside(e0, emin, emax) || is_inside(e1, emin, emax) || is_inside(emin, e0, e1) || is_inside(emax, e0, e1); + + if (!this.is_simple() || is_entries_inside) { + itemsToRead.push(this); + dataToRead.push(Number(page.locator.offset), page.locator.size); + pagesToRead.push(p); + this.views[p] = null; // placeholder, filled after request + } else + this.views[p] = { byteLength: this.sz * Number(page.numElements) }; // dummy entry only to allow proper navigation + + e0 = e1; + } + } + + async unzipBlob(blob, cluster_locations, page_indx) { + const colEntry = cluster_locations[this.id], // Access column entry + numElements = Number(colEntry.pages[page_indx].numElements), + elementSize = this.column.bitsOnStorage / 8, + expectedSize = Math.ceil(numElements * elementSize); + + // Check if data is compressed + if ((colEntry.compression === 0) || (blob.byteLength === expectedSize)) + return blob; // Uncompressed: use blob directly + + // Try decompression + return R__unzip(blob, expectedSize).then(result => { + return result || blob; // Fallback to original blob ?? + }).catch(err => { + throw new Error(`Failed to unzip page ${page_indx} for column ${this.id}: ${err.message}`); + }); + } + + reconstructBlob(rawblob, page_indx) { + if (!(rawblob instanceof DataView)) + throw new Error(`Invalid blob type for column ${this.id}: ${Object.prototype.toString.call(rawblob)}`); + + const originalColtype = this.column.coltype, + view = recontructUnsplitBuffer(rawblob, originalColtype); + + // Handle split index types + switch (originalColtype) { + case kSplitIndex32: decodeIndex32(view); break; + case kSplitIndex64: decodeIndex64(view, 0); break; + case kSwitch: decodeIndex64(view, 4); break; + case kSplitInt16: decodeZigzag16(view); break; + case kSplitInt32: decodeZigzag32(view); break; + case kSplitInt64: decodeZigzag64(view); break; + } + + this.views[page_indx] = view; + } + +} + + +/** @class reading std::string field + * @private */ + +class StringReaderItem extends ReaderItem { + + constructor(items, name) { + super(items, name); + items[0]._is_offset_item = true; + items[1].set_not_simple(); + this.off0 = 0; + } + + reset_extras() { + this.off0 = 0; + } + + func(tgtobj) { + const tmp = {}; + this.items[0].func(tmp); + const off = Number(tmp.len); + tgtobj[this.name] = this.items[1].readStr(off - this.off0); + this.off0 = off; + } - // Detect and decode string fields - if (Array.isArray(fieldData) && fieldData.length === 2) { - const [offsets, payload] = fieldData, - start = entryIndex === 0 ? 0 : Number(offsets[entryIndex - 1]), - end = Number(offsets[entryIndex]), - decoded = payload.slice(start, end).join(''); // Convert to string - return decoded; + shift(entries) { + this.items[0].shift(entries - 1); + const tmp = {}; + this.items[0].func(tmp); + const off = Number(tmp.len); + this.items[1].shift_o(off - this.off0); + this.off0 = off; } - // Fallback: primitive type (e.g. int, float) - return fieldData[0][entryIndex]; } -/** @summary Return field name for specified branch index - * @desc API let use field name in selector or field object itself */ -function getSelectorFieldName(selector, i) { - const br = selector.getBranch(i); - return isStr(br) ? br : br?.fieldName; +/** @class reading Streamed field + * @private */ + +class StreamedReaderItem extends ReaderItem { + + constructor(items, name, file, classname) { + super(items, name); + items[0]._is_offset_item = true; + items[1].set_not_simple(); + this.file = file; + this.classname = classname; + this.off0 = 0; + } + + reset_extras() { + this.off0 = 0; + } + + func(tgtobj) { + const tmp = {}, res = {}; + this.items[0].func(tmp); + const off = Number(tmp.len), + buf = new TBuffer(this.items[1].view, this.items[1].o, this.file, this.items[1].o + off - this.off0); + + // TODO: if by chance object splited between two pages + if (this.items[1].view.byteLength < this.items[1].o + off - this.off0) + console.error('FAILURE - buffer is splitted, need to be read from next page'); + + buf.classStreamer(res, this.classname); + + this.items[1].shift_o(off - this.off0); + this.off0 = off; + tgtobj[this.name] = res; + } + + shift(entries) { + this.items[0].shift(entries - 1); + const tmp = {}; + this.items[0].func(tmp); + const off = Number(tmp.len); + this.items[1].shift_o(off - this.off0); + this.off0 = off; + } + } -// Read and process the next data cluster from the RNTuple -function readNextCluster(rntuple, selector) { - const builder = rntuple.builder; - // Add validation - if (!builder.clusterSummaries || builder.clusterSummaries.length === 0) - throw new Error('No cluster summaries available - possibly incomplete file reading'); +/** @class reading of std::array + * @private */ - const clusterIndex = selector.currentCluster, - clusterSummary = builder.clusterSummaries[clusterIndex], - // Gather all pages for this cluster from selected fields only - pages = [], - // Collect only selected field names from selector - selectedFields = []; +class ArrayReaderItem extends ReaderItem { - for (let i = 0; i < selector.numBranches(); ++i) - selectedFields.push(getSelectorFieldName(selector, i)); + constructor(items, tgtname, arrsize) { + super(items, tgtname); + this.arrsize = arrsize; + items[0].set_not_simple(); + } - // For each selected field, collect its columns' pages - for (const fieldName of selectedFields) { - const columns = rntuple.fieldToColumns[fieldName]; - if (!columns) - throw new Error(`Selected field '${fieldName}' not found in RNTuple`); + func(tgtobj) { + const arr = [], tmp = {}; + let len = this.arrsize; + while (len-- > 0) { + this.items[0].func(tmp); + arr.push(tmp.value); + } + tgtobj[this.name] = arr; + } - for (const colDesc of columns) { - const colEntry = builder.pageLocations[clusterIndex]?.[colDesc.index]; + shift(entries) { + this.items[0].shift(entries * this.arrsize); + } + +} + + +/** @class reading of std::bitset + * @desc large numbers with more than 48 bits converted to BigInt + * @private */ - // When the data is missing or broken - if (!colEntry || !colEntry.pages) - throw new Error(`No pages for column ${colDesc.index} in cluster ${clusterIndex}`); +class BitsetReaderItem extends ReaderItem { - for (const page of colEntry.pages) - pages.push({ page, colDesc, fieldName }); + constructor(items, tgtname, size) { + super(items, tgtname); + this.size = size; + items[0].set_not_simple(); + this.bigint = size > 48; + } + + func(tgtobj) { + const tmp = {}; + let len = 0, res = this.bigint ? 0n : 0; + while (len < this.size) { + this.items[0].func(tmp); + if (tmp.bit) { + if (this.bigint) + res |= (1n << BigInt(len)); + else + res |= 1 << len; + } + len++; } + tgtobj[this.name] = res; } - selector.currentCluster++; + shift(entries) { + this.items[0].shift(entries * this.size); + } - // Early exit if no pages to read (i.e., no selected fields matched) - if (pages.length === 0) { - selector.Terminate(false); - return Promise.resolve(); - } - - // Build flat array of [offset, size, offset, size, ...] to read pages - const dataToRead = pages.flatMap(p => - [Number(p.page.locator.offset), Number(p.page.locator.size)] - ); - - return rntuple.$file.readBuffer(dataToRead).then(blobsRaw => { - const blobs = Array.isArray(blobsRaw) ? blobsRaw : [blobsRaw], - unzipPromises = blobs.map((blob, idx) => { - const { page, colDesc } = pages[idx], - colEntry = builder.pageLocations[clusterIndex][colDesc.index], // Access column entry - numElements = Number(page.numElements), - elementSize = colDesc.bitsOnStorage / 8; - - // Check if data is compressed - if (colEntry.compression === 0) - return Promise.resolve(blob); // Uncompressed: use blob directly - const expectedSize = numElements * elementSize; - - // Special handling for boolean fields - if (colDesc.coltype === ENTupleColumnType.kBit) { - const expectedBoolSize = Math.ceil(numElements / 8); - if (blob.byteLength === expectedBoolSize) - return Promise.resolve(blob); - // Try decompression but catch errors for boolean fields - return R__unzip(blob, expectedBoolSize).catch(err => { - throw new Error(`Failed to unzip boolean page ${idx}: ${err.message}`); - }); - } +} - // If the blob is already the expected size, treat as uncompressed - if (blob.byteLength === expectedSize) - return Promise.resolve(blob); - - // Try decompression - return R__unzip(blob, expectedSize).then(result => { - if (!result) - return blob; // Fallback to original blob - return result; - }).catch(err => { - throw new Error(`Failed to unzip page ${idx}: ${err.message}`); - }); - }); - return Promise.all(unzipPromises).then(unzipBlobs => { - rntuple._clusterData = {}; // store deserialized data per field - - for (let i = 0; i < unzipBlobs.length; ++i) { - const blob = unzipBlobs[i]; - // Ensure blob is a DataView - if (!(blob instanceof DataView)) - throw new Error(`Invalid blob type for page ${i}: ${Object.prototype.toString.call(blob)}`); - const { - page, - colDesc - } = pages[i], - field = builder.fieldDescriptors[colDesc.fieldId], - values = builder.deserializePage(blob, colDesc, page); - - // Support multiple representations (e.g., string fields with offsets + payload) - if (!rntuple._clusterData[field.fieldName]) - rntuple._clusterData[field.fieldName] = []; - - // splitting string fields into offset and payload components - if (field.typeName === 'std::string') { - if ( - colDesc.coltype === ENTupleColumnType.kIndex64 || - colDesc.coltype === ENTupleColumnType.kIndex32 || - colDesc.coltype === ENTupleColumnType.kSplitIndex64 || - colDesc.coltype === ENTupleColumnType.kSplitIndex32 - ) // Index64/Index32 - rntuple._clusterData[field.fieldName][0] = values; // Offsets - else if (colDesc.coltype === ENTupleColumnType.kChar) - rntuple._clusterData[field.fieldName][1] = values; // Payload - else - throw new Error(`Unsupported column type for string field: ${colDesc.coltype}`); - } else - rntuple._clusterData[field.fieldName][0] = values; +/** @class reading std::vector and other kinds of collections + * @private */ + +class CollectionReaderItem extends ReaderItem { + + constructor(items, tgtname) { + super(items, tgtname); + this.off0 = 0; + items[0]._is_offset_item = true; + items[1].set_not_simple(); + } + + reset_extras() { + this.off0 = 0; + } + + func(tgtobj) { + const arr = [], tmp = {}; + this.items[0].func(tmp); + const off = Number(tmp.len); + let len = off - this.off0; + while (len-- > 0) { + this.items[1].func(tmp); + arr.push(tmp.val); + } + tgtobj[this.name] = arr; + this.off0 = off; + } + + shift(entries) { + const tmp = {}; + this.items[0].shift(entries - 1); + this.items[0].func(tmp); + const off = Number(tmp.len); + this.items[1].shift(off - this.off0); + this.off0 = off; + } + +} + +/** @class reading std::variant field + * @private */ + +class VariantReaderItem extends ReaderItem { + + constructor(items, tgtname) { + super(items, tgtname); + this.set_not_simple(); + } + + func(tgtobj) { + const tmp = {}; + this.items[0].func(tmp); + const id = tmp.switch; + if (id === 0) + tgtobj[this.name] = null; // set null + else if (Number.isInteger(id) && (id > 0) && (id < this.items.length)) + this.items[id].func(tgtobj); + } + +} + + +/** @class reading std::tuple<> field + * @private */ + +class TupleReaderItem extends ReaderItem { + + func(tgtobj) { + const tuple = {}; + this.items.forEach(item => item.func(tuple)); + tgtobj[this.name] = tuple; + } + + shift(entries) { + this.items.forEach(item => item.shift(entries)); + } + +} + +/** @class reading custom class field + * @private */ + +class CustomClassReaderItem extends ReaderItem { + + constructor(items, tgtname, classname) { + super(items, tgtname); + this.classname = classname; + this.set_not_simple(); + } + + func(tgtobj) { + const obj = { _typename: this.classname }; + this.items.forEach(item => item.func(obj)); + tgtobj[this.name] = obj; + } + + shift(entries) { + this.items.forEach(item => item.shift(entries)); + } + +} + + +/** @class reading std::pair field + * @private */ + +class PairReaderItem extends ReaderItem { + + func(tgtobj) { + const res = {}; + this.items[0].func(res); + this.items[1].func(res); + tgtobj[this.name] = res; + } + + shift(entries) { + this.items[0].shift(entries); + this.items[1].shift(entries); + } + +} + + +async function rntupleProcess(rntuple, selector, args = {}) { + const handle = { + file: rntuple.$file, // keep file reference + columns: [], // list of ReaderItem with real columns for reading + items: [], // list of ReaderItem producing output fields + current_cluster: 0, // current cluster to process + current_cluster_first_entry: 0, // first entry in current cluster + current_cluster_last_entry: 0, // last entry in current cluster + current_entry: 0, // current processed entry + process_arrays: false, // one can process all branches as arrays + firstentry: 0, // first entry in the rntuple + lastentry: 0 // last entry in the rntuple + }; + + function readNextPortion(builder, inc_cluster) { + let do_again = true, numClusterEntries, locations; + + while (do_again) { + if (inc_cluster) { + handle.current_cluster++; + handle.current_cluster_first_entry = handle.current_cluster_last_entry; } - // Ensure string fields have ending offset for proper reconstruction of the last entry - for (const fieldName of selectedFields) { - const field = builder.fieldDescriptors.find(f => f.fieldName === fieldName), - colData = rntuple._clusterData[fieldName]; - if (field.typeName === 'std::string') { - if (!Array.isArray(colData) || colData.length !== 2) - throw new Error(`String field '${fieldName}' must have 2 columns`); - if (colData[0].length !== builder.clusterSummaries[clusterIndex].numEntries) - throw new Error(`Malformed string field '${fieldName}': missing final offset`); - } + locations = builder.pageLocations[handle.current_cluster]; + if (!locations) { + selector.Terminate(true); + return selector; } - const numEntries = clusterSummary.numEntries; - for (let i = 0; i < numEntries; ++i) { - for (let b = 0; b < selector.numBranches(); ++b) { - const fieldName = getSelectorFieldName(selector, b), - tgtName = selector.nameOfBranch(b), - values = rntuple._clusterData[fieldName]; + numClusterEntries = builder.clusterSummaries[handle.current_cluster].numEntries; + + handle.current_cluster_last_entry = handle.current_cluster_first_entry + numClusterEntries; + + do_again = inc_cluster && handle.process_entries && + (handle.process_entries[handle.process_entries_indx] >= handle.current_cluster_last_entry); + } + + // calculate entries which can be extracted from the cluster + let emin, emax; + const dataToRead = [], itemsToRead = [], pagesToRead = [], elist = []; - if (!values) - throw new Error(`Missing values for selected field: ${fieldName}`); - selector.tgtobj[tgtName] = readEntry(rntuple, fieldName, i); + if (handle.process_entries) { + let i = handle.process_entries_indx; + while ((i < handle.process_entries.length) && (handle.process_entries[i] < handle.current_cluster_last_entry)) + elist.push(handle.process_entries[i++] - handle.current_cluster_first_entry); + emin = elist[0]; + emax = elist[elist.length - 1]; + } else { + emin = handle.current_entry - handle.current_cluster_first_entry; + emax = Math.min(numClusterEntries, handle.process_max - handle.current_cluster_first_entry); + } + + // loop over all columns and request required pages + handle.columns.forEach(item => item.collectPages(locations, dataToRead, itemsToRead, pagesToRead, emin, emax, elist)); + + return rntuple.$file.readBuffer(dataToRead).then(blobsRaw => { + const blobs = Array.isArray(blobsRaw) ? blobsRaw : [blobsRaw], + unzipPromises = blobs.map((blob, idx) => itemsToRead[idx].unzipBlob(blob, locations, pagesToRead[idx])); + return Promise.all(unzipPromises); + }).then(unzipBlobs => { + unzipBlobs.map((rawblob, idx) => itemsToRead[idx].reconstructBlob(rawblob, pagesToRead[idx])); + + // reset reading pointer after all buffers are there + handle.columns.forEach(item => item.init_o()); + handle.items.forEach(item => item.reset_extras()); + + let skip_entries = handle.current_entry - handle.current_cluster_first_entry; + + while (handle.current_entry < handle.current_cluster_last_entry) { + for (let i = 0; i < handle.items.length; ++i) { + if (skip_entries > 0) + handle.items[i].shift(skip_entries); + handle.items[i].func(selector.tgtobj); + } + skip_entries = 0; + + selector.Process(handle.current_entry); + + if (handle.process_entries) { + if (++handle.process_entries_indx >= handle.process_entries.length) { + selector.Terminate(true); + return selector; + } + const prev_entry = handle.current_entry; + handle.current_entry = handle.process_entries[handle.process_entries_indx]; + skip_entries = handle.current_entry - prev_entry - 1; + } else if (++handle.current_entry >= handle.process_max) { + selector.Terminate(true); + return selector; } - selector.Process(); } - selector.Terminate(true); + return readNextPortion(builder, true); }); - }); -} + } + + function addColumnReadout(column, tgtname) { + const item = new ReaderItem(column, tgtname); + item.assignReadFunc(); + handle.columns.push(item); + return item; + } + + function addFieldReading(builder, field, tgtname) { + const columns = builder.findColumns(field), + childs = builder.findChildFields(field); + if (!columns?.length) { + if ((childs.length === 2) && (field.typeName.indexOf('std::pair') === 0)) { + const item1 = addFieldReading(builder, childs[0], 'first'), + item2 = addFieldReading(builder, childs[1], 'second'); + return new PairReaderItem([item1, item2], tgtname); + } + + if ((childs.length === 1) && (field.typeName.indexOf('std::array') === 0)) { + const item1 = addFieldReading(builder, childs[0], 'value'); + return new ArrayReaderItem([item1], tgtname, Number(field.arraySize)); + } + + if ((childs.length === 1) && (field.typeName.indexOf('std::atomic') === 0)) + return addFieldReading(builder, childs[0], tgtname); + + + if ((childs.length > 0) && (field.typeName.indexOf('std::tuple') === 0)) { + const items = []; + for (let i = 0; i < childs.length; ++i) + items.push(addFieldReading(builder, childs[i], `_${i}`)); + return new TupleReaderItem(items, tgtname); + } -// TODO args can later be used to filter fields, limit entries, etc. -// Create reader and deserialize doubles from the buffer -function rntupleProcess(rntuple, selector, args) { - return readHeaderFooter(rntuple).then(() => { - selector.Begin(); - selector.currentCluster = 0; - return readNextCluster(rntuple, selector); + // this is custom class which is decomposed on several fields + if ((childs.length > 0) && field.checksum && field.typeName) { + const items = []; + for (let i = 0; i < childs.length; ++i) + items.push(addFieldReading(builder, childs[i], childs[i].fieldName)); + return new CustomClassReaderItem(items, tgtname, field.typeName); + } + + throw new Error(`No columns found for field '${field.fieldName}' in RNTuple`); + } + + if ((columns.length === 2) && (field.typeName === 'std::string')) { + const itemlen = addColumnReadout(columns[0], 'len'), + itemstr = addColumnReadout(columns[1], 'str'); + return new StringReaderItem([itemlen, itemstr], tgtname); + } + + if ((columns.length === 1) && (field.typeName.indexOf('std::bitset') === 0)) { + const itembit = addColumnReadout(columns[0], 'bit'); + return new BitsetReaderItem([itembit], tgtname, Number(field.arraySize)); + } + + if ((columns.length === 2) && field.checksum && field.typeName) { + if (!handle.file.getStreamer(field.typeName, { checksum: field.checksum })) + throw new Error(`No streamer for type '${field.typeName}' checksum ${field.checksum}`); + + const itemlen = addColumnReadout(columns[0], 'len'), + itemb = addColumnReadout(columns[1], 'b'); + return new StreamedReaderItem([itemlen, itemb], tgtname, handle.file, field.typeName); + } + + let is_stl = false; + ['vector', 'map', 'unordered_map', 'multimap', 'unordered_multimap', 'set', 'unordered_set', 'multiset', 'unordered_multiset'].forEach(name => { + if (field.typeName.indexOf('std::' + name) === 0) + is_stl = true; + }); + + if ((childs.length === 1) && is_stl) { + const itemlen = addColumnReadout(columns[0], 'len'), + itemval = addFieldReading(builder, childs[0], 'val'); + return new CollectionReaderItem([itemlen, itemval], tgtname); + } + + if ((childs.length > 0) && (field.typeName.indexOf('std::variant') === 0)) { + const items = [addColumnReadout(columns[0], 'switch')]; + for (let i = 0; i < childs.length; ++i) + items.push(addFieldReading(builder, childs[i], tgtname)); + return new VariantReaderItem(items, tgtname); + } + + return addColumnReadout(columns[0], tgtname); + } + + return readHeaderFooter(rntuple).then(builder => { + if (!builder) + throw new Error('Not able to read header for the RNtuple'); + + for (let i = 0; i < selector.numBranches(); ++i) { + const br = selector.getBranch(i), + name = isStr(br) ? br : br?.fieldName, + tgtname = selector.nameOfBranch(i); + if (!name) + throw new Error(`Not able to extract name for field ${i}`); + + const field = builder.findField(name); + if (!field) + throw new Error(`Field ${name} not found`); + + const item = addFieldReading(builder, field, tgtname); + handle.items.push(item); + } + + // calculate number of entries + builder.clusterSummaries.forEach(summary => { handle.lastentry += summary.numEntries; }); + + if (handle.firstentry >= handle.lastentry) + throw new Error('Not able to find entries in the RNtuple'); + + // select range of entries to process + handle.process_min = handle.firstentry; + handle.process_max = handle.lastentry; + + if (args.elist) { + args.firstentry = args.elist.at(0); + args.numentries = args.elist.at(-1) - args.elist.at(0) + 1; + handle.process_entries = args.elist; + handle.process_entries_indx = 0; + handle.process_arrays = false; // do not use arrays process for selected entries + } + + if (Number.isInteger(args.firstentry) && (args.firstentry > handle.firstentry) && (args.firstentry < handle.lastentry)) + handle.process_min = args.firstentry; + + if (Number.isInteger(args.numentries) && (args.numentries > 0)) + handle.process_max = Math.min(handle.process_max, handle.process_min + args.numentries); + + // first check from which cluster one should start + for (let indx = 0, emin = 0; indx < builder.clusterSummaries.length; ++indx) { + const summary = builder.clusterSummaries[indx], + emax = emin + summary.numEntries; + if ((handle.process_min >= emin) && (handle.process_min < emax)) { + handle.current_cluster = indx; + handle.current_cluster_first_entry = emin; + break; + } + emin = emax; + } + + if (handle.current_cluster < 0) + throw new Error(`Not able to find cluster for entry ${handle.process_min} in the RNtuple`); + + handle.current_entry = handle.process_min; + + selector.Begin(rntuple); + + return readNextPortion(builder); }).then(() => selector); } + class TDrawSelectorTuple extends TDrawSelector { /** @summary Return total number of entries @@ -178306,11 +181177,7 @@ class TDrawSelectorTuple extends TDrawSelector { /** @summary Search for field in tuple * @desc TODO: Can be more complex when name includes extra parts referencing member or collection size or more */ - findBranch(tuple, name) { - return tuple.builder?.fieldDescriptors.find(field => { - return field.fieldName === name; - }); - } + findBranch(tuple, name) { return tuple.builder?.findField(name); } /** @summary Returns true if field can be used as array */ isArrayBranch(/* tuple, br */) { return false; } @@ -178340,8 +181207,8 @@ async function rntupleDraw(rntuple, args) { args.SelectorClass = TDrawSelectorTuple; args.processFunction = rntupleProcess; - return readHeaderFooter(rntuple).then(res_header_footer => { - return res_header_footer ? treeDraw(rntuple, args) : null; + return readHeaderFooter(rntuple).then(builder => { + return builder ? treeDraw(rntuple, args) : null; }); } @@ -178352,12 +181219,10 @@ async function rntupleDraw(rntuple, args) { async function tupleHierarchy(tuple_node, tuple) { tuple_node._childs = []; // tuple_node._tuple = tuple; // set reference, will be used later by RNTuple::Draw - - return readHeaderFooter(tuple).then(res => { - if (!res) - return res; - - tuple.builder?.fieldDescriptors.forEach(field => { + return readHeaderFooter(tuple).then(builder => { + builder?.fieldDescriptors.forEach((field, indx) => { + if (field.parentFieldId !== indx) + return; const item = { _name: field.fieldName, _typename: 'ROOT::RNTupleField', // pseudo class name, used in draw.mjs @@ -178366,20 +181231,16 @@ async function tupleHierarchy(tuple_node, tuple) { $tuple: tuple, // reference on tuple, need for drawing $field: field }; - item._obj = item; - tuple_node._childs.push(item); }); - - return true; + return Boolean(builder); }); } var rntuple = /*#__PURE__*/Object.freeze({ __proto__: null, RBufferReader: RBufferReader, -readEntry: readEntry, readHeaderFooter: readHeaderFooter, rntupleDraw: rntupleDraw, rntupleProcess: rntupleProcess, @@ -178394,13 +181255,15 @@ async function drawRNTuple(dom, obj, opt) { const args = {}; let tuple; - if (obj?.$tuple) { + if (obj?.$tuple && obj.$field) { // case of fictional ROOT::RNTupleField tuple = obj.$tuple; args.expr = obj._name; - if (isStr(opt) && opt.indexOf('dump') === 0) + if (isStr(opt) && opt.indexOf('dump') === 0) { args.expr += '>>' + opt; - else if (opt) + args.branch = obj.$field; + args.copy_fields = false; // no need to copy fields, reading is simple + } else if (opt) args.expr += opt; } else { tuple = obj; @@ -178918,7 +181781,7 @@ class RAxisPainter extends RObjectPainter { this.logbase = Math.exp(1); else if (_log > 1.9) this.logbase = Math.round(_log); - this.func = log().base(this.logbase).domain([smin, smax]); + this.func = log$1().base(this.logbase).domain([smin, smax]); } else this.func = linear().domain([smin, smax]); @@ -184942,6 +187805,7 @@ exports.getAbsPosInCanvas = getAbsPosInCanvas; exports.getActivePad = getActivePad; exports.getBoxDecorations = getBoxDecorations; exports.getColor = getColor; +exports.getColorPalette = getColorPalette; exports.getDocument = getDocument; exports.getDomCanvasPainter = getDomCanvasPainter; exports.getElementCanvPainter = getElementCanvPainter; @@ -185001,6 +187865,7 @@ exports.registerMethods = registerMethods; exports.resize = resize; exports.selectActivePad = selectActivePad; exports.setBatchMode = setBatchMode; +exports.setColorPalette = setColorPalette; exports.setDefaultDrawOpt = setDefaultDrawOpt; exports.setHPainter = setHPainter; exports.setHistogramTitle = setHistogramTitle; diff --git a/js/changes.md b/js/changes.md index 7e7f905a1e9f3..7b761be597070 100644 --- a/js/changes.md +++ b/js/changes.md @@ -2,10 +2,51 @@ ## Changes in dev +1. Implement new data types in `RNtuple` + - reduced float types kFloat16, kReal32Trunc, kReal32Quant + - `std::vector` + - `std::map`, `std::unordered_map`, `std::multimap`, `std::unordered_multimap` with `std::pair` + - `std::set`, `std::unordered_set`, `std::multiset`, `std::unordered_multiset` + - `std::array` + - `std::variant` + - `std::tuple` + - `std::bitset` + - `std::atomic` + - simple custom classes + - streamed types +1. Resort order of ranges in http request, fixing several long-standing problems #374 1. Implement for `TPie` 3d, text, title drawing including interactivity -1. Remove support for deprectaed TH1K class +1. Implement `TCanvas` support in `build3d` function #373 +1. Implements `TTree` branches filtering via context menu #364 +1. Let define alternative draw function #378 +1. Implement padsN draw option for `THStack` and `TMultiGraph` +1. Support custom click handler for `TGraph` https://root-forum.cern.ch/t/64744 +1. Use `resvg-js` backend for PNG support in node.js #391, thanks to https://github.com/OmarMesqq +1. Remove support for deprectaed `TH1K` class +1. Introduce `settings.ServerTimeout` global timeout for THttpServer operations +1. Let set custom color palette with `setColorPalette` function +1. Upgrade three.js r180 -> r183 +1. Fix - paint frame border mode/size from `TCanvas` +1. Fix - interactivity for TH3 palette drawing #398 + + +## Changes in 7.10.3 +1. Fix - add `TLeafG` support in `TTree` #397 +2. Fix - reset contour while drawing `TH3` +3. Fix - fix kFloat16/kDouble32 processing in `TTree` + + +## Changes in 7.10.2 +1. Fix - correctly process `TLeafB` arrays in tree draw #384 +2. Fix - better detect default ranges in `TGraph` histogram +3. Fix - convert BigInt before `RNtuple` drawing +4. Fix - pages and clusters processing in `RNtuple` #390 +5. Fix - extra row for legend header, proper horizontal align https://github.com/root-project/root/issues/21173 + + +## Changes in 7.10.1 1. Fix - proper paint axis labels on both sides when pad.fTickx/y = 2 -1. Fix - paint frame border mode/size from TCanvas +2. Fix - recover io after bad http response ## Changes in 7.10.0 @@ -1469,11 +1510,11 @@ 8. Fix several problems with markers drawing; implement plus, asterisk, mult symbols. 9. Implement custom layout, which allows to configure user-defined layout for displayed objects 10. Fix errors with scaling of axis labels. -11. Support also Y axis with custom labels like: http://jsroot.gsi.de/dev/?nobrowser&file=../files/atlas.root&item=LEDShapeHeightCorr_Gain0;1&opt=col +11. Support also Y axis with custom labels like: https://jsroot.gsi.de/dev/?nobrowser&file=https://jsroot.gsi.de/files/atlas.root&item=LEDShapeHeightCorr_Gain0;1&opt=col ## Changes in 3.7 -1. Support of X axis with custom labels like: http://jsroot.gsi.de/dev/?nobrowser&json=../files/hist_xlabels.json +1. Support of X axis with custom labels like: https://jsroot.gsi.de/dev/?nobrowser&json=https://jsroot.gsi.de/files/hist_xlabels.json 2. Extend functionality of JSROOT.addDrawFunc() function. One could register type-specific `make_request` and `after_request` functions; `icon`, `prereq`, `script`, `monitor` properties. This let add more custom elements to the generic gui, implemented with JSROOT.HierarchyPainter @@ -1660,7 +1701,7 @@ 13. Provide example fileitem.htm how read and display item from ROOT file. 14. In default index.htm page one could specify 'file', 'layout', 'item' and 'items' parameters like: - + 15. Support direct reading of objects from sub-sub-directories. 16. Introduce demo.htm, which demonstrates online usage of JSROOT. 17. One could use demo.htm directly with THttpServer providing address like: diff --git a/js/index.htm b/js/index.htm index cd123332065fa..fb99f5640b8e8 100644 --- a/js/index.htm +++ b/js/index.htm @@ -14,7 +14,7 @@ -
+
loading modules ...