diff options
author | Liang Qi <liang.qi@qt.io> | 2019-08-27 09:45:52 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2019-08-27 09:45:52 +0200 |
commit | 0f1f7fb97fd0dd572ad61a0ebaef68c2bba72e57 (patch) | |
tree | e1f3f1b58aa69904e6bc629e88ed68fca858f83a /src | |
parent | e8c70fb07f01b492b721451c00496f860eb40be0 (diff) | |
parent | 5bb178c479a247720fbc3fbb7f06a32b725193ac (diff) |
Merge remote-tracking branch 'origin/dev' into 5.14
Conflicts:
src/widgets/kernel/qwidget.cpp
src/widgets/kernel/qwidget_p.h
src/widgets/kernel/qwidgetrepaintmanager.cpp
src/widgets/kernel/qwidgetwindow.cpp
tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp
Change-Id: Ifae457d0427be8e2465e474b055722e11b3b1e5c
Diffstat (limited to 'src')
97 files changed, 3057 insertions, 1290 deletions
diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index 12537b6199..e4f3bd85de 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2019 Intel Corporation. ** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com ** ** Permission is hereby granted, free of charge, to any person obtaining a copy @@ -59,9 +59,6 @@ # define HAVE_PIPE2 1 # endif #endif -#if defined(__FreeBSD__) && __FreeBSD__ >= 9 -# include <sys/procdesc.h> -#endif #if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500 # define HAVE_WAITID 1 @@ -93,9 +90,11 @@ # endif #endif -#ifndef FFD_ATOMIC_RELAXED -# include "forkfd_gcc.h" -#endif +#include "forkfd_atomic.h" + +static int system_has_forkfd(void); +static int system_forkfd(int flags, pid_t *ppid, int *system); +static int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage); #define CHILDREN_IN_SMALL_ARRAY 16 #define CHILDREN_IN_BIG_ARRAY 256 @@ -448,6 +447,37 @@ static void ignore_sigpipe() #endif } +#if defined(__GNUC__) && (!defined(__FreeBSD__) || __FreeBSD__ < 10) +__attribute((destructor, unused)) static void cleanup(); +#endif + +static void cleanup() +{ + BigArray *array; + /* This function is not thread-safe! + * It must only be called when the process is shutting down. + * At shutdown, we expect no one to be calling forkfd(), so we don't + * need to be thread-safe with what is done there. + * + * But SIGCHLD might be delivered to any thread, including this one. + * There's no way to prevent that. The correct solution would be to + * cooperatively delete. We don't do that. + */ + if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0) + return; + + /* notify the handler that we're no longer in operation */ + ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED); + + /* free any arrays we might have */ + array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE); + while (array != NULL) { + BigArray *next = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE); + free(array); + array = next; + } +} + static void forkfd_initialize() { #if defined(HAVE_BROKEN_WAITID) @@ -489,44 +519,15 @@ static void forkfd_initialize() ignore_sigpipe(); #endif -#ifndef __GNUC__ +#ifdef __GNUC__ + (void) cleanup; /* suppress unused static function warning */ +#else atexit(cleanup); #endif ffd_atomic_store(&forkfd_status, 1, FFD_ATOMIC_RELAXED); } -#ifdef __GNUC__ -__attribute((destructor, unused)) static void cleanup(); -#endif - -static void cleanup() -{ - BigArray *array; - /* This function is not thread-safe! - * It must only be called when the process is shutting down. - * At shutdown, we expect no one to be calling forkfd(), so we don't - * need to be thread-safe with what is done there. - * - * But SIGCHLD might be delivered to any thread, including this one. - * There's no way to prevent that. The correct solution would be to - * cooperatively delete. We don't do that. - */ - if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0) - return; - - /* notify the handler that we're no longer in operation */ - ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED); - - /* free any arrays we might have */ - array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE); - while (array != NULL) { - BigArray *next = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE); - free(array); - array = next; - } -} - static int create_pipe(int filedes[], int flags) { int ret = -1; @@ -565,55 +566,6 @@ static int create_pipe(int filedes[], int flags) return ret; } -#if defined(FORKFD_NO_SPAWNFD) && defined(__FreeBSD__) && __FreeBSD__ >= 9 -# if __FreeBSD__ == 9 -/* PROCDESC is an optional feature in the kernel and wasn't enabled - * by default on FreeBSD 9. So we need to check for it at runtime. */ -static ffd_atomic_int system_has_forkfd = FFD_ATOMIC_INIT(1); -# else -/* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option - * anymore and can't be disabled. */ -static const int system_has_forkfd = 1; -# endif - -static int system_forkfd(int flags, pid_t *ppid) -{ - int ret; - pid_t pid; - pid = pdfork(&ret, PD_DAEMON); - if (__builtin_expect(pid == -1, 0)) { -# if __FreeBSD__ == 9 - if (errno == ENOSYS) { - /* PROCDESC wasn't compiled into the kernel: don't try it again. */ - ffd_atomic_store(&system_has_forkfd, 0, FFD_ATOMIC_RELAXED); - } -# endif - return -1; - } - if (pid == 0) { - /* child process */ - return FFD_CHILD_PROCESS; - } - - /* parent process */ - if (flags & FFD_CLOEXEC) - fcntl(ret, F_SETFD, FD_CLOEXEC); - if (flags & FFD_NONBLOCK) - fcntl(ret, F_SETFL, fcntl(ret, F_GETFL) | O_NONBLOCK); - if (ppid) - *ppid = pid; - return ret; -} -#else -static const int system_has_forkfd = 0; -static int system_forkfd(int flags, pid_t *ppid) -{ - (void)flags; - (void)ppid; - return -1; -} -#endif - #ifndef FORKFD_NO_FORKFD /** * @brief forkfd returns a file descriptor representing a child process @@ -661,11 +613,9 @@ int forkfd(int flags, pid_t *ppid) int efd; #endif - if (system_has_forkfd) { - ret = system_forkfd(flags, ppid); - if (system_has_forkfd) - return ret; - } + fd = system_forkfd(flags, ppid, &ret); + if (ret) + return fd; (void) pthread_once(&forkfd_initialization, forkfd_initialize); @@ -790,7 +740,7 @@ int spawnfd(int flags, pid_t *ppid, const char *path, const posix_spawn_file_act /* we can only do work if we have a way to start the child in stopped mode; * otherwise, we have a major race condition. */ - assert(!system_has_forkfd); + assert(!system_has_forkfd()); (void) pthread_once(&forkfd_initialization, forkfd_initialize); @@ -843,30 +793,13 @@ out: #endif // _POSIX_SPAWN && !FORKFD_NO_SPAWNFD -int forkfd_wait(int ffd, forkfd_info *info, struct rusage *rusage) +int forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) { struct pipe_payload payload; int ret; - if (system_has_forkfd) { -#if defined(__FreeBSD__) && __FreeBSD__ >= 9 - pid_t pid; - int status; - int options = WEXITED; - - ret = pdgetpid(ffd, &pid); - if (ret == -1) - return ret; - ret = fcntl(ffd, F_GETFL); - if (ret == -1) - return ret; - options |= (ret & O_NONBLOCK) ? WNOHANG : 0; - ret = wait4(pid, &status, options, rusage); - if (ret != -1 && info) - convertStatusToForkfdInfo(status, info); - return ret == -1 ? -1 : 0; -#endif - } + if (system_has_forkfd()) + return system_forkfd_wait(ffd, info, rusage); ret = read(ffd, &payload, sizeof(payload)); if (ret == -1) @@ -886,3 +819,28 @@ int forkfd_close(int ffd) { return close(ffd); } + +#if defined(__FreeBSD__) && __FreeBSD__ >= 9 +# include "forkfd_freebsd.c" +#else +int system_has_forkfd() +{ + return 0; +} + +int system_forkfd(int flags, pid_t *ppid, int *system) +{ + (void)flags; + (void)ppid; + *system = 0; + return -1; +} + +int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +{ + (void)ffd; + (void)info; + (void)rusage; + return -1; +} +#endif diff --git a/src/3rdparty/forkfd/forkfd.h b/src/3rdparty/forkfd/forkfd.h index 958321c299..eb121de593 100644 --- a/src/3rdparty/forkfd/forkfd.h +++ b/src/3rdparty/forkfd/forkfd.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2019 Intel Corporation. ** ** Permission is hereby granted, free of charge, to any person obtaining a copy ** of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,7 @@ #include <fcntl.h> #include <stdint.h> +#include <sys/wait.h> #include <unistd.h> // to get the POSIX flags #if _POSIX_SPAWN > 0 @@ -48,7 +49,7 @@ struct forkfd_info { }; int forkfd(int flags, pid_t *ppid); -int forkfd_wait(int ffd, forkfd_info *info, struct rusage *rusage); +int forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage); int forkfd_close(int ffd); #if _POSIX_SPAWN > 0 diff --git a/src/3rdparty/forkfd/forkfd_atomic.h b/src/3rdparty/forkfd/forkfd_atomic.h new file mode 100644 index 0000000000..394e30d26c --- /dev/null +++ b/src/3rdparty/forkfd/forkfd_atomic.h @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Intel Corporation. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#if !defined(FFD_ATOMIC_H) & !defined(FFD_ATOMIC_RELAXED) +#define FFD_ATOMIC_H + +#if defined(__cplusplus) && __cplusplus >= 201103L +# include "forkfd_c11.h" +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +# include "forkfd_c11.h" +#elif defined(__GNUC__) +# include "forkfd_gcc.h" +#endif + +#endif /* FFD_ATOMIC_h && FFD_ATOMIC_RELAXED */ +#ifndef FFD_ATOMIC_RELAXED +# error "Could not determine atomics for this platform" +#endif diff --git a/src/3rdparty/forkfd/forkfd_c11.h b/src/3rdparty/forkfd/forkfd_c11.h new file mode 100644 index 0000000000..f3dc2b5357 --- /dev/null +++ b/src/3rdparty/forkfd/forkfd_c11.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Intel Corporation. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef FFD_ATOMIC_C11_H +#define FFD_ATOMIC_C11_H + +/* atomics */ +/* Using the C11 <stdatomic.h> header or C++11's <atomic> + */ + +#if defined(__cplusplus) +# include <atomic> +# define ffd_atomic_pointer(type) std::atomic<type*> +# define FFD_ATOMIC_RELAXED std::memory_order_relaxed +# define FFD_ATOMIC_ACQUIRE std::memory_order_acquire +# define FFD_ATOMIC_RELEASE std::memory_order_release +// acq_rel & cst not necessary +typedef std::atomic_int ffd_atomic_int; +#else +# include <stdatomic.h> +# define ffd_atomic_pointer(type) _Atomic(type*) +# define FFD_ATOMIC_RELAXED memory_order_relaxed +# define FFD_ATOMIC_ACQUIRE memory_order_acquire +# define FFD_ATOMIC_RELEASE memory_order_release +// acq_rel & cst not necessary + +typedef atomic_int ffd_atomic_int; +#endif + +#define FFD_ATOMIC_INIT(val) ATOMIC_VAR_INIT(val) + +#define ffd_atomic_load(ptr, order) \ + atomic_load_explicit(ptr, order) +#define ffd_atomic_store(ptr, val, order) \ + atomic_store_explicit(ptr, val, order) +#define ffd_atomic_exchange(ptr,val,order) \ + atomic_exchange_explicit(ptr, val, order) +#define ffd_atomic_compare_exchange(ptr, expected, desired, order1, order2) \ + atomic_compare_exchange_strong_explicit(ptr, expected, desired, order1, order2) +#define ffd_atomic_add_fetch(ptr, val, order) \ + (atomic_fetch_add_explicit(ptr, val, order) + (val)) + +#endif diff --git a/src/3rdparty/forkfd/forkfd_freebsd.c b/src/3rdparty/forkfd/forkfd_freebsd.c new file mode 100644 index 0000000000..77ce3fcfad --- /dev/null +++ b/src/3rdparty/forkfd/forkfd_freebsd.c @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Intel Corporation. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#include "forkfd.h" + +#include <sys/types.h> +#include <sys/procdesc.h> + +#include "forkfd_atomic.h" + +#if __FreeBSD__ >= 10 +/* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option + * anymore and can't be disabled. */ +static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(1); +#else +static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(0); +#endif + +int system_has_forkfd() +{ + return ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED) > 0; +} + +int system_forkfd(int flags, pid_t *ppid, int *system) +{ + int ret; + pid_t pid; + + int state = ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED); + *system = 0; + if (state < 0) + return -1; + + pid = pdfork(&ret, PD_DAEMON); +# if __FreeBSD__ == 9 + if (state == 0 && pid != 0) { + /* Parent process: remember whether PROCDESC was compiled into the kernel */ + state = (pid == -1 && errno == ENOSYS) ? -1 : 1; + ffd_atomic_store(&system_forkfd_state, state, FFD_ATOMIC_RELAXED); + } + if (state < 0) + return -1; +# endif + *system = 1; + if (__builtin_expect(pid == -1, 0)) + return -1; + + if (pid == 0) { + /* child process */ + return FFD_CHILD_PROCESS; + } + + /* parent process */ + if (flags & FFD_CLOEXEC) + fcntl(ret, F_SETFD, FD_CLOEXEC); + if (flags & FFD_NONBLOCK) + fcntl(ret, F_SETFL, fcntl(ret, F_GETFL) | O_NONBLOCK); + if (ppid) + *ppid = pid; + return ret; +} + +int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage) +{ + pid_t pid; + int status; + int options = WEXITED; + + int ret = pdgetpid(ffd, &pid); + if (ret == -1) + return ret; + ret = fcntl(ffd, F_GETFL); + if (ret == -1) + return ret; + options |= (ret & O_NONBLOCK) ? WNOHANG : 0; + ret = wait4(pid, &status, options, rusage); + if (ret != -1 && info) + convertStatusToForkfdInfo(status, info); + return ret == -1 ? -1 : 0; +} diff --git a/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties b/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties index bf3de21830..4b7e1f3d38 100644 --- a/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties +++ b/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/android/jar/jar.pro b/src/android/jar/jar.pro index ac6fc79968..24a83d56a1 100644 --- a/src/android/jar/jar.pro +++ b/src/android/jar/jar.pro @@ -1,6 +1,7 @@ TARGET = QtAndroid CONFIG += java + DESTDIR = $$[QT_INSTALL_PREFIX/get]/jar PATHPREFIX = $$PWD/src/org/qtproject/qt5/android/ diff --git a/src/android/java/java.pro b/src/android/java/java.pro index 9d37eb1026..7f0dfa8a1b 100644 --- a/src/android/java/java.pro +++ b/src/android/java/java.pro @@ -1,3 +1,5 @@ +CONFIG += single_arch + CONFIG -= qt android_install javaresources.files = \ diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java index d3b0600b2f..45941e8ed8 100644 --- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, BogDan Vatra <bogdan@kde.org> + Copyright (c) 2019, BogDan Vatra <bogdan@kde.org> Contact: http://www.qt.io/licensing/ Commercial License Usage @@ -63,9 +63,12 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.List; import dalvik.system.DexClassLoader; @@ -85,7 +88,6 @@ public abstract class QtLoader { public static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables"; public static final String APPLICATION_PARAMETERS_KEY = "application.parameters"; public static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries"; - public static final String QT_LIBS_RESOURCE_ID_KEY = "android.app.qt_libs_resource_id"; public static final String BUNDLED_IN_LIB_RESOURCE_ID_KEY = "android.app.bundled_in_lib_resource_id"; public static final String BUNDLED_IN_ASSETS_RESOURCE_ID_KEY = "android.app.bundled_in_assets_resource_id"; public static final String MAIN_LIBRARY_KEY = "main.library"; @@ -153,7 +155,7 @@ public abstract class QtLoader { // this repository is used to push a new release, and should be used to test your application. // * unstable - unstable repository, DO NOT use this repository in production, // this repository is used to push Qt snapshots. - public String[] m_qtLibs = null; // required qt libs + public ArrayList<String> m_qtLibs = null; // required qt libs public int m_displayDensity = -1; private ContextWrapper m_context; protected ComponentInfo m_contextInfo; @@ -191,6 +193,36 @@ public abstract class QtLoader { } // Implement in subclass + private final List<String> supportedAbis = Arrays.asList(Build.SUPPORTED_ABIS); + private String preferredAbi = null; + + private ArrayList<String> prefferedAbiLibs(String []libs) + { + HashMap<String, ArrayList<String>> abisLibs = new HashMap<>(); + for (String lib : libs) { + String[] archLib = lib.split(";", 2); + if (preferredAbi != null && !archLib[0].equals(preferredAbi)) + continue; + if (!abisLibs.containsKey(archLib[0])) + abisLibs.put(archLib[0], new ArrayList<String>()); + abisLibs.get(archLib[0]).add(archLib[1]); + } + + if (preferredAbi != null) { + if (abisLibs.containsKey(preferredAbi)) { + return abisLibs.get(preferredAbi); + } + return new ArrayList<String>(); + } + + for (String abi: supportedAbis) { + if (abisLibs.containsKey(abi)) { + preferredAbi = abi; + return abisLibs.get(abi); + } + } + return new ArrayList<String>(); + } // this function is used to load and start the loader private void loadApplication(Bundle loaderParams) @@ -218,12 +250,14 @@ public abstract class QtLoader { // add all bundled Qt libs to loader params ArrayList<String> libs = new ArrayList<String>(); - if (m_contextInfo.metaData.containsKey("android.app.bundled_libs_resource_id")) - libs.addAll(Arrays.asList(m_context.getResources().getStringArray(m_contextInfo.metaData.getInt("android.app.bundled_libs_resource_id")))); + if (m_contextInfo.metaData.containsKey("android.app.bundled_libs_resource_id")) { + int resourceId = m_contextInfo.metaData.getInt("android.app.bundled_libs_resource_id"); + libs.addAll(prefferedAbiLibs(m_context.getResources().getStringArray(resourceId))); + } String libName = null; if (m_contextInfo.metaData.containsKey("android.app.lib_name")) { - libName = m_contextInfo.metaData.getString("android.app.lib_name"); + libName = m_contextInfo.metaData.getString("android.app.lib_name") + "_" + preferredAbi; loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function } @@ -278,7 +312,7 @@ public abstract class QtLoader { try { if (m_service != null) { Bundle parameters = new Bundle(); - parameters.putStringArray(REQUIRED_MODULES_KEY, m_qtLibs); + parameters.putStringArray(REQUIRED_MODULES_KEY, (String[]) m_qtLibs.toArray()); parameters.putString(APPLICATION_TITLE_KEY, getTitle()); parameters.putInt(MINIMUM_MINISTRO_API_KEY, MINISTRO_API_LEVEL); parameters.putInt(MINIMUM_QT_VERSION_KEY, QT_VERSION); @@ -464,7 +498,8 @@ public abstract class QtLoader { // why can't we load the plugins directly from libs ?!?! String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY; if (m_contextInfo.metaData.containsKey(key)) { - String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key)); + int resourceId = m_contextInfo.metaData.getInt(key); + ArrayList<String> list = prefferedAbiLibs(m_context.getResources().getStringArray(resourceId)); for (String bundledImportBinary : list) { String[] split = bundledImportBinary.split(":"); @@ -603,7 +638,7 @@ public abstract class QtLoader { if (m_contextInfo.metaData.containsKey("android.app.qt_libs_resource_id")) { int resourceId = m_contextInfo.metaData.getInt("android.app.qt_libs_resource_id"); - m_qtLibs = m_context.getResources().getStringArray(resourceId); + m_qtLibs = prefferedAbiLibs(m_context.getResources().getStringArray(resourceId)); } if (m_contextInfo.metaData.containsKey("android.app.use_local_qt_libs") @@ -617,6 +652,7 @@ public abstract class QtLoader { apkDeployFromSystem = true; String libsDir = null; + String bundledLibsDir = null; if (apkDeployFromSystem) { String systemLibsPrefix = SYSTEM_LIB_PATH; if (m_contextInfo.metaData.containsKey("android.app.system_libs_prefix")) { @@ -633,8 +669,11 @@ public abstract class QtLoader { } else { String nativeLibraryPrefix = m_context.getApplicationInfo().nativeLibraryDir + "/"; File nativeLibraryDir = new File(nativeLibraryPrefix); - if (nativeLibraryDir.exists() && nativeLibraryDir.isDirectory() && nativeLibraryDir.list().length > 0) + if (nativeLibraryDir.exists() && nativeLibraryDir.isDirectory() && nativeLibraryDir.list().length > 0) { libsDir = nativeLibraryPrefix; + bundledLibsDir = nativeLibraryPrefix; + } + } if (apkDeployFromSystem && libsDir == null) @@ -643,8 +682,8 @@ public abstract class QtLoader { if (m_qtLibs != null) { String libPrefix = libsDir + "lib"; - for (int i = 0; i < m_qtLibs.length; i++) - libraryList.add(libPrefix + m_qtLibs[i] + ".so"); + for (String lib : m_qtLibs) + libraryList.add(libPrefix + lib + ".so"); } if (m_contextInfo.metaData.containsKey("android.app.bundle_local_qt_libs") @@ -654,22 +693,26 @@ public abstract class QtLoader { String pluginsPrefix = dataPath + "qt-reserved-files/"; if (libsDir == null) - throw new Exception(""); + throw new Exception("Invalid libsDir"); cleanOldCacheIfNecessary(dataPath, pluginsPrefix); extractBundledPluginsAndImports(pluginsPrefix, libsDir); if (m_contextInfo.metaData.containsKey(BUNDLED_IN_LIB_RESOURCE_ID_KEY)) { - String[] extraLibs = m_contextInfo.metaData.getString("android.app.load_local_libs").split(":"); - for (String lib : extraLibs) { - if (!lib.isEmpty()) - libraryList.add(pluginsPrefix + lib); + int resourceId = m_contextInfo.metaData.getInt("android.app.load_local_libs_resource_id"); + for (String libs : prefferedAbiLibs(m_context.getResources().getStringArray(resourceId))) { + for (String lib : libs.split(":")) { + if (!lib.isEmpty()) + libraryList.add(libsDir + lib); + } } } ENVIRONMENT_VARIABLES += "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml" + "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports" + "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins"; + if (bundledLibsDir != null) + ENVIRONMENT_VARIABLES += "\tQT_BUNDLED_LIBS_PATH=" + bundledLibsDir; } Bundle loaderParams = new Bundle(); diff --git a/src/android/templates/AndroidManifest.xml b/src/android/templates/AndroidManifest.xml index aed8a3c888..75da314c2b 100644 --- a/src/android/templates/AndroidManifest.xml +++ b/src/android/templates/AndroidManifest.xml @@ -12,7 +12,7 @@ <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> - <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --"> + <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true"> <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" @@ -36,10 +36,11 @@ <meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/> <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/> + <!-- Run with local libs --> <meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/> <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/> - <meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/> + <meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/> <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/> <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/> <!-- Used to specify custom system library path to run with local system libs --> diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle index 8d4aa63153..d2da115936 100644 --- a/src/android/templates/build.gradle +++ b/src/android/templates/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.2.0' + classpath 'com.android.tools.build:gradle:3.5.0' } } diff --git a/src/android/templates/res/values/libs.xml b/src/android/templates/res/values/libs.xml index 4009a7785a..db777bf433 100644 --- a/src/android/templates/res/values/libs.xml +++ b/src/android/templates/res/values/libs.xml @@ -1,7 +1,7 @@ <?xml version='1.0' encoding='utf-8'?> <resources> <array name="qt_sources"> - <item>https://download.qt.io/ministro/android/qt5/qt-5.9</item> + <item>https://download.qt.io/ministro/android/qt5/qt-5.14</item> </array> <!-- The following is handled automatically by the deployment tool. It should @@ -12,12 +12,17 @@ </array> <array name="qt_libs"> - <!-- %%INSERT_QT_LIBS%% --> + <!-- %%INSERT_QT_LIBS%% --> </array> <array name="bundled_in_lib"> <!-- %%INSERT_BUNDLED_IN_LIB%% --> </array> + + <array name="load_local_libs"> + <!-- %%INSERT_LOCAL_LIBS%% --> + </array> + <array name="bundled_in_assets"> <!-- %%INSERT_BUNDLED_IN_ASSETS%% --> </array> diff --git a/src/android/templates/templates.pro b/src/android/templates/templates.pro index 55387f3af7..9a64251ee3 100644 --- a/src/android/templates/templates.pro +++ b/src/android/templates/templates.pro @@ -1,3 +1,5 @@ +CONFIG += single_arch + CONFIG -= qt android_install templates.files = \ diff --git a/src/corelib/codecs/qicucodec.cpp b/src/corelib/codecs/qicucodec.cpp index 101c3a3278..b9f1a814fa 100644 --- a/src/corelib/codecs/qicucodec.cpp +++ b/src/corelib/codecs/qicucodec.cpp @@ -42,8 +42,10 @@ #include "qtextcodec_p.h" #include "qutfcodec_p.h" #include "qlatincodec_p.h" +#if QT_CONFIG(codecs) #include "qtsciicodec_p.h" #include "qisciicodec_p.h" +#endif #include "qsimplecodec_p.h" #include "private/qcoreglobaldata_p.h" #include "qdebug.h" diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index b9bcc70e17..121db51eb5 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -12,6 +12,7 @@ CONFIG += qt_tracepoints CONFIG += $$MODULE_CONFIG DEFINES += $$MODULE_DEFINES +android: DEFINES += LIBS_SUFFIX='\\"_$${QT_ARCH}.so\\"' DEFINES += QT_NO_USING_NAMESPACE QT_NO_FOREACH msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x67000000 diff --git a/src/corelib/io/forkfd_qt.cpp b/src/corelib/io/forkfd_qt.cpp index 80d1cd54d7..cf44874153 100644 --- a/src/corelib/io/forkfd_qt.cpp +++ b/src/corelib/io/forkfd_qt.cpp @@ -37,37 +37,12 @@ ** ****************************************************************************/ -// these might be defined via precompiled headers -#include <QtCore/qatomic.h> +#include <QtCore/qglobal.h> #define FORKFD_NO_SPAWNFD - #if defined(QT_NO_DEBUG) && !defined(NDEBUG) # define NDEBUG #endif -typedef QT_PREPEND_NAMESPACE(QBasicAtomicInt) ffd_atomic_int; -#define ffd_atomic_pointer(type) QT_PREPEND_NAMESPACE(QBasicAtomicPointer<type>) - -QT_BEGIN_NAMESPACE - -#define FFD_ATOMIC_INIT(val) Q_BASIC_ATOMIC_INITIALIZER(val) - -#define FFD_ATOMIC_RELAXED Relaxed -#define FFD_ATOMIC_ACQUIRE Acquire -#define FFD_ATOMIC_RELEASE Release - -#define FFD_CONCAT(x, y) x ## y - -#define ffd_atomic_load(ptr,order) (ptr)->FFD_CONCAT(load, order)() -#define ffd_atomic_store(ptr,val,order) (ptr)->FFD_CONCAT(store, order)(val) -#define ffd_atomic_exchange(ptr,val,order) (ptr)->FFD_CONCAT(fetchAndStore, order)(val) -#define ffd_atomic_compare_exchange(ptr,expected,desired,order1,order2) \ - (ptr)->FFD_CONCAT(testAndSet, order1)(*expected, desired, *expected) -#define ffd_atomic_add_fetch(ptr,val,order) ((ptr)->FFD_CONCAT(fetchAndAdd, order)(val) + val) - -QT_END_NAMESPACE - -extern "C" { +#include <forkfd.h> #include "../../3rdparty/forkfd/forkfd.c" -} diff --git a/src/corelib/itemmodels/qabstractitemmodel.cpp b/src/corelib/itemmodels/qabstractitemmodel.cpp index c5fb5b9fc5..6e97c2fd39 100644 --- a/src/corelib/itemmodels/qabstractitemmodel.cpp +++ b/src/corelib/itemmodels/qabstractitemmodel.cpp @@ -1898,10 +1898,17 @@ bool QAbstractItemModel::clearItemData(const QModelIndex &index) */ bool QAbstractItemModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) { - bool b = true; - for (QMap<int, QVariant>::ConstIterator it = roles.begin(); it != roles.end(); ++it) - b = b && setData(index, it.value(), it.key()); - return b; + // ### Qt 6: Consider change the semantics of this function, + // or deprecating/removing it altogether. + // + // For instance, it should try setting *all* the data + // in \a roles, and not bail out at the first setData that returns + // false. It should also have a transactional approach. + for (auto it = roles.begin(), e = roles.end(); it != e; ++it) { + if (!setData(index, it.value(), it.key())) + return false; + } + return true; } /*! diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 4a26e9cdc0..9251a3c05f 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -527,28 +527,6 @@ inline void QMetaCallEvent::allocArgs() /*! \internal - Only used by QtDeclarative - to be removed when migrated. - */ -QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative, - QObjectPrivate::StaticMetaCallFunction callFunction, - const QObject *sender, int signalId, - int nargs, int *types_, void **args_) - : QAbstractMetaCallEvent(sender, signalId), - d({nullptr, nullptr, callFunction, nargs, method_offset, method_relative}), - prealloc_() -{ - allocArgs(); - for (int arg = 0; arg < nargs; ++arg) { - types()[arg] = types_[arg]; - args()[arg] = args_[arg]; - } - free(types_); - free(args_); -} - -/*! - \internal - Used for blocking queued connections, just passes \a args through without allocating any memory. */ diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 5d0b3a8399..feafcaf323 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -509,12 +509,6 @@ private: class Q_CORE_EXPORT QMetaCallEvent : public QAbstractMetaCallEvent { public: - // kept for compatibility until QtDeclarative has moved over - QMetaCallEvent(ushort method_offset, ushort method_relative, - QObjectPrivate::StaticMetaCallFunction callFunction, - const QObject *sender, int signalId, - int nargs, int *types, void **args); - // blocking queued with semaphore - args always owned by caller QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index 84fcc53d52..44d5513163 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -52,6 +52,10 @@ # include <private/qcore_mac_p.h> #endif +#ifdef Q_OS_ANDROID +# include <private/qjnihelpers_p.h> +#endif + QT_BEGIN_NAMESPACE static QString qdlerror() @@ -96,6 +100,9 @@ QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion) suffixes << QLatin1String(".so.%1").arg(fullVersion); } else { suffixes << QLatin1String(".so"); +# ifdef Q_OS_ANDROID + suffixes << QStringLiteral(LIBS_SUFFIX); +# endif } #endif # ifdef Q_OS_MAC @@ -157,9 +164,12 @@ bool QLibraryPrivate::load_sys() // Do not unload the library during dlclose(). Consequently, the // library's specific static variables are not reinitialized if the // library is reloaded with dlopen() at a later time. -#if defined(RTLD_NODELETE) && !defined(Q_OS_ANDROID) +#if defined(RTLD_NODELETE) if (loadHints & QLibrary::PreventUnloadHint) { - dlFlags |= RTLD_NODELETE; +# ifdef Q_OS_ANDROID // RTLD_NODELETE flag is supported by Android 23+ + if (QtAndroidPrivate::androidSdkVersion() > 22) +# endif + dlFlags |= RTLD_NODELETE; } #endif @@ -219,7 +229,14 @@ bool QLibraryPrivate::load_sys() } else { attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix); } + pHnd = dlopen(QFile::encodeName(attempt), dlFlags); +#ifdef Q_OS_ANDROID + if (!pHnd) { + auto attemptFromBundle = attempt; + pHnd = dlopen(QFile::encodeName(attemptFromBundle.replace(QLatin1Char('/'), QLatin1Char('_'))), dlFlags); + } +#endif if (!pHnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) { // We only want to continue if dlopen failed due to that the shared library did not exist. diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index ba616c0a7d..9053618014 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -915,7 +915,7 @@ void QCborContainerPrivate::replaceAt_complex(Element &e, const QCborValue &valu // in qstring.cpp void qt_to_latin1_unchecked(uchar *dst, const ushort *uc, qsizetype len); -Q_NEVER_INLINE void QCborContainerPrivate::appendAsciiString(const QString &s) +Q_NEVER_INLINE void QCborContainerPrivate::appendAsciiString(QStringView s) { qsizetype len = s.size(); QtCbor::Element e; @@ -926,7 +926,7 @@ Q_NEVER_INLINE void QCborContainerPrivate::appendAsciiString(const QString &s) char *ptr = data.data() + e.value + sizeof(ByteData); uchar *l = reinterpret_cast<uchar *>(ptr); - const ushort *uc = (const ushort *)s.unicode(); + const ushort *uc = (const ushort *)s.utf16(); qt_to_latin1_unchecked(l, uc, len); } @@ -1646,13 +1646,23 @@ QCborValue::QCborValue(const QByteArray &ba) container->ref.storeRelaxed(1); } +#if QT_STRINGVIEW_LEVEL < 2 /*! Creates a QCborValue with string value \a s. The value can later be retrieved using toString(). \sa toString(), isString(), isByteArray() */ -QCborValue::QCborValue(const QString &s) +QCborValue::QCborValue(const QString &s) : QCborValue(qToStringViewIgnoringNull(s)) {} +#endif + +/*! + Creates a QCborValue with string value \a s. The value can later be + retrieved using toString(). + + \sa toString(), isString(), isByteArray() +*/ +QCborValue::QCborValue(QStringView s) : n(0), container(new QCborContainerPrivate), t(String) { container->append(s); diff --git a/src/corelib/serialization/qcborvalue.h b/src/corelib/serialization/qcborvalue.h index f542e44c47..f79fc572c4 100644 --- a/src/corelib/serialization/qcborvalue.h +++ b/src/corelib/serialization/qcborvalue.h @@ -143,7 +143,10 @@ public: QCborValue(QCborSimpleType st) : t(type_helper(st)) {} QCborValue(const QByteArray &ba); +#if QT_STRINGVIEW_LEVEL < 2 QCborValue(const QString &s); +#endif + QCborValue(QStringView s); QCborValue(QLatin1String s); #ifndef QT_NO_CAST_FROM_ASCII QT_ASCII_CAST_WARN QCborValue(const char *s) : QCborValue(QString::fromUtf8(s)) {} diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h index 4050d18fa9..590c2d6e05 100644 --- a/src/corelib/serialization/qcborvalue_p.h +++ b/src/corelib/serialization/qcborvalue_p.h @@ -245,13 +245,21 @@ public: appendByteData(s.latin1(), s.size(), QCborValue::String, QtCbor::Element::StringIsAscii); } - void appendAsciiString(const QString &s); + void appendAsciiString(QStringView s); + +#if QT_STRINGVIEW_LEVEL < 2 void append(const QString &s) { + append(qToStringViewIgnoringNull(s)); + } +#endif + + void append(QStringView s) + { if (QtPrivate::isAscii(s)) appendAsciiString(s); else - appendByteData(reinterpret_cast<const char *>(s.constData()), s.size() * 2, + appendByteData(reinterpret_cast<const char *>(s.utf16()), s.size() * 2, QCborValue::String, QtCbor::Element::StringIsUtf16); } void append(const QCborValue &v) @@ -345,33 +353,41 @@ public: return e; } - bool stringEqualsElement(qsizetype idx, QLatin1String s) const + static int compareUtf8(const QtCbor::ByteData *b, const QLatin1String &s) { - const auto &e = elements.at(idx); - if (e.type != QCborValue::String) - return false; - - const QtCbor::ByteData *b = byteData(idx); - if (!b) - return s.isEmpty(); + return QUtf8::compareUtf8(b->byte(), b->len, s); + } - if (e.flags & QtCbor::Element::StringIsUtf16) - return QtPrivate::compareStrings(b->asStringView(), s) == 0; - return QUtf8::compareUtf8(b->byte(), b->len, s) == 0; + static int compareUtf8(const QtCbor::ByteData *b, QStringView s) + { + return QUtf8::compareUtf8(b->byte(), b->len, s.data(), s.size()); } - bool stringEqualsElement(qsizetype idx, const QString &s) const + + template<typename String> + int stringCompareElement(const QtCbor::Element &e, String s) const { - const auto &e = elements.at(idx); if (e.type != QCborValue::String) - return false; + return int(e.type) - int(QCborValue::String); - const QtCbor::ByteData *b = byteData(idx); + const QtCbor::ByteData *b = byteData(e); if (!b) - return s.isEmpty(); + return s.isEmpty() ? 0 : -1; if (e.flags & QtCbor::Element::StringIsUtf16) - return QtPrivate::compareStrings(b->asStringView(), s) == 0; - return QUtf8::compareUtf8(b->byte(), b->len, s.data(), s.size()) == 0; + return QtPrivate::compareStrings(b->asStringView(), s); + return compareUtf8(b, s); + } + + template<typename String> + bool stringEqualsElement(const QtCbor::Element &e, String s) const + { + return stringCompareElement(e, s) == 0; + } + + template<typename String> + bool stringEqualsElement(qsizetype idx, String s) const + { + return stringEqualsElement(elements.at(idx), s); } static int compareElement_helper(const QCborContainerPrivate *c1, QtCbor::Element e1, diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index 88286b902a..7b1351666d 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -57,15 +57,6 @@ #include <string> #include <iterator> -#if defined(Q_OS_ANDROID) && !defined(ANDROID_HAS_WSTRING) -// std::wstring is disabled on android's glibc, as bionic lacks certain features -// that libstdc++ checks for (like mbcslen). -namespace std -{ - typedef basic_string<wchar_t> wstring; -} -#endif - #include <stdarg.h> #ifdef truncate diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 38e9c1c3ec..cb3c0d6bb1 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -108,20 +108,8 @@ Q_STATIC_ASSERT(sizeof(pthread_t) <= sizeof(Qt::HANDLE)); enum { ThreadPriorityResetFlag = 0x80000000 }; -#if defined(Q_OS_LINUX) && defined(__GLIBC__) && (defined(Q_CC_GNU) || defined(Q_CC_INTEL)) && !defined(QT_LINUXBASE) -/* LSB doesn't have __thread, https://lsbbugs.linuxfoundation.org/show_bug.cgi?id=993 */ -#define HAVE_TLS -#endif -#if defined(Q_CC_XLC) || defined (Q_CC_SUN) -#define HAVE_TLS -#endif -#if defined(Q_OS_RTEMS) -#define HAVE_TLS -#endif -#ifdef HAVE_TLS -static __thread QThreadData *currentThreadData = 0; -#endif +static thread_local QThreadData *currentThreadData = 0; static pthread_once_t current_thread_data_once = PTHREAD_ONCE_INIT; static pthread_key_t current_thread_data_key; @@ -182,28 +170,19 @@ Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key) // Utility functions for getting, setting and clearing thread specific data. static QThreadData *get_thread_data() { -#ifdef HAVE_TLS return currentThreadData; -#else - pthread_once(¤t_thread_data_once, create_current_thread_data_key); - return reinterpret_cast<QThreadData *>(pthread_getspecific(current_thread_data_key)); -#endif } static void set_thread_data(QThreadData *data) { -#ifdef HAVE_TLS currentThreadData = data; -#endif pthread_once(¤t_thread_data_once, create_current_thread_data_key); pthread_setspecific(current_thread_data_key, data); } static void clear_thread_data() { -#ifdef HAVE_TLS currentThreadData = 0; -#endif pthread_setspecific(current_thread_data_key, 0); } diff --git a/src/corelib/tools/qsize.cpp b/src/corelib/tools/qsize.cpp index fe508ad459..2cbaae117d 100644 --- a/src/corelib/tools/qsize.cpp +++ b/src/corelib/tools/qsize.cpp @@ -390,7 +390,25 @@ QSize QSize::scaled(const QSize &s, Qt::AspectRatioMode mode) const noexcept \sa expandedTo(), scale() */ +/*! + \fn QSize QSize::grownBy(QMargins margins) const + \fn QSizeF QSizeF::grownBy(QMarginsF margins) const + \since 5.14 + + Returns the size that results from growing this size by \a margins. + + \sa shrunkBy() +*/ +/*! + \fn QSize QSize::shrunkBy(QMargins margins) const + \fn QSizeF QSizeF::shrunkBy(QMarginsF margins) const + \since 5.14 + + Returns the size that results from shrinking this size by \a margins. + + \sa grownBy() +*/ /***************************************************************************** QSize stream functions diff --git a/src/corelib/tools/qsize.h b/src/corelib/tools/qsize.h index 4114609856..06de1cd63f 100644 --- a/src/corelib/tools/qsize.h +++ b/src/corelib/tools/qsize.h @@ -41,6 +41,7 @@ #define QSIZE_H #include <QtCore/qnamespace.h> +#include <QtCore/qmargins.h> #if defined(Q_OS_DARWIN) || defined(Q_QDOC) struct CGSize; @@ -74,6 +75,11 @@ public: Q_REQUIRED_RESULT Q_DECL_CONSTEXPR inline QSize expandedTo(const QSize &) const noexcept; Q_REQUIRED_RESULT Q_DECL_CONSTEXPR inline QSize boundedTo(const QSize &) const noexcept; + Q_REQUIRED_RESULT Q_DECL_CONSTEXPR QSize grownBy(QMargins m) const noexcept + { return {width() + m.left() + m.right(), height() + m.top() + m.bottom()}; } + Q_REQUIRED_RESULT Q_DECL_CONSTEXPR QSize shrunkBy(QMargins m) const noexcept + { return {width() - m.left() - m.right(), height() - m.top() - m.bottom()}; } + Q_DECL_RELAXED_CONSTEXPR inline int &rwidth() noexcept; Q_DECL_RELAXED_CONSTEXPR inline int &rheight() noexcept; @@ -238,6 +244,11 @@ public: Q_REQUIRED_RESULT Q_DECL_CONSTEXPR inline QSizeF expandedTo(const QSizeF &) const noexcept; Q_REQUIRED_RESULT Q_DECL_CONSTEXPR inline QSizeF boundedTo(const QSizeF &) const noexcept; + Q_REQUIRED_RESULT Q_DECL_CONSTEXPR QSizeF grownBy(QMarginsF m) const noexcept + { return {width() + m.left() + m.right(), height() + m.top() + m.bottom()}; } + Q_REQUIRED_RESULT Q_DECL_CONSTEXPR QSizeF shrunkBy(QMarginsF m) const noexcept + { return {width() - m.left() - m.right(), height() - m.top() - m.bottom()}; } + Q_DECL_RELAXED_CONSTEXPR inline qreal &rwidth() noexcept; Q_DECL_RELAXED_CONSTEXPR inline qreal &rheight() noexcept; diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf index 049b9ef179..76dd6d7ea1 100644 --- a/src/gui/doc/qtgui.qdocconf +++ b/src/gui/doc/qtgui.qdocconf @@ -67,3 +67,6 @@ navigation.cppclassespage = "Qt GUI C++ Classes" # Ignore warnings about undocumented enum values for the QGradient presets spurious += "Undocumented enum item '.*' in QGradient::Preset" + +# \svgcolor {#ffdead} +macro.svgcolor.HTML = "<div style=\"padding:10px;color:#fff;background:\1;\"></div>" diff --git a/src/gui/doc/src/includes/qt-colors.qdocinc b/src/gui/doc/src/includes/qt-colors.qdocinc new file mode 100644 index 0000000000..4c082323b6 --- /dev/null +++ b/src/gui/doc/src/includes/qt-colors.qdocinc @@ -0,0 +1,86 @@ +\table +\header + \li Name + \li Hex + \li Color +\row + \li Color0 + \li #000000 + \li \svgcolor {#000000} +\row + \li Color1 + \li #ffffff + \li \svgcolor {#ffffff} +\row + \li Black + \li #000000 + \li \svgcolor {#000000} +\row + \li White + \li #ffffff + \li \svgcolor {#ffffff} +\row + \li DarkGray + \li #808080 + \li \svgcolor {#808080} +\row + \li Gray + \li #a0a0a4 + \li \svgcolor {#a0a0a4} +\row + \li LightGray + \li #c0c0c0 + \li \svgcolor {#c0c0c0} +\row + \li Red + \li #ff0000 + \li \svgcolor {#ff0000} +\row + \li Green + \li #00ff00 + \li \svgcolor {#00ff00} +\row + \li Blue + \li #0000ff + \li \svgcolor {#0000ff} +\row + \li Cyan + \li #00ffff + \li \svgcolor {#00ffff} +\row + \li Magenta + \li #ff00ff + \li \svgcolor {#ff00ff} +\row + \li Yellow + \li #ffff00 + \li \svgcolor {#ffff00} +\row + \li DarkRed + \li #800000 + \li \svgcolor {#800000} +\row + \li DarkGreen + \li #008000 + \li \svgcolor {#008000} +\row + \li DarkBlue + \li #000080 + \li \svgcolor {#000080} +\row + \li DarkCyan + \li #008080 + \li \svgcolor {#008080} +\row + \li DarkMagenta + \li #800080 + \li \svgcolor {#800080} +\row + \li DarkYellow + \li #808000 + \li \svgcolor {#808000} +\row + \li Transparent + \li #00000000 + \li (transparent) +\endtable diff --git a/src/gui/doc/src/includes/svg-colors.qdocinc b/src/gui/doc/src/includes/svg-colors.qdocinc new file mode 100644 index 0000000000..4e5fb56d5e --- /dev/null +++ b/src/gui/doc/src/includes/svg-colors.qdocinc @@ -0,0 +1,594 @@ +\table +\header + \li Name + \li Hex + \li Color +\row + \li aliceblue + \li #f0f8ff + \li \svgcolor {#f0f8ff} +\row + \li antiquewhite + \li #faebd7 + \li \svgcolor {#faebd7} +\row + \li aqua + \li #00ffff + \li \svgcolor {#00ffff} +\row + \li aquamarine + \li #7fffd4 + \li \svgcolor {#7fffd4} +\row + \li azure + \li #f0ffff + \li \svgcolor {#f0ffff} +\row + \li beige + \li #f5f5dc + \li \svgcolor {#f5f5dc} +\row + \li bisque + \li #ffe4c4 + \li \svgcolor {#ffe4c4} +\row + \li black + \li #000000 + \li \svgcolor {#000000} +\row + \li blanchedalmond + \li #ffebcd + \li \svgcolor {#ffebcd} +\row + \li blue + \li #0000ff + \li \svgcolor {#0000ff} +\row + \li blueviolet + \li #8a2be2 + \li \svgcolor {#8a2be2} +\row + \li brown + \li #a52a2a + \li \svgcolor {#a52a2a} +\row + \li burlywood + \li #deb887 + \li \svgcolor {#deb887} +\row + \li cadetblue + \li #5f9ea0 + \li \svgcolor {#5f9ea0} +\row + \li chartreuse + \li #7fff00 + \li \svgcolor {#7fff00} +\row + \li chocolate + \li #d2691e + \li \svgcolor {#d2691e} +\row + \li coral + \li #ff7f50 + \li \svgcolor {#ff7f50} +\row + \li cornflowerblue + \li #6495ed + \li \svgcolor {#6495ed} +\row + \li cornsilk + \li #fff8dc + \li \svgcolor {#fff8dc} +\row + \li crimson + \li #dc143c + \li \svgcolor {#dc143c} +\row + \li cyan + \li #00ffff + \li \svgcolor {#00ffff} +\row + \li darkblue + \li #00008b + \li \svgcolor {#00008b} +\row + \li darkcyan + \li #008b8b + \li \svgcolor {#008b8b} +\row + \li darkgoldenrod + \li #b8860b + \li \svgcolor {#b8860b} +\row + \li darkgray + \li #a9a9a9 + \li \svgcolor {#a9a9a9} +\row + \li darkgreen + \li #006400 + \li \svgcolor {#006400} +\row + \li darkgrey + \li #a9a9a9 + \li \svgcolor {#a9a9a9} +\row + \li darkkhaki + \li #bdb76b + \li \svgcolor {#bdb76b} +\row + \li darkmagenta + \li #8b008b + \li \svgcolor {#8b008b} +\row + \li darkolivegreen + \li #556b2f + \li \svgcolor {#556b2f} +\row + \li darkorange + \li #ff8c00 + \li \svgcolor {#ff8c00} +\row + \li darkorchid + \li #9932cc + \li \svgcolor {#9932cc} +\row + \li darkred + \li #8b0000 + \li \svgcolor {#8b0000} +\row + \li darksalmon + \li #e9967a + \li \svgcolor {#e9967a} +\row + \li darkseagreen + \li #8fbc8f + \li \svgcolor {#8fbc8f} +\row + \li darkslateblue + \li #483d8b + \li \svgcolor {#483d8b} +\row + \li darkslategray + \li #2f4f4f + \li \svgcolor {#2f4f4f} +\row + \li darkslategrey + \li #2f4f4f + \li \svgcolor {#2f4f4f} +\row + \li darkturquoise + \li #00ced1 + \li \svgcolor {#00ced1} +\row + \li darkviolet + \li #9400d3 + \li \svgcolor {#9400d3} +\row + \li deeppink + \li #ff1493 + \li \svgcolor {#ff1493} +\row + \li deepskyblue + \li #00bfff + \li \svgcolor {#00bfff} +\row + \li dimgray + \li #696969 + \li \svgcolor {#696969} +\row + \li dimgrey + \li #696969 + \li \svgcolor {#696969} +\row + \li dodgerblue + \li #1e90ff + \li \svgcolor {#1e90ff} +\row + \li firebrick + \li #b22222 + \li \svgcolor {#b22222} +\row + \li floralwhite + \li #fffaf0 + \li \svgcolor {#fffaf0} +\row + \li forestgreen + \li #228b22 + \li \svgcolor {#228b22} +\row + \li fuchsia + \li #ff00ff + \li \svgcolor {#ff00ff} +\row + \li gainsboro + \li #dcdcdc + \li \svgcolor {#dcdcdc} +\row + \li ghostwhite + \li #f8f8ff + \li \svgcolor {#f8f8ff} +\row + \li gold + \li #ffd700 + \li \svgcolor {#ffd700} +\row + \li goldenrod + \li #daa520 + \li \svgcolor {#daa520} +\row + \li gray + \li #808080 + \li \svgcolor {#808080} +\row + \li grey + \li #808080 + \li \svgcolor {#808080} +\row + \li green + \li #008000 + \li \svgcolor {#008000} +\row + \li greenyellow + \li #adff2f + \li \svgcolor {#adff2f} +\row + \li honeydew + \li #f0fff0 + \li \svgcolor {#f0fff0} +\row + \li hotpink + \li #ff69b4 + \li \svgcolor {#ff69b4} +\row + \li indianred + \li #cd5c5c + \li \svgcolor {#cd5c5c} +\row + \li indigo + \li #4b0082 + \li \svgcolor {#4b0082} +\row + \li ivory + \li #fffff0 + \li \svgcolor {#fffff0} +\row + \li khaki + \li #f0e68c + \li \svgcolor {#f0e68c} +\row + \li lavender + \li #e6e6fa + \li \svgcolor {#e6e6fa} +\row + \li lavenderblush + \li #fff0f5 + \li \svgcolor {#fff0f5} +\row + \li lawngreen + \li #7cfc00 + \li \svgcolor {#7cfc00} +\row + \li lemonchiffon + \li #fffacd + \li \svgcolor {#fffacd} +\row + \li lightblue + \li #add8e6 + \li \svgcolor {#add8e6} +\row + \li lightcoral + \li #f08080 + \li \svgcolor {#f08080} +\row + \li lightcyan + \li #e0ffff + \li \svgcolor {#e0ffff} +\row + \li lightgoldenrodyellow + \li #fafad2 + \li \svgcolor {#fafad2} +\row + \li lightgray + \li #d3d3d3 + \li \svgcolor {#d3d3d3} +\row + \li lightgreen + \li #90ee90 + \li \svgcolor {#90ee90} +\row + \li lightgrey + \li #d3d3d3 + \li \svgcolor {#d3d3d3} +\row + \li lightpink + \li #ffb6c1 + \li \svgcolor {#ffb6c1} +\row + \li lightsalmon + \li #ffa07a + \li \svgcolor {#ffa07a} +\row + \li lightseagreen + \li #20b2aa + \li \svgcolor {#20b2aa} +\row + \li lightskyblue + \li #87cefa + \li \svgcolor {#87cefa} +\row + \li lightslategray + \li #778899 + \li \svgcolor {#778899} +\row + \li lightslategrey + \li #778899 + \li \svgcolor {#778899} +\row + \li lightsteelblue + \li #b0c4de + \li \svgcolor {#b0c4de} +\row + \li lightyellow + \li #ffffe0 + \li \svgcolor {#ffffe0} +\row + \li lime + \li #00ff00 + \li \svgcolor {#00ff00} +\row + \li limegreen + \li #32cd32 + \li \svgcolor {#32cd32} +\row + \li linen + \li #faf0e6 + \li \svgcolor {#faf0e6} +\row + \li magenta + \li #ff00ff + \li \svgcolor {#ff00ff} +\row + \li maroon + \li #800000 + \li \svgcolor {#800000} +\row + \li mediumaquamarine + \li #66cdaa + \li \svgcolor {#66cdaa} +\row + \li mediumblue + \li #0000cd + \li \svgcolor {#0000cd} +\row + \li mediumorchid + \li #ba55d3 + \li \svgcolor {#ba55d3} +\row + \li mediumpurple + \li #9370db + \li \svgcolor {#9370db} +\row + \li mediumseagreen + \li #3cb371 + \li \svgcolor {#3cb371} +\row + \li mediumslateblue + \li #7b68ee + \li \svgcolor {#7b68ee} +\row + \li mediumspringgreen + \li #00fa9a + \li \svgcolor {#00fa9a} +\row + \li mediumturquoise + \li #48d1cc + \li \svgcolor {#48d1cc} +\row + \li mediumvioletred + \li #c71585 + \li \svgcolor {#c71585} +\row + \li midnightblue + \li #191970 + \li \svgcolor {#191970} +\row + \li mintcream + \li #f5fffa + \li \svgcolor {#f5fffa} +\row + \li mistyrose + \li #ffe4e1 + \li \svgcolor {#ffe4e1} +\row + \li moccasin + \li #ffe4b5 + \li \svgcolor {#ffe4b5} +\row + \li navajowhite + \li #ffdead + \li \svgcolor {#ffdead} +\row + \li navy + \li #000080 + \li \svgcolor {#000080} +\row + \li oldlace + \li #fdf5e6 + \li \svgcolor {#fdf5e6} +\row + \li olive + \li #808000 + \li \svgcolor {#808000} +\row + \li olivedrab + \li #6b8e23 + \li \svgcolor {#6b8e23} +\row + \li orange + \li #ffa500 + \li \svgcolor {#ffa500} +\row + \li orangered + \li #ff4500 + \li \svgcolor {#ff4500} +\row + \li orchid + \li #da70d6 + \li \svgcolor {#da70d6} +\row + \li palegoldenrod + \li #eee8aa + \li \svgcolor {#eee8aa} +\row + \li palegreen + \li #98fb98 + \li \svgcolor {#98fb98} +\row + \li paleturquoise + \li #afeeee + \li \svgcolor {#afeeee} +\row + \li palevioletred + \li #db7093 + \li \svgcolor {#db7093} +\row + \li papayawhip + \li #ffefd5 + \li \svgcolor {#ffefd5} +\row + \li peachpuff + \li #ffdab9 + \li \svgcolor {#ffdab9} +\row + \li peru + \li #cd853f + \li \svgcolor {#cd853f} +\row + \li pink + \li #ffc0cb + \li \svgcolor {#ffc0cb} +\row + \li plum + \li #dda0dd + \li \svgcolor {#dda0dd} +\row + \li powderblue + \li #b0e0e6 + \li \svgcolor {#b0e0e6} +\row + \li purple + \li #800080 + \li \svgcolor {#800080} +\row + \li red + \li #ff0000 + \li \svgcolor {#ff0000} +\row + \li rosybrown + \li #bc8f8f + \li \svgcolor {#bc8f8f} +\row + \li royalblue + \li #4169e1 + \li \svgcolor {#4169e1} +\row + \li saddlebrown + \li #8b4513 + \li \svgcolor {#8b4513} +\row + \li salmon + \li #fa8072 + \li \svgcolor {#fa8072} +\row + \li sandybrown + \li #f4a460 + \li \svgcolor {#f4a460} +\row + \li seagreen + \li #2e8b57 + \li \svgcolor {#2e8b57} +\row + \li seashell + \li #fff5ee + \li \svgcolor {#fff5ee} +\row + \li sienna + \li #a0522d + \li \svgcolor {#a0522d} +\row + \li silver + \li #c0c0c0 + \li \svgcolor {#c0c0c0} +\row + \li skyblue + \li #87ceeb + \li \svgcolor {#87ceeb} +\row + \li slateblue + \li #6a5acd + \li \svgcolor {#6a5acd} +\row + \li slategray + \li #708090 + \li \svgcolor {#708090} +\row + \li slategrey + \li #708090 + \li \svgcolor {#708090} +\row + \li snow + \li #fffafa + \li \svgcolor {#fffafa} +\row + \li springgreen + \li #00ff7f + \li \svgcolor {#00ff7f} +\row + \li steelblue + \li #4682b4 + \li \svgcolor {#4682b4} +\row + \li tan + \li #d2b48c + \li \svgcolor {#d2b48c} +\row + \li teal + \li #008080 + \li \svgcolor {#008080} +\row + \li thistle + \li #d8bfd8 + \li \svgcolor {#d8bfd8} +\row + \li tomato + \li #ff6347 + \li \svgcolor {#ff6347} +\row + \li turquoise + \li #40e0d0 + \li \svgcolor {#40e0d0} +\row + \li violet + \li #ee82ee + \li \svgcolor {#ee82ee} +\row + \li wheat + \li #f5deb3 + \li \svgcolor {#f5deb3} +\row + \li white + \li #ffffff + \li \svgcolor {#ffffff} +\row + \li whitesmoke + \li #f5f5f5 + \li \svgcolor {#f5f5f5} +\row + \li yellow + \li #ffff00 + \li \svgcolor {#ffff00} +\row + \li yellowgreen + \li #9acd32 + \li \svgcolor {#9acd32} +\endtable diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index 70fccbc378..3b2ced3f58 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -82,7 +82,18 @@ qtConfig(png) { } # SIMD -SSSE3_SOURCES += image/qimage_ssse3.cpp -NEON_SOURCES += image/qimage_neon.cpp -MIPS_DSPR2_SOURCES += image/qimage_mips_dspr2.cpp -MIPS_DSPR2_ASM += image/qimage_mips_dspr2_asm.S +!android { + SSSE3_SOURCES += image/qimage_ssse3.cpp + NEON_SOURCES += image/qimage_neon.cpp + MIPS_DSPR2_SOURCES += image/qimage_mips_dspr2.cpp + MIPS_DSPR2_ASM += image/qimage_mips_dspr2_asm.S +} else { + # see https://developer.android.com/ndk/guides/abis + arm64-v8a { + SOURCES += image/qimage_neon.cpp + } + x86 | x86_64 { + DEFINES += QT_COMPILER_SUPPORTS_SSE2 QT_COMPILER_SUPPORTS_SSE3 QT_COMPILER_SUPPORTS_SSSE3 + SOURCES += image/qimage_ssse3.cpp + } +} diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 8e0bbb6907..b7f67cd7ef 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -287,6 +287,7 @@ bool QImageData::checkForAlphaPixels() const case QImage::Format_RGB555: case QImage::Format_RGB666: case QImage::Format_RGB888: + case QImage::Format_BGR888: case QImage::Format_RGBX8888: case QImage::Format_BGR30: case QImage::Format_RGB30: @@ -720,6 +721,7 @@ bool QImageData::checkForAlphaPixels() const \value Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12) \value Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12) + \value Format_BGR888 The image is stored using a 24-bit BGR format. (added in Qt 5.14) \note Drawing into a QImage with QImage::Format_Indexed8 is not supported. @@ -5550,6 +5552,19 @@ static Q_CONSTEXPR QPixelFormat pixelformats[] = { /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, /*INTERPRETATION*/ QPixelFormat::UnsignedShort, /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_BGR888: + QPixelFormat(QPixelFormat::BGR, + /*RED*/ 8, + /*GREEN*/ 8, + /*BLUE*/ 8, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 0, + /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtBeginning, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedByte, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), }; Q_STATIC_ASSERT(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats); diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 7c68168be8..7544ccca05 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -131,6 +131,7 @@ public: Format_RGBA64, Format_RGBA64_Premultiplied, Format_Grayscale16, + Format_BGR888, #ifndef Q_QDOC NImageFormats #endif diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 837ac88470..539bac222a 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -429,8 +429,8 @@ typedef void (QT_FASTCALL *Rgb888ToRgbConverter)(quint32 *dst, const uchar *src, template <bool rgbx> static void convert_RGB888_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { - Q_ASSERT(src->format == QImage::Format_RGB888); - if (rgbx) + Q_ASSERT(src->format == QImage::Format_RGB888 || src->format == QImage::Format_BGR888); + if (rgbx ^ (src->format == QImage::Format_BGR888)) Q_ASSERT(dest->format == QImage::Format_RGBX8888 || dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied); else Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied); @@ -1421,6 +1421,69 @@ static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt } } +static void convert_RGB888_to_BGR888(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGB888 || src->format == QImage::Format_BGR888); + Q_ASSERT(dest->format == QImage::Format_RGB888 || dest->format == QImage::Format_BGR888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const qsizetype sbpl = src->bytes_per_line; + const qsizetype dbpl = dest->bytes_per_line; + const uchar *src_data = src->data; + uchar *dest_data = dest->data; + + for (int i = 0; i < src->height; ++i) { + int pixel = 0; + // Handle 4 pixels (12 bytes) at a time + for (; pixel + 3 < src->width; pixel += 4) { + const uchar *src = src_data + pixel * 3; + quint32 *dest_packed = (quint32 *) (dest_data + pixel * 3); + dest_packed[0] = (src[5] << 24) | (src[0] << 16) | (src[1] << 8) | (src[2] << 0); + dest_packed[1] = (src[7] << 24) | (src[8] << 16) | (src[3] << 8) | (src[4] << 0); + dest_packed[2] = (src[9] << 24) | (src[10] << 16) | (src[11] << 8) | (src[6] << 0); + } + + // epilog: handle left over pixels + for (; pixel < src->width; ++pixel) { + dest_data[pixel * 3 + 0] = src_data[pixel * 3 + 2]; + dest_data[pixel * 3 + 1] = src_data[pixel * 3 + 1]; + dest_data[pixel * 3 + 2] = src_data[pixel * 3 + 0]; + } + + src_data += sbpl; + dest_data += dbpl; + } +} + +static bool convert_RGB888_to_BGR888_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGB888 || data->format == QImage::Format_BGR888); + + const qsizetype bpl = data->bytes_per_line; + uchar *line_data = data->data; + + for (int i = 0; i < data->height; ++i) { + for (int j = 0; j < data->width; ++j) + qSwap(line_data[j * 3 + 0], line_data[j * 3 + 2]); + line_data += bpl; + } + + switch (data->format) { + case QImage::Format_RGB888: + data->format = QImage::Format_BGR888; + break; + case QImage::Format_BGR888: + data->format = QImage::Format_RGB888; + break; + default: + Q_UNREACHABLE(); + data->format = QImage::Format_Invalid; + return false; + } + return true; +} + static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format) { QVector<QRgb> colorTable = ctbl; @@ -2265,7 +2328,7 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = { { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, @@ -2286,7 +2349,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Mono { @@ -2308,7 +2371,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_MonoLSB { @@ -2333,7 +2396,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, 0, 0, convert_Indexed8_to_Alpha8, convert_Indexed8_to_Grayscale8, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_Indexed8 { @@ -2361,7 +2424,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGB_to_RGB30<PixelOrderRGB, false>, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGB32 { @@ -2391,7 +2454,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, convert_ARGB32_to_RGBA64<false>, - 0, 0 + 0, 0, 0 }, // Format_ARGB32 { @@ -2416,7 +2479,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_ARGB_to_RGBA, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_ARGB32_Premultiplied { @@ -2438,7 +2501,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB16 { @@ -2460,7 +2523,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8565_Premultiplied { @@ -2482,7 +2545,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB666 { @@ -2504,7 +2567,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB6666_Premultiplied { @@ -2526,7 +2589,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB555 { @@ -2548,7 +2611,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8555_Premultiplied { @@ -2565,13 +2628,14 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, + 0, // self 0, 0, convert_RGB888_to_RGB<true>, convert_RGB888_to_RGB<true>, convert_RGB888_to_RGB<true>, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + convert_RGB888_to_BGR888, }, // Format_RGB888 { @@ -2593,7 +2657,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB444 { @@ -2614,7 +2678,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB4444_Premultiplied { 0, @@ -2641,7 +2705,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGB_to_RGB30<PixelOrderRGB, true>, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGBX8888 { 0, @@ -2670,7 +2734,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, convert_ARGB32_to_RGBA64<true>, - 0, 0 + 0, 0, 0 }, // Format_RGBA8888 { @@ -2692,7 +2756,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888_Premultiplied { @@ -2720,7 +2784,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_BGR30_to_RGB30, convert_BGR30_to_RGB30, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_BGR30 { 0, @@ -2747,7 +2811,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_A2RGB30_PM_to_RGB30<true>, convert_BGR30_to_RGB30, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_A2BGR30_Premultiplied { 0, @@ -2773,7 +2837,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_BGR30_to_RGB30, 0, convert_passthrough, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB30 { 0, @@ -2800,7 +2864,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_A2RGB30_PM_to_RGB30<false>, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_A2RGB30_Premultiplied { 0, @@ -2820,7 +2884,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Alpha8 { 0, @@ -2840,14 +2904,15 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Grayscale8 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // self convert_passthrough, convert_passthrough, - convert_RGBA64_to_gray16 + convert_RGBA64_to_gray16, + 0 }, // Format_RGBX64 { 0, @@ -2874,6 +2939,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGBA64_to_RGBx64, 0, // self convert_RGBA64_to_RGBA64PM, + 0, 0 }, // Format_RGBA64 { @@ -2901,7 +2967,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGBA64PM_to_RGBA64<true>, convert_RGBA64PM_to_RGBA64<false>, 0, // self - convert_RGBA64_to_gray16 + convert_RGBA64_to_gray16, + 0 }, // Format_RGBA64_Premultiplied { 0, @@ -2931,20 +2998,46 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_gray16_to_RGBA64, convert_gray16_to_RGBA64, convert_gray16_to_RGBA64, - 0 // self + 0, // self + 0 }, // Format_Grayscale16 + { + 0, + 0, + 0, + 0, + 0, 0, 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGB888_to_BGR888, + 0, + 0, +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + convert_RGB888_to_RGB<false>, + convert_RGB888_to_RGB<false>, + convert_RGB888_to_RGB<false>, +#else + 0, 0, 0, +#endif + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // self + }, // Format_BGR888 }; InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = { { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Mono { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_MonoLSB { 0, @@ -2968,7 +3061,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, 0, 0, convert_Indexed8_to_Alpha8_inplace, convert_Indexed8_to_Grayscale8_inplace, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_Indexed8 { 0, @@ -2995,7 +3088,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGB32 { 0, @@ -3022,7 +3115,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_RGB_to_RGB30_inplace<PixelOrderRGB, false>, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_ARGB32 { 0, @@ -3046,34 +3139,35 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888_Premultiplied>, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_ARGB32_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB16 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8565_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB666 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB6666_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB555 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8555_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + convert_RGB888_to_BGR888_inplace }, // Format_RGB888 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB444 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB4444_Premultiplied { 0, @@ -3100,7 +3194,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGBX8888 { 0, @@ -3127,7 +3221,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_RGB_to_RGB30_inplace<PixelOrderRGB, true>, 0, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGBA8888 { 0, @@ -3149,7 +3243,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888_Premultiplied { 0, @@ -3176,7 +3270,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_BGR30_to_RGB30_inplace, convert_BGR30_to_A2RGB30_inplace, 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_BGR30 { 0, @@ -3202,7 +3296,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, // self convert_A2RGB30_PM_to_RGB30_inplace<true>, convert_BGR30_to_RGB30_inplace, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0 }, // Format_A2BGR30_Premultiplied { 0, @@ -3228,7 +3322,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_BGR30_to_A2RGB30_inplace, 0, // self convert_passthrough_inplace<QImage::Format_A2RGB30_Premultiplied>, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB30 { 0, @@ -3255,7 +3349,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_A2RGB30_PM_to_RGB30_inplace<false>, 0, // self 0, 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_A2RGB30_Premultiplied { 0, @@ -3280,7 +3374,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, 0, 0, // self 0, - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_Alpha8 { 0, @@ -3305,32 +3399,37 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, 0, 0, 0, // self - 0, 0, 0, 0 + 0, 0, 0, 0, 0 }, // Format_Grayscale8 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // self convert_passthrough_inplace<QImage::Format_RGBA64>, convert_passthrough_inplace<QImage::Format_RGBA64_Premultiplied>, - 0 + 0, 0 }, // Format_RGBX64 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, convert_RGBA64_to_RGBx64_inplace, 0, // self convert_RGBA64_to_RGBA64PM_inplace, - 0 + 0, 0 }, // Format_RGBA64 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, convert_RGBA64PM_to_RGBA64_inplace<true>, convert_RGBA64PM_to_RGBA64_inplace<false>, 0, // self - 0 + 0, 0 }, // Format_RGBA64_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Grayscale16 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + convert_RGB888_to_BGR888_inplace, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_BGR888 }; static void qInitImageConversions() @@ -3341,6 +3440,9 @@ static void qInitImageConversions() qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_ssse3; qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_ssse3; qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_ssse3; + qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBX8888] = convert_RGB888_to_RGB32_ssse3; + qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888] = convert_RGB888_to_RGB32_ssse3; + qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB32_ssse3; } #endif diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index 9da6acd0a7..9e2d9c86bb 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -207,6 +207,7 @@ inline int qt_depthForFormat(QImage::Format format) case QImage::Format_ARGB8565_Premultiplied: case QImage::Format_ARGB8555_Premultiplied: case QImage::Format_RGB888: + case QImage::Format_BGR888: depth = 24; break; case QImage::Format_RGBX64: diff --git a/src/gui/image/qimage_ssse3.cpp b/src/gui/image/qimage_ssse3.cpp index 9cdfba20e3..fb81a1a6c3 100644 --- a/src/gui/image/qimage_ssse3.cpp +++ b/src/gui/image/qimage_ssse3.cpp @@ -121,8 +121,11 @@ Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, con void convert_RGB888_to_RGB32_ssse3(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { - Q_ASSERT(src->format == QImage::Format_RGB888); - Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied); + Q_ASSERT(src->format == QImage::Format_RGB888 || src->format == QImage::Format_BGR888); + if (src->format == QImage::Format_BGR888) + Q_ASSERT(dest->format == QImage::Format_RGBX8888 || dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied); + else + Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied); Q_ASSERT(src->width == dest->width); Q_ASSERT(src->height == dest->height); diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp index 3a2db74098..8aad77b991 100644 --- a/src/gui/image/qpixmap_win.cpp +++ b/src/gui/image/qpixmap_win.cpp @@ -281,6 +281,7 @@ Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &imageIn, int hbitmapForm } break; case QImage::Format_RGB888: + case QImage::Format_BGR888: compression = BI_RGB; bitCount = 24u; break; @@ -368,7 +369,7 @@ static QImage::Format imageFromWinHBITMAP_Format(const BITMAPINFOHEADER &header, ? QImage::Format_RGB32 : QImage::Format_ARGB32_Premultiplied; break; case 24: - result = QImage::Format_RGB888; + result = QImage::Format_BGR888; break; case 16: result = QImage::Format_RGB555; diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 16d6c25b8b..4ab45337b0 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -1079,6 +1079,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i if (color_type == PNG_COLOR_TYPE_RGB) { switch (image.format()) { case QImage::Format_RGB888: + case QImage::Format_BGR888: break; case QImage::Format_RGBX8888: case QImage::Format_RGBX64: @@ -1131,6 +1132,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i case QImage::Format_RGB32: case QImage::Format_ARGB32: case QImage::Format_RGB888: + case QImage::Format_BGR888: case QImage::Format_RGBX8888: case QImage::Format_RGBA8888: case QImage::Format_RGBX64: diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index ec52791010..616cca1422 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -535,13 +535,6 @@ Qt::MouseEventFlags QMouseEvent::flags() const */ /*! - \fn QPointF QMouseEvent::posF() const - \obsolete - - Use localPos() instead. -*/ - -/*! \class QHoverEvent \ingroup events \inmodule QtGui @@ -750,12 +743,14 @@ QHoverEvent::~QHoverEvent() \l inverted always returns false. */ +#if QT_DEPRECATED_SINCE(5, 15) /*! \fn Qt::Orientation QWheelEvent::orientation() const \obsolete Use angleDelta() instead. */ +#endif #if QT_CONFIG(wheelevent) #if QT_DEPRECATED_SINCE(5, 15) @@ -936,6 +931,7 @@ QWheelEvent::~QWheelEvent() \endlist */ +#if QT_DEPRECATED_SINCE(5, 15) /*! \fn int QWheelEvent::delta() const \obsolete @@ -999,6 +995,7 @@ QWheelEvent::~QWheelEvent() This function has been deprecated, use globalPosition() instead. */ +#endif /*! \fn Qt::ScrollPhase QWheelEvent::phase() const @@ -1102,16 +1099,6 @@ QKeyEvent::~QKeyEvent() } /*! - \fn QKeyEvent *QKeyEvent::createExtendedKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, const QString& text, bool autorep, ushort count) - \internal -*/ - -/*! - \fn bool QKeyEvent::hasExtendedInfo() const - \internal -*/ - -/*! \fn quint32 QKeyEvent::nativeScanCode() const \since 4.2 @@ -2539,7 +2526,7 @@ Qt::MouseButtons QTabletEvent::buttons() const globalPos() can differ significantly from the current position QCursor::pos(). - \sa globalX(), globalY(), hiResGlobalPos() + \sa globalX(), globalY() */ /*! @@ -2584,15 +2571,6 @@ Qt::MouseButtons QTabletEvent::buttons() const */ /*! - \fn const QPointF &QTabletEvent::hiResGlobalPos() const - - The high precision coordinates delivered from the tablet expressed. - Sub pixeling information is in the fractional part of the QPointF. - - \sa globalPos(), hiResGlobalX(), hiResGlobalY() -*/ - -/*! \fn qreal &QTabletEvent::hiResGlobalX() const The high precision x position of the tablet device. @@ -2674,10 +2652,10 @@ Qt::MouseButtons QTabletEvent::buttons() const \sa Qt::NativeGestureType, QGestureEvent */ +#if QT_DEPRECATED_SINCE(5, 10) /*! \deprecated The QTouchDevice parameter is now required */ -#if QT_DEPRECATED_SINCE(5, 10) QNativeGestureEvent::QNativeGestureEvent(Qt::NativeGestureType type, const QPointF &localPos, const QPointF &windowPos, const QPointF &screenPos, qreal realValue, ulong sequenceId, quint64 intValue) : QInputEvent(QEvent::NativeGesture), mGestureType(type), @@ -4290,18 +4268,6 @@ QWindowStateChangeEvent::~QWindowStateChangeEvent() QGraphicsItem::acceptTouchEvents() */ -/*! \enum QTouchEvent::DeviceType - \obsolete - - This enum represents the type of device that generated a QTouchEvent. - - This enum has been deprecated. Use QTouchDevice::DeviceType instead. - \omitvalue TouchPad - \omitvalue TouchScreen - - \sa QTouchDevice::DeviceType, QTouchDevice::type(), QTouchEvent::device() -*/ - /*! Constructs a QTouchEvent with the given \a eventType, \a device, and \a touchPoints. The \a touchPointStates and \a modifiers @@ -4341,16 +4307,6 @@ QTouchEvent::~QTouchEvent() This is typically a QWidget or a QQuickItem. May be 0 when no specific target is available. */ -/*! \fn QTouchEvent::DeviceType QTouchEvent::deviceType() const - \obsolete - - Returns the touch device Type, which is of type \l {QTouchEvent::DeviceType} {DeviceType}. - - This function has been deprecated. Use QTouchDevice::type() instead. - - \sa QTouchDevice::type(), QTouchEvent::device() -*/ - /*! \fn QTouchEvent::TouchPoint::TouchPoint(TouchPoint &&other) Move-constructs a TouchPoint instance, making it point to the same diff --git a/src/gui/opengl/qopengltextureuploader.cpp b/src/gui/opengl/qopengltextureuploader.cpp index d9d5403cf3..9e393bc47a 100644 --- a/src/gui/opengl/qopengltextureuploader.cpp +++ b/src/gui/opengl/qopengltextureuploader.cpp @@ -65,6 +65,10 @@ #define GL_RGBA16 0x805B #endif +#ifndef GL_BGR +#define GL_BGR 0x80E0 +#endif + #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif @@ -202,6 +206,20 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag pixelType = GL_UNSIGNED_BYTE; targetFormat = QImage::Format_RGB888; break; + case QImage::Format_BGR888: + if (isOpenGL12orBetter) { + externalFormat = GL_BGR; + internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = QImage::Format_BGR888; + } else if (funcs->hasOpenGLExtension(QOpenGLExtensions::TextureSwizzle)) { + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, GL_RED); + funcs->glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + externalFormat = internalFormat = GL_RGB; + pixelType = GL_UNSIGNED_BYTE; + targetFormat = QImage::Format_BGR888; + } + break; case QImage::Format_RGBX8888: case QImage::Format_RGBA8888: case QImage::Format_RGBA8888_Premultiplied: diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri index 972cf387ff..fcf6488edd 100644 --- a/src/gui/painting/painting.pri +++ b/src/gui/painting/painting.pri @@ -135,23 +135,41 @@ gcc:equals(QT_GCC_MAJOR_VERSION, 5) { NO_PCH_SOURCES += painting/qdrawhelper.cpp } -SSE2_SOURCES += painting/qdrawhelper_sse2.cpp -SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp -SSE4_1_SOURCES += painting/qdrawhelper_sse4.cpp \ - painting/qimagescale_sse4.cpp -ARCH_HASWELL_SOURCES += painting/qdrawhelper_avx2.cpp +!android { + SSE2_SOURCES += painting/qdrawhelper_sse2.cpp + SSSE3_SOURCES += painting/qdrawhelper_ssse3.cpp + SSE4_1_SOURCES += painting/qdrawhelper_sse4.cpp \ + painting/qimagescale_sse4.cpp + ARCH_HASWELL_SOURCES += painting/qdrawhelper_avx2.cpp -NEON_SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp -NEON_HEADERS += painting/qdrawhelper_neon_p.h + NEON_SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp + NEON_HEADERS += painting/qdrawhelper_neon_p.h +} !uikit:!win32:contains(QT_ARCH, "arm"): CONFIG += no_clang_integrated_as -!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") { +!android:!uikit:!win32:!integrity:!contains(QT_ARCH, "arm64") { NEON_ASM += ../3rdparty/pixman/pixman-arm-neon-asm.S painting/qdrawhelper_neon_asm.S DEFINES += ENABLE_PIXMAN_DRAWHELPERS } -MIPS_DSP_SOURCES += painting/qdrawhelper_mips_dsp.cpp -MIPS_DSP_HEADERS += painting/qdrawhelper_mips_dsp_p.h painting/qt_mips_asm_dsp_p.h -MIPS_DSP_ASM += painting/qdrawhelper_mips_dsp_asm.S -MIPS_DSPR2_ASM += painting/qdrawhelper_mips_dspr2_asm.S +!android { + MIPS_DSP_SOURCES += painting/qdrawhelper_mips_dsp.cpp + MIPS_DSP_HEADERS += painting/qdrawhelper_mips_dsp_p.h painting/qt_mips_asm_dsp_p.h + MIPS_DSP_ASM += painting/qdrawhelper_mips_dsp_asm.S + MIPS_DSPR2_ASM += painting/qdrawhelper_mips_dspr2_asm.S +} else { + # see https://developer.android.com/ndk/guides/abis + x86 | x86_64 { + DEFINES += QT_COMPILER_SUPPORTS_SSE2 QT_COMPILER_SUPPORTS_SSE3 QT_COMPILER_SUPPORTS_SSSE3 + SOURCES += painting/qdrawhelper_sse2.cpp painting/qdrawhelper_ssse3.cpp + } + x86_64 { + DEFINES += QT_COMPILER_SUPPORTS_SSE4_1 QT_COMPILER_SUPPORTS_SSE4_2 + SOURCES += painting/qdrawhelper_sse4.cpp painting/qimagescale_sse4.cpp + } + arm64-v8a { + SOURCES += painting/qdrawhelper_neon.cpp painting/qimagescale_neon.cpp + HEADERS += painting/qdrawhelper_neon_p.h + } +} include($$PWD/../../3rdparty/zlib_dependency.pri) diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp index daf19fffe1..9861fffff3 100644 --- a/src/gui/painting/qbezier.cpp +++ b/src/gui/painting/qbezier.cpp @@ -47,6 +47,8 @@ #include <private/qnumeric_p.h> +#include <tuple> // for std::tie() + QT_BEGIN_NAMESPACE //#define QDEBUG_BEZIER @@ -128,7 +130,7 @@ void QBezier::addToPolygon(QPolygonF *polygon, qreal bezier_flattening_threshold --lvl; } else { // split, second half of the polygon goes lower into the stack - b->split(b+1, b); + std::tie(b[1], b[0]) = b->split(); lvl[1] = --lvl[0]; ++b; ++lvl; @@ -166,7 +168,7 @@ void QBezier::addToPolygon(QDataBuffer<QPointF> &polygon, qreal bezier_flattenin --lvl; } else { // split, second half of the polygon goes lower into the stack - b->split(b+1, b); + std::tie(b[1], b[0]) = b->split(); lvl[1] = --lvl[0]; ++b; ++lvl; @@ -422,7 +424,7 @@ redo: o += 2; --b; } else { - b->split(b+1, b); + std::tie(b[1], b[0]) = b->split(); ++b; } } @@ -464,8 +466,6 @@ qreal QBezier::length(qreal error) const void QBezier::addIfClose(qreal *length, qreal error) const { - QBezier left, right; /* bez poly splits */ - qreal len = qreal(0.0); /* arc length */ qreal chord; /* chord length */ @@ -476,9 +476,9 @@ void QBezier::addIfClose(qreal *length, qreal error) const chord = QLineF(QPointF(x1, y1),QPointF(x4, y4)).length(); if((len-chord) > error) { - split(&left, &right); /* split in two */ - left.addIfClose(length, error); /* try left side */ - right.addIfClose(length, error); /* try right side */ + const auto halves = split(); /* split in two */ + halves.first.addIfClose(length, error); /* try left side */ + halves.second.addIfClose(length, error); /* try right side */ return; } diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h index f88e3b35b3..1c49f82416 100644 --- a/src/gui/painting/qbezier_p.h +++ b/src/gui/painting/qbezier_p.h @@ -107,7 +107,7 @@ public: inline QLineF endTangent() const; inline void parameterSplitLeft(qreal t, QBezier *left); - inline void split(QBezier *firstHalf, QBezier *secondHalf) const; + inline std::pair<QBezier, QBezier> split() const; int shifted(QBezier *curveSegments, int maxSegmets, qreal offset, float threshold) const; @@ -223,28 +223,21 @@ inline QPointF QBezier::secondDerivedAt(qreal t) const a * y1 + b * y2 + c * y3 + d * y4); } -inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const +std::pair<QBezier, QBezier> QBezier::split() const { - Q_ASSERT(firstHalf); - Q_ASSERT(secondHalf); - - qreal c = (x2 + x3)*.5; - firstHalf->x2 = (x1 + x2)*.5; - secondHalf->x3 = (x3 + x4)*.5; - firstHalf->x1 = x1; - secondHalf->x4 = x4; - firstHalf->x3 = (firstHalf->x2 + c)*.5; - secondHalf->x2 = (secondHalf->x3 + c)*.5; - firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5; - - c = (y2 + y3)/2; - firstHalf->y2 = (y1 + y2)*.5; - secondHalf->y3 = (y3 + y4)*.5; - firstHalf->y1 = y1; - secondHalf->y4 = y4; - firstHalf->y3 = (firstHalf->y2 + c)*.5; - secondHalf->y2 = (secondHalf->y3 + c)*.5; - firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5; + const auto mid = [](QPointF lhs, QPointF rhs) { return (lhs + rhs) * .5; }; + + const QPointF mid_12 = mid(pt1(), pt2()); + const QPointF mid_23 = mid(pt2(), pt3()); + const QPointF mid_34 = mid(pt3(), pt4()); + const QPointF mid_12_23 = mid(mid_12, mid_23); + const QPointF mid_23_34 = mid(mid_23, mid_34); + const QPointF mid_12_23__23_34 = mid(mid_12_23, mid_23_34); + + return { + fromPoints(pt1(), mid_12, mid_12_23, mid_12_23__23_34), + fromPoints(mid_12_23__23_34, mid_23_34, mid_34, pt4()), + }; } inline void QBezier::parameterSplitLeft(qreal t, QBezier *left) diff --git a/src/gui/painting/qcolor.cpp b/src/gui/painting/qcolor.cpp index 6cbc30e79a..8780cce223 100644 --- a/src/gui/painting/qcolor.cpp +++ b/src/gui/painting/qcolor.cpp @@ -147,6 +147,7 @@ static bool get_hex_rgb(const QChar *str, size_t len, QRgba64 *rgb) #endif #define rgb(r,g,b) (0xff000000 | (r << 16) | (g << 8) | b) +// keep this is in sync with QColorConstants static const struct RGBData { const char name[21]; uint value; @@ -475,25 +476,35 @@ static QStringList get_colornames() \section1 Predefined Colors - There are 20 predefined QColors described by the Qt::GlobalColor enum, - including black, white, primary and secondary colors, darker versions - of these colors and three shades of gray. QColor also recognizes a - variety of color names; the static colorNames() function returns a - QStringList color names that QColor knows about. + There are 20 predefined QColor objects in the \c{QColorConstants} + namespace, including black, white, primary and secondary colors, + darker versions of these colors, and three shades of gray. + Furthermore, the \c{QColorConstants::Svg} namespace defines QColor + objects for the standard \l{https://www.w3.org/TR/SVG11/types.html#ColorKeywords}{SVG color keyword names}. \image qt-colors.png Qt Colors - Additionally, the Qt::color0, Qt::color1 and Qt::transparent colors - are used for special purposes. + The \c{QColorConstants::Color0}, \c{QColorConstants::Color1} and + \c{QColorConstants::Transparent} colors are used for special + purposes. - Qt::color0 (zero pixel value) and Qt::color1 (non-zero pixel value) - are special colors for drawing in QBitmaps. Painting with Qt::color0 - sets the bitmap bits to 0 (transparent; i.e., background), and painting - with Qt::color1 sets the bits to 1 (opaque; i.e., foreground). + \c{QColorConstants::Color0} (zero pixel value) and + \c{QColorConstants::Color1} (non-zero pixel value) are special + colors for drawing in QBitmaps. Painting with + \c{QColorConstants::Color0} sets the bitmap bits to 0 (transparent; + i.e., background), and painting with c{QColorConstants::Color1} + sets the bits to 1 (opaque; i.e., foreground). - Qt::transparent is used to indicate a transparent pixel. When painting - with this value, a pixel value will be used that is appropriate for the - underlying pixel format in use. + \c{QColorConstants::Transparent} is used to indicate a transparent + pixel. When painting with this value, a pixel value will be used + that is appropriate for the underlying pixel format in use. + + For historical reasons, the 20 predefined colors are also available + in the Qt::GlobalColor enumeration. + + Finally, QColor recognizes a variety of color names (as strings); + the static colorNames() function returns a QStringList color names + that QColor knows about. \section1 The Extended RGB Color Model @@ -586,7 +597,7 @@ static QStringList get_colornames() alpha-channel to feature \l {QColor#Alpha-Blended Drawing}{alpha-blended drawing}. - \sa QPalette, QBrush + \sa QPalette, QBrush, QColorConstants */ #define QCOLOR_INT_RANGE_CHECK(fn, var) \ @@ -886,7 +897,8 @@ QString QColor::name(NameFormat format) const \li #AARRGGBB (Since 5.2) \li #RRRGGGBBB \li #RRRRGGGGBBBB - \li A name from the list of colors defined in the list of \l{http://www.w3.org/TR/SVG/types.html#ColorKeywords}{SVG color keyword names} + \li A name from the list of colors defined in the list of + \l{https://www.w3.org/TR/SVG11/types.html#ColorKeywords}{SVG color keyword names} provided by the World Wide Web Consortium; for example, "steelblue" or "gainsboro". These color names work on all platforms. Note that these color names are \e not the same as defined by the Qt::GlobalColor enums, e.g. "green" and Qt::green does not @@ -3249,4 +3261,41 @@ const uint qt_inv_premul_factor[256] = { \sa QColor::rgb(), QColor::rgba() */ +/*! + \namespace QColorConstants + \inmodule QtGui + + \brief The QColorConstants namespace contains QColor predefined constants. + + These constants are usable everywhere a QColor object is expected: + + \code + painter.setBrush(QColorConstants::Svg::lightblue); + \endcode + + Their usage is much cheaper than e.g. passing a string to QColor's constructor, + as they don't require any parsing of the string, and always result in a valid + QColor object: + + \badcode + object.setColor(QColor("lightblue")); // expensive + \endcode + + \section1 Qt Colors + + The following colors are defined in the \c{QColorConstants} namespace: + + \include qt-colors.qdocinc + + \section1 SVG Colors + + The following table lists the available + \l {http://www.w3.org/TR/SVG/types.html#ColorKeywords}{SVG colors}. + They are available in the \c{QColorConstants::Svg} inner namespace. + + \include svg-colors.qdocinc + + \sa QColor, Qt::GlobalColor +*/ + QT_END_NAMESPACE diff --git a/src/gui/painting/qcolor.h b/src/gui/painting/qcolor.h index 723b9fce73..f0d7dd23ad 100644 --- a/src/gui/painting/qcolor.h +++ b/src/gui/painting/qcolor.h @@ -309,6 +309,12 @@ private: friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColor &); friend Q_GUI_EXPORT QDataStream &operator>>(QDataStream &, QColor &); #endif + +#ifdef Q_COMPILER_UNIFORM_INIT +public: // can't give friendship to a namespace, so it needs to be public + Q_DECL_CONSTEXPR explicit QColor(Spec spec, ushort a1, ushort a2, ushort a3, ushort a4, ushort a5=0) noexcept + : cspec(spec), ct(a1, a2, a3, a4, a5) {} +#endif // Q_COMPILER_UNIFORM_INIT }; Q_DECLARE_TYPEINFO(QColor, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE); @@ -326,6 +332,187 @@ inline QColor::QColor(const QString& aname) inline bool QColor::isValid() const noexcept { return cspec != Invalid; } +// define these namespaces even if the contents are ifdef'd out +namespace QColorConstants +{ +namespace Svg {} + +#if defined(Q_COMPILER_CONSTEXPR) & defined(Q_COMPILER_UNIFORM_INIT) + // Qt::GlobalColor names + constexpr Q_DECL_UNUSED QColor Color0 {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor Color1 {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor Black {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor White {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkGray {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor Gray {QColor::Rgb, 0xff * 0x101, 0xa0 * 0x101, 0xa0 * 0x101, 0xa4 * 0x101}; + constexpr Q_DECL_UNUSED QColor LightGray {QColor::Rgb, 0xff * 0x101, 0xc0 * 0x101, 0xc0 * 0x101, 0xc0 * 0x101}; + constexpr Q_DECL_UNUSED QColor Red {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor Green {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor Blue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor Cyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor Magenta {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor Yellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkRed {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkGreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkBlue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkCyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkMagenta {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor DarkYellow {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor Transparent {QColor::Rgb, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + + // SVG names supported by QColor (see qcolor.cpp). +namespace Svg { + constexpr Q_DECL_UNUSED QColor aliceblue {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xf8 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor antiquewhite {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xeb * 0x101, 0xd7 * 0x101}; + constexpr Q_DECL_UNUSED QColor aqua {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor aquamarine {QColor::Rgb, 0xff * 0x101, 0x7f * 0x101, 0xff * 0x101, 0xd4 * 0x101}; + constexpr Q_DECL_UNUSED QColor azure {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor beige {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xf5 * 0x101, 0xdc * 0x101}; + constexpr Q_DECL_UNUSED QColor bisque {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xc4 * 0x101}; + constexpr Q_DECL_UNUSED QColor black {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor blanchedalmond {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xeb * 0x101, 0xcd * 0x101}; + constexpr Q_DECL_UNUSED QColor blue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor blueviolet {QColor::Rgb, 0xff * 0x101, 0x8a * 0x101, 0x2b * 0x101, 0xe2 * 0x101}; + constexpr Q_DECL_UNUSED QColor brown {QColor::Rgb, 0xff * 0x101, 0xa5 * 0x101, 0x2a * 0x101, 0x2a * 0x101}; + constexpr Q_DECL_UNUSED QColor burlywood {QColor::Rgb, 0xff * 0x101, 0xde * 0x101, 0xb8 * 0x101, 0x87 * 0x101}; + constexpr Q_DECL_UNUSED QColor cadetblue {QColor::Rgb, 0xff * 0x101, 0x5f * 0x101, 0x9e * 0x101, 0xa0 * 0x101}; + constexpr Q_DECL_UNUSED QColor chartreuse {QColor::Rgb, 0xff * 0x101, 0x7f * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor chocolate {QColor::Rgb, 0xff * 0x101, 0xd2 * 0x101, 0x69 * 0x101, 0x1e * 0x101}; + constexpr Q_DECL_UNUSED QColor coral {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x7f * 0x101, 0x50 * 0x101}; + constexpr Q_DECL_UNUSED QColor cornflowerblue {QColor::Rgb, 0xff * 0x101, 0x64 * 0x101, 0x95 * 0x101, 0xed * 0x101}; + constexpr Q_DECL_UNUSED QColor cornsilk {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf8 * 0x101, 0xdc * 0x101}; + constexpr Q_DECL_UNUSED QColor crimson {QColor::Rgb, 0xff * 0x101, 0xdc * 0x101, 0x14 * 0x101, 0x3c * 0x101}; + constexpr Q_DECL_UNUSED QColor cyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor darkblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x8b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkcyan {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x8b * 0x101, 0x8b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkgoldenrod {QColor::Rgb, 0xff * 0x101, 0xb8 * 0x101, 0x86 * 0x101, 0x0b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkgray {QColor::Rgb, 0xff * 0x101, 0xa9 * 0x101, 0xa9 * 0x101, 0xa9 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x64 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkgrey {QColor::Rgb, 0xff * 0x101, 0xa9 * 0x101, 0xa9 * 0x101, 0xa9 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkkhaki {QColor::Rgb, 0xff * 0x101, 0xbd * 0x101, 0xb7 * 0x101, 0x6b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkmagenta {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x00 * 0x101, 0x8b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkolivegreen {QColor::Rgb, 0xff * 0x101, 0x55 * 0x101, 0x6b * 0x101, 0x2f * 0x101}; + constexpr Q_DECL_UNUSED QColor darkorange {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x8c * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkorchid {QColor::Rgb, 0xff * 0x101, 0x99 * 0x101, 0x32 * 0x101, 0xcc * 0x101}; + constexpr Q_DECL_UNUSED QColor darkred {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor darksalmon {QColor::Rgb, 0xff * 0x101, 0xe9 * 0x101, 0x96 * 0x101, 0x7a * 0x101}; + constexpr Q_DECL_UNUSED QColor darkseagreen {QColor::Rgb, 0xff * 0x101, 0x8f * 0x101, 0xbc * 0x101, 0x8f * 0x101}; + constexpr Q_DECL_UNUSED QColor darkslateblue {QColor::Rgb, 0xff * 0x101, 0x48 * 0x101, 0x3d * 0x101, 0x8b * 0x101}; + constexpr Q_DECL_UNUSED QColor darkslategray {QColor::Rgb, 0xff * 0x101, 0x2f * 0x101, 0x4f * 0x101, 0x4f * 0x101}; + constexpr Q_DECL_UNUSED QColor darkslategrey {QColor::Rgb, 0xff * 0x101, 0x2f * 0x101, 0x4f * 0x101, 0x4f * 0x101}; + constexpr Q_DECL_UNUSED QColor darkturquoise {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xce * 0x101, 0xd1 * 0x101}; + constexpr Q_DECL_UNUSED QColor darkviolet {QColor::Rgb, 0xff * 0x101, 0x94 * 0x101, 0x00 * 0x101, 0xd3 * 0x101}; + constexpr Q_DECL_UNUSED QColor deeppink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x14 * 0x101, 0x93 * 0x101}; + constexpr Q_DECL_UNUSED QColor deepskyblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xbf * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor dimgray {QColor::Rgb, 0xff * 0x101, 0x69 * 0x101, 0x69 * 0x101, 0x69 * 0x101}; + constexpr Q_DECL_UNUSED QColor dimgrey {QColor::Rgb, 0xff * 0x101, 0x69 * 0x101, 0x69 * 0x101, 0x69 * 0x101}; + constexpr Q_DECL_UNUSED QColor dodgerblue {QColor::Rgb, 0xff * 0x101, 0x1e * 0x101, 0x90 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor firebrick {QColor::Rgb, 0xff * 0x101, 0xb2 * 0x101, 0x22 * 0x101, 0x22 * 0x101}; + constexpr Q_DECL_UNUSED QColor floralwhite {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xf0 * 0x101}; + constexpr Q_DECL_UNUSED QColor forestgreen {QColor::Rgb, 0xff * 0x101, 0x22 * 0x101, 0x8b * 0x101, 0x22 * 0x101}; + constexpr Q_DECL_UNUSED QColor fuchsia {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor gainsboro {QColor::Rgb, 0xff * 0x101, 0xdc * 0x101, 0xdc * 0x101, 0xdc * 0x101}; + constexpr Q_DECL_UNUSED QColor ghostwhite {QColor::Rgb, 0xff * 0x101, 0xf8 * 0x101, 0xf8 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor gold {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xd7 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor goldenrod {QColor::Rgb, 0xff * 0x101, 0xda * 0x101, 0xa5 * 0x101, 0x20 * 0x101}; + constexpr Q_DECL_UNUSED QColor gray {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor green {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor greenyellow {QColor::Rgb, 0xff * 0x101, 0xad * 0x101, 0xff * 0x101, 0x2f * 0x101}; + constexpr Q_DECL_UNUSED QColor grey {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor honeydew {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xff * 0x101, 0xf0 * 0x101}; + constexpr Q_DECL_UNUSED QColor hotpink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x69 * 0x101, 0xb4 * 0x101}; + constexpr Q_DECL_UNUSED QColor indianred {QColor::Rgb, 0xff * 0x101, 0xcd * 0x101, 0x5c * 0x101, 0x5c * 0x101}; + constexpr Q_DECL_UNUSED QColor indigo {QColor::Rgb, 0xff * 0x101, 0x4b * 0x101, 0x00 * 0x101, 0x82 * 0x101}; + constexpr Q_DECL_UNUSED QColor ivory {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xf0 * 0x101}; + constexpr Q_DECL_UNUSED QColor khaki {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0xe6 * 0x101, 0x8c * 0x101}; + constexpr Q_DECL_UNUSED QColor lavender {QColor::Rgb, 0xff * 0x101, 0xe6 * 0x101, 0xe6 * 0x101, 0xfa * 0x101}; + constexpr Q_DECL_UNUSED QColor lavenderblush {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf0 * 0x101, 0xf5 * 0x101}; + constexpr Q_DECL_UNUSED QColor lawngreen {QColor::Rgb, 0xff * 0x101, 0x7c * 0x101, 0xfc * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor lemonchiffon {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xcd * 0x101}; + constexpr Q_DECL_UNUSED QColor lightblue {QColor::Rgb, 0xff * 0x101, 0xad * 0x101, 0xd8 * 0x101, 0xe6 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightcoral {QColor::Rgb, 0xff * 0x101, 0xf0 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightcyan {QColor::Rgb, 0xff * 0x101, 0xe0 * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor lightgoldenrodyellow {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xfa * 0x101, 0xd2 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightgray {QColor::Rgb, 0xff * 0x101, 0xd3 * 0x101, 0xd3 * 0x101, 0xd3 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightgreen {QColor::Rgb, 0xff * 0x101, 0x90 * 0x101, 0xee * 0x101, 0x90 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightgrey {QColor::Rgb, 0xff * 0x101, 0xd3 * 0x101, 0xd3 * 0x101, 0xd3 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightpink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xb6 * 0x101, 0xc1 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightsalmon {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xa0 * 0x101, 0x7a * 0x101}; + constexpr Q_DECL_UNUSED QColor lightseagreen {QColor::Rgb, 0xff * 0x101, 0x20 * 0x101, 0xb2 * 0x101, 0xaa * 0x101}; + constexpr Q_DECL_UNUSED QColor lightskyblue {QColor::Rgb, 0xff * 0x101, 0x87 * 0x101, 0xce * 0x101, 0xfa * 0x101}; + constexpr Q_DECL_UNUSED QColor lightslategray {QColor::Rgb, 0xff * 0x101, 0x77 * 0x101, 0x88 * 0x101, 0x99 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightslategrey {QColor::Rgb, 0xff * 0x101, 0x77 * 0x101, 0x88 * 0x101, 0x99 * 0x101}; + constexpr Q_DECL_UNUSED QColor lightsteelblue {QColor::Rgb, 0xff * 0x101, 0xb0 * 0x101, 0xc4 * 0x101, 0xde * 0x101}; + constexpr Q_DECL_UNUSED QColor lightyellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xe0 * 0x101}; + constexpr Q_DECL_UNUSED QColor lime {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor limegreen {QColor::Rgb, 0xff * 0x101, 0x32 * 0x101, 0xcd * 0x101, 0x32 * 0x101}; + constexpr Q_DECL_UNUSED QColor linen {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0xf0 * 0x101, 0xe6 * 0x101}; + constexpr Q_DECL_UNUSED QColor magenta {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor maroon {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumaquamarine {QColor::Rgb, 0xff * 0x101, 0x66 * 0x101, 0xcd * 0x101, 0xaa * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumblue {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0xcd * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumorchid {QColor::Rgb, 0xff * 0x101, 0xba * 0x101, 0x55 * 0x101, 0xd3 * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumpurple {QColor::Rgb, 0xff * 0x101, 0x93 * 0x101, 0x70 * 0x101, 0xdb * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumseagreen {QColor::Rgb, 0xff * 0x101, 0x3c * 0x101, 0xb3 * 0x101, 0x71 * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumslateblue {QColor::Rgb, 0xff * 0x101, 0x7b * 0x101, 0x68 * 0x101, 0xee * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumspringgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xfa * 0x101, 0x9a * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumturquoise {QColor::Rgb, 0xff * 0x101, 0x48 * 0x101, 0xd1 * 0x101, 0xcc * 0x101}; + constexpr Q_DECL_UNUSED QColor mediumvioletred {QColor::Rgb, 0xff * 0x101, 0xc7 * 0x101, 0x15 * 0x101, 0x85 * 0x101}; + constexpr Q_DECL_UNUSED QColor midnightblue {QColor::Rgb, 0xff * 0x101, 0x19 * 0x101, 0x19 * 0x101, 0x70 * 0x101}; + constexpr Q_DECL_UNUSED QColor mintcream {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xff * 0x101, 0xfa * 0x101}; + constexpr Q_DECL_UNUSED QColor mistyrose {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xe1 * 0x101}; + constexpr Q_DECL_UNUSED QColor moccasin {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xe4 * 0x101, 0xb5 * 0x101}; + constexpr Q_DECL_UNUSED QColor navajowhite {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xde * 0x101, 0xad * 0x101}; + constexpr Q_DECL_UNUSED QColor navy {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor oldlace {QColor::Rgb, 0xff * 0x101, 0xfd * 0x101, 0xf5 * 0x101, 0xe6 * 0x101}; + constexpr Q_DECL_UNUSED QColor olive {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x80 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor olivedrab {QColor::Rgb, 0xff * 0x101, 0x6b * 0x101, 0x8e * 0x101, 0x23 * 0x101}; + constexpr Q_DECL_UNUSED QColor orange {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xa5 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor orangered {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x45 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor orchid {QColor::Rgb, 0xff * 0x101, 0xda * 0x101, 0x70 * 0x101, 0xd6 * 0x101}; + constexpr Q_DECL_UNUSED QColor palegoldenrod {QColor::Rgb, 0xff * 0x101, 0xee * 0x101, 0xe8 * 0x101, 0xaa * 0x101}; + constexpr Q_DECL_UNUSED QColor palegreen {QColor::Rgb, 0xff * 0x101, 0x98 * 0x101, 0xfb * 0x101, 0x98 * 0x101}; + constexpr Q_DECL_UNUSED QColor paleturquoise {QColor::Rgb, 0xff * 0x101, 0xaf * 0x101, 0xee * 0x101, 0xee * 0x101}; + constexpr Q_DECL_UNUSED QColor palevioletred {QColor::Rgb, 0xff * 0x101, 0xdb * 0x101, 0x70 * 0x101, 0x93 * 0x101}; + constexpr Q_DECL_UNUSED QColor papayawhip {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xef * 0x101, 0xd5 * 0x101}; + constexpr Q_DECL_UNUSED QColor peachpuff {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xda * 0x101, 0xb9 * 0x101}; + constexpr Q_DECL_UNUSED QColor peru {QColor::Rgb, 0xff * 0x101, 0xcd * 0x101, 0x85 * 0x101, 0x3f * 0x101}; + constexpr Q_DECL_UNUSED QColor pink {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xc0 * 0x101, 0xcb * 0x101}; + constexpr Q_DECL_UNUSED QColor plum {QColor::Rgb, 0xff * 0x101, 0xdd * 0x101, 0xa0 * 0x101, 0xdd * 0x101}; + constexpr Q_DECL_UNUSED QColor powderblue {QColor::Rgb, 0xff * 0x101, 0xb0 * 0x101, 0xe0 * 0x101, 0xe6 * 0x101}; + constexpr Q_DECL_UNUSED QColor purple {QColor::Rgb, 0xff * 0x101, 0x80 * 0x101, 0x00 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor red {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor rosybrown {QColor::Rgb, 0xff * 0x101, 0xbc * 0x101, 0x8f * 0x101, 0x8f * 0x101}; + constexpr Q_DECL_UNUSED QColor royalblue {QColor::Rgb, 0xff * 0x101, 0x41 * 0x101, 0x69 * 0x101, 0xe1 * 0x101}; + constexpr Q_DECL_UNUSED QColor saddlebrown {QColor::Rgb, 0xff * 0x101, 0x8b * 0x101, 0x45 * 0x101, 0x13 * 0x101}; + constexpr Q_DECL_UNUSED QColor salmon {QColor::Rgb, 0xff * 0x101, 0xfa * 0x101, 0x80 * 0x101, 0x72 * 0x101}; + constexpr Q_DECL_UNUSED QColor sandybrown {QColor::Rgb, 0xff * 0x101, 0xf4 * 0x101, 0xa4 * 0x101, 0x60 * 0x101}; + constexpr Q_DECL_UNUSED QColor seagreen {QColor::Rgb, 0xff * 0x101, 0x2e * 0x101, 0x8b * 0x101, 0x57 * 0x101}; + constexpr Q_DECL_UNUSED QColor seashell {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xf5 * 0x101, 0xee * 0x101}; + constexpr Q_DECL_UNUSED QColor sienna {QColor::Rgb, 0xff * 0x101, 0xa0 * 0x101, 0x52 * 0x101, 0x2d * 0x101}; + constexpr Q_DECL_UNUSED QColor silver {QColor::Rgb, 0xff * 0x101, 0xc0 * 0x101, 0xc0 * 0x101, 0xc0 * 0x101}; + constexpr Q_DECL_UNUSED QColor skyblue {QColor::Rgb, 0xff * 0x101, 0x87 * 0x101, 0xce * 0x101, 0xeb * 0x101}; + constexpr Q_DECL_UNUSED QColor slateblue {QColor::Rgb, 0xff * 0x101, 0x6a * 0x101, 0x5a * 0x101, 0xcd * 0x101}; + constexpr Q_DECL_UNUSED QColor slategray {QColor::Rgb, 0xff * 0x101, 0x70 * 0x101, 0x80 * 0x101, 0x90 * 0x101}; + constexpr Q_DECL_UNUSED QColor slategrey {QColor::Rgb, 0xff * 0x101, 0x70 * 0x101, 0x80 * 0x101, 0x90 * 0x101}; + constexpr Q_DECL_UNUSED QColor snow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xfa * 0x101, 0xfa * 0x101}; + constexpr Q_DECL_UNUSED QColor springgreen {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0xff * 0x101, 0x7f * 0x101}; + constexpr Q_DECL_UNUSED QColor steelblue {QColor::Rgb, 0xff * 0x101, 0x46 * 0x101, 0x82 * 0x101, 0xb4 * 0x101}; + constexpr Q_DECL_UNUSED QColor tan {QColor::Rgb, 0xff * 0x101, 0xd2 * 0x101, 0xb4 * 0x101, 0x8c * 0x101}; + constexpr Q_DECL_UNUSED QColor teal {QColor::Rgb, 0xff * 0x101, 0x00 * 0x101, 0x80 * 0x101, 0x80 * 0x101}; + constexpr Q_DECL_UNUSED QColor thistle {QColor::Rgb, 0xff * 0x101, 0xd8 * 0x101, 0xbf * 0x101, 0xd8 * 0x101}; + constexpr Q_DECL_UNUSED QColor tomato {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0x63 * 0x101, 0x47 * 0x101}; + constexpr Q_DECL_UNUSED QColor turquoise {QColor::Rgb, 0xff * 0x101, 0x40 * 0x101, 0xe0 * 0x101, 0xd0 * 0x101}; + constexpr Q_DECL_UNUSED QColor violet {QColor::Rgb, 0xff * 0x101, 0xee * 0x101, 0x82 * 0x101, 0xee * 0x101}; + constexpr Q_DECL_UNUSED QColor wheat {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xde * 0x101, 0xb3 * 0x101}; + constexpr Q_DECL_UNUSED QColor white {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101}; + constexpr Q_DECL_UNUSED QColor whitesmoke {QColor::Rgb, 0xff * 0x101, 0xf5 * 0x101, 0xf5 * 0x101, 0xf5 * 0x101}; + constexpr Q_DECL_UNUSED QColor yellow {QColor::Rgb, 0xff * 0x101, 0xff * 0x101, 0xff * 0x101, 0x00 * 0x101}; + constexpr Q_DECL_UNUSED QColor yellowgreen {QColor::Rgb, 0xff * 0x101, 0x9a * 0x101, 0xcd * 0x101, 0x32 * 0x101}; +} // namespace Svg +#endif // Q_COMPILER_CONSTEXPR && Q_COMPILER_UNIFORM_INIT +} // namespace QColorLiterals + QT_END_NAMESPACE #endif // QCOLOR_H diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 55d69221f5..c17bf2ddfd 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -88,6 +88,7 @@ template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB444>() { return 4; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB555>() { return 5; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB666>() { return 6; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_BGR888>() { return 8; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; } @@ -101,6 +102,7 @@ template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB444>() { return 8; template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB555>() { return 10; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB666>() { return 12; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB888>() { return 16; } +template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_BGR888>() { return 0; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB4444_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8555_Premultiplied>() { return 18; } template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8565_Premultiplied>() { return 19; } @@ -119,6 +121,7 @@ template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB444>() { return 4; template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB555>() { return 5; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB666>() { return 6; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_BGR888>() { return 8; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8565_Premultiplied>() { return 6; } @@ -132,6 +135,7 @@ template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB444>() { return 4; template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB555>() { return 5; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB666>() { return 6; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_BGR888>() { return 8; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8555_Premultiplied>() { return 13; } template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8565_Premultiplied>() { return 13; } @@ -150,6 +154,7 @@ template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB444>() { return 4; template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB555>() { return 5; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB666>() { return 6; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB888>() { return 8; } +template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_BGR888>() { return 8; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; } template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; } @@ -163,6 +168,7 @@ template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB444>() { return 0; template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB555>() { return 0; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB666>() { return 0; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_BGR888>() { return 16; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB4444_Premultiplied>() { return 0; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8555_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8565_Premultiplied>() { return 8; } @@ -181,6 +187,7 @@ template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB444>() { return 0; template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB555>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB666>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_RGB888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_BGR888>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8555_Premultiplied>() { return 8; } template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8565_Premultiplied>() { return 8; } @@ -194,6 +201,7 @@ template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB444>() { return 0; template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB555>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB666>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_RGB888>() { return 0; } +template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_BGR888>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB4444_Premultiplied>() { return 12; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8555_Premultiplied>() { return 0; } template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8565_Premultiplied>() { return 0; } @@ -214,6 +222,7 @@ template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { r template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB555>() { return QPixelLayout::BPP16; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB666>() { return QPixelLayout::BPP24; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB888>() { return QPixelLayout::BPP24; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_BGR888>() { return QPixelLayout::BPP24; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB4444_Premultiplied>() { return QPixelLayout::BPP16; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8555_Premultiplied>() { return QPixelLayout::BPP24; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8565_Premultiplied>() { return QPixelLayout::BPP24; } @@ -1528,7 +1537,8 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = { { false, false, QPixelLayout::BPP16, nullptr, convertGrayscale16ToRGB32, convertGrayscale16ToRGBA64, fetchGrayscale16ToRGB32, fetchGrayscale16ToRGBA64, - storeGrayscale16FromARGB32PM, storeGrayscale16FromRGB32 } // Format_Grayscale16 + storeGrayscale16FromARGB32PM, storeGrayscale16FromRGB32 }, // Format_Grayscale16 + pixelLayoutRGB<QImage::Format_BGR888>(), }; Q_STATIC_ASSERT(sizeof(qPixelLayouts) / sizeof(*qPixelLayouts) == QImage::NImageFormats); @@ -1643,7 +1653,8 @@ ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = { storeRGBX64FromRGBA64PM, storeRGBA64FromRGBA64PM, storeRGBA64PMFromRGBA64PM, - storeGray16FromRGBA64PM + storeGray16FromRGBA64PM, + storeGenericFromRGBA64PM<QImage::Format_BGR888>, }; /* @@ -1732,6 +1743,7 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] = destFetch, // Format_RGBA64 destFetch, // Format_RGBA64_Premultiplied destFetch, // Format_Grayscale16 + destFetch, // Format_BGR888 }; #if QT_CONFIG(raster_64bit) @@ -1782,6 +1794,7 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = destFetch64, // Format_RGBA64 destFetchRGB64, // Format_RGBA64_Premultiplied destFetch64, // Format_Grayscale16 + destFetch64, // Format_BGR888 }; #endif @@ -1922,6 +1935,7 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] = destStore, // Format_RGBA64 destStore, // Format_RGBA64_Premultiplied destStore, // Format_Grayscale16 + destStore, // Format_BGR888 }; #if QT_CONFIG(raster_64bit) @@ -1971,6 +1985,7 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] = destStore64RGBA64, // Format_RGBA64 0, // Format_RGBA64_Premultiplied destStore64, // Format_Grayscale16 + destStore64, // Format_BGR888 }; #endif @@ -3922,6 +3937,7 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { fetchUntransformed, // RGBA64 fetchUntransformed, // RGBA64_Premultiplied fetchUntransformed, // Grayscale16 + fetchUntransformed, // BGR888 }; static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = { @@ -6589,6 +6605,14 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = qt_alphargbblit_generic, qt_rectfill_quint16 }, + // Format_BGR888 + { + blend_color_generic, + 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint24 + }, }; #if !defined(__SSE2__) diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index b1d1f30800..1fb37ece56 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -1855,10 +1855,9 @@ static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt, } // split curve and try again... - QBezier first_half, second_half; - bezier.split(&first_half, &second_half); - qt_painterpath_isect_curve(first_half, pt, winding, depth + 1); - qt_painterpath_isect_curve(second_half, pt, winding, depth + 1); + const auto halves = bezier.split(); + qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1); + qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1); } } @@ -2013,10 +2012,9 @@ static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) return true; - QBezier first_half, second_half; - bezier.split(&first_half, &second_half); - if (qt_isect_curve_horizontal(first_half, y, x1, x2, depth + 1) - || qt_isect_curve_horizontal(second_half, y, x1, x2, depth + 1)) + const auto halves = bezier.split(); + if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1) + || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1)) return true; } return false; @@ -2032,10 +2030,9 @@ static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qr if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) return true; - QBezier first_half, second_half; - bezier.split(&first_half, &second_half); - if (qt_isect_curve_vertical(first_half, x, y1, y2, depth + 1) - || qt_isect_curve_vertical(second_half, x, y1, y2, depth + 1)) + const auto halves = bezier.split(); + if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1) + || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1)) return true; } return false; diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp index 83f9415c69..85c894a74a 100644 --- a/src/gui/platform/wasm/qwasmlocalfileaccess.cpp +++ b/src/gui/platform/wasm/qwasmlocalfileaccess.cpp @@ -164,6 +164,43 @@ void openFile(const std::string &accept, openFiles(accept, FileSelectMode::SingleFile, fileDialogClosedWithInt, acceptFile, fileDataReady); } +void saveFile(const char *content, size_t size, const std::string &fileNameHint) +{ + // Save a file by creating programatically clicking a download + // link to an object url to a Blob containing the file content. + // File content is copied once, so that the passed in content + // buffer can be released as soon as this function returns - we + // don't know for how long the browser will retain the TypedArray + // view used to create the Blob. + + emscripten::val document = emscripten::val::global("document"); + emscripten::val window = emscripten::val::global("window"); + + emscripten::val fileContentView = emscripten::val(emscripten::typed_memory_view(size, content)); + emscripten::val fileContentCopy = emscripten::val::global("ArrayBuffer").new_(size); + emscripten::val fileContentCopyView = emscripten::val::global("Uint8Array").new_(fileContentCopy); + fileContentCopyView.call<void>("set", fileContentView); + + emscripten::val contentArray = emscripten::val::array(); + contentArray.call<void>("push", fileContentCopyView); + emscripten::val type = emscripten::val::object(); + type.set("type","application/octet-stream"); + emscripten::val contentBlob = emscripten::val::global("Blob").new_(contentArray, type); + + emscripten::val contentUrl = window["URL"].call<emscripten::val>("createObjectURL", contentBlob); + emscripten::val contentLink = document.call<emscripten::val>("createElement", std::string("a")); + contentLink.set("href", contentUrl); + contentLink.set("download", fileNameHint); + contentLink.set("style", "display:none"); + + emscripten::val body = document["body"]; + body.call<void>("appendChild", contentLink); + contentLink.call<void>("click"); + body.call<void>("removeChild", contentLink); + + window["URL"].call<emscripten::val>("revokeObjectURL", contentUrl); +} + } // namespace QWasmLocalFileAccess QT_END_NAMESPACE diff --git a/src/gui/platform/wasm/qwasmlocalfileaccess_p.h b/src/gui/platform/wasm/qwasmlocalfileaccess_p.h index 794db8d9b2..ccd88570c8 100644 --- a/src/gui/platform/wasm/qwasmlocalfileaccess_p.h +++ b/src/gui/platform/wasm/qwasmlocalfileaccess_p.h @@ -71,6 +71,8 @@ void openFile(const std::string &accept, const std::function<char *(uint64_t size, const std::string name)> &acceptFile, const std::function<void()> &fileDataReady); +void saveFile(const char *content, size_t size, const std::string &fileNameHint); + } // namespace QWasmLocalFileAccess QT_END_NAMESPACE diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp index 45f1ca596e..ce7c7610c1 100644 --- a/src/gui/text/qcssparser.cpp +++ b/src/gui/text/qcssparser.cpp @@ -92,6 +92,7 @@ static const QCssKnownValue properties[NumProperties - 1] = { { "border-bottom-right-radius", BorderBottomRightRadius }, { "border-bottom-style", BorderBottomStyle }, { "border-bottom-width", BorderBottomWidth }, + { "border-collapse", BorderCollapse }, { "border-color", BorderColor }, { "border-image", BorderImage }, { "border-left", BorderLeft }, @@ -611,11 +612,7 @@ bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *st case BorderRightStyle: styles[RightEdge] = decl.styleValue(); break; case BorderStyles: decl.styleValues(styles); break; -#ifndef QT_OS_ANDROID_GCC_48_WORKAROUND case BorderTopLeftRadius: radii[0] = sizeValue(decl); break; -#else - case BorderTopLeftRadius: new(radii)QSize(sizeValue(decl)); break; -#endif case BorderTopRightRadius: radii[1] = sizeValue(decl); break; case BorderBottomLeftRadius: radii[2] = sizeValue(decl); break; case BorderBottomRightRadius: radii[3] = sizeValue(decl); break; @@ -1732,6 +1729,14 @@ void Declaration::borderImageValue(QString *image, int *cuts, *h = *v; } +bool Declaration::borderCollapseValue() const +{ + if (d->values.count() != 1) + return false; + else + return d->values.at(0).toString() == QLatin1String("collapse"); +} + QIcon Declaration::iconValue() const { if (d->parsed.isValid()) diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h index ddc46803ae..ab85e76cf3 100644 --- a/src/gui/text/qcssparser_p.h +++ b/src/gui/text/qcssparser_p.h @@ -122,6 +122,7 @@ enum Property { BorderRight, BorderTop, BorderBottom, + BorderCollapse, Padding, PaddingLeft, PaddingRight, @@ -478,6 +479,7 @@ struct Q_GUI_EXPORT Declaration QIcon iconValue() const; void borderImageValue(QString *image, int *cuts, TileMode *h, TileMode *v) const; + bool borderCollapseValue() const; }; QT_CSS_DECLARE_TYPEINFO(Declaration, Q_MOVABLE_TYPE) diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 97e73f0723..3c1a052f37 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -180,14 +180,14 @@ Q_GUI_EXPORT int qt_defaultDpi() } QFontPrivate::QFontPrivate() - : engineData(0), dpi(qt_defaultDpi()), screen(0), + : engineData(0), dpi(qt_defaultDpi()), underline(false), overline(false), strikeOut(false), kerning(true), capital(0), letterSpacingIsAbsolute(false), scFont(0) { } QFontPrivate::QFontPrivate(const QFontPrivate &other) - : request(other.request), engineData(0), dpi(other.dpi), screen(other.screen), + : request(other.request), engineData(0), dpi(other.dpi), underline(other.underline), overline(other.overline), strikeOut(other.strikeOut), kerning(other.kerning), capital(other.capital), letterSpacingIsAbsolute(other.letterSpacingIsAbsolute), @@ -581,11 +581,9 @@ QFont::QFont(const QFont &font, const QPaintDevice *pd) { Q_ASSERT(pd); const int dpi = pd->logicalDpiY(); - const int screen = 0; - if (font.d->dpi != dpi || font.d->screen != screen ) { + if (font.d->dpi != dpi) { d = new QFontPrivate(*font.d); d->dpi = dpi; - d->screen = screen; } else { d = font.d; } diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index 466e19e9cc..adbb7a0121 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -185,7 +185,6 @@ public: QFontDef request; mutable QFontEngineData *engineData; int dpi; - int screen; uint underline : 1; uint overline : 1; @@ -230,19 +229,17 @@ public: void clear(); struct Key { - Key() : script(0), multi(0), screen(0) { } - Key(const QFontDef &d, uchar c, bool m = 0, uchar s = 0) - : def(d), script(c), multi(m), screen(s) { } + Key() : script(0), multi(0) { } + Key(const QFontDef &d, uchar c, bool m = 0) + : def(d), script(c), multi(m) { } QFontDef def; uchar script; uchar multi: 1; - uchar screen: 7; inline bool operator<(const Key &other) const { if (script != other.script) return script < other.script; - if (screen != other.screen) return screen < other.screen; if (multi != other.multi) return multi < other.multi; if (multi && def.fallBackFamilies.size() != other.def.fallBackFamilies.size()) return def.fallBackFamilies.size() < other.def.fallBackFamilies.size(); @@ -251,7 +248,6 @@ public: inline bool operator==(const Key &other) const { return script == other.script - && screen == other.screen && multi == other.multi && (!multi || def.fallBackFamilies == other.def.fallBackFamilies) && def == other.def; diff --git a/src/gui/text/qfontmetrics.cpp b/src/gui/text/qfontmetrics.cpp index c85dd4e1e3..d3e4f11e8c 100644 --- a/src/gui/text/qfontmetrics.cpp +++ b/src/gui/text/qfontmetrics.cpp @@ -185,11 +185,9 @@ QFontMetrics::QFontMetrics(const QFont &font, const QPaintDevice *paintdevice) #endif { const int dpi = paintdevice ? paintdevice->logicalDpiY() : qt_defaultDpi(); - const int screen = 0; - if (font.d->dpi != dpi || font.d->screen != screen ) { + if (font.d->dpi != dpi) { d = new QFontPrivate(*font.d); d->dpi = dpi; - d->screen = screen; } else { d = font.d; } @@ -1178,11 +1176,9 @@ QFontMetricsF::QFontMetricsF(const QFont &font, const QPaintDevice *paintdevice) #endif { int dpi = paintdevice ? paintdevice->logicalDpiY() : qt_defaultDpi(); - const int screen = 0; - if (font.d->dpi != dpi || font.d->screen != screen ) { + if (font.d->dpi != dpi) { d = new QFontPrivate(*font.d); d->dpi = dpi; - d->screen = screen; } else { d = font.d; } diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index dc34a96918..c80617f929 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -2593,51 +2593,43 @@ void QTextHtmlExporter::emitFloatStyle(QTextFrameFormat::Position pos, StyleMode html += QLatin1Char('\"'); } -void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style) +static QLatin1String richtextBorderStyleToHtmlBorderStyle(QTextFrameFormat::BorderStyle style) { - Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset); - - html += QLatin1String(" border-style:"); - switch (style) { case QTextFrameFormat::BorderStyle_None: - html += QLatin1String("none"); - break; + return QLatin1String("none"); case QTextFrameFormat::BorderStyle_Dotted: - html += QLatin1String("dotted"); - break; + return QLatin1String("dotted"); case QTextFrameFormat::BorderStyle_Dashed: - html += QLatin1String("dashed"); - break; + return QLatin1String("dashed"); case QTextFrameFormat::BorderStyle_Solid: - html += QLatin1String("solid"); - break; + return QLatin1String("solid"); case QTextFrameFormat::BorderStyle_Double: - html += QLatin1String("double"); - break; + return QLatin1String("double"); case QTextFrameFormat::BorderStyle_DotDash: - html += QLatin1String("dot-dash"); - break; + return QLatin1String("dot-dash"); case QTextFrameFormat::BorderStyle_DotDotDash: - html += QLatin1String("dot-dot-dash"); - break; + return QLatin1String("dot-dot-dash"); case QTextFrameFormat::BorderStyle_Groove: - html += QLatin1String("groove"); - break; + return QLatin1String("groove"); case QTextFrameFormat::BorderStyle_Ridge: - html += QLatin1String("ridge"); - break; + return QLatin1String("ridge"); case QTextFrameFormat::BorderStyle_Inset: - html += QLatin1String("inset"); - break; + return QLatin1String("inset"); case QTextFrameFormat::BorderStyle_Outset: - html += QLatin1String("outset"); - break; + return QLatin1String("outset"); default: - Q_ASSERT(false); - break; + Q_UNREACHABLE(); }; + return QLatin1String(""); +} + +void QTextHtmlExporter::emitBorderStyle(QTextFrameFormat::BorderStyle style) +{ + Q_ASSERT(style <= QTextFrameFormat::BorderStyle_Outset); + html += QLatin1String(" border-style:"); + html += richtextBorderStyleToHtmlBorderStyle(style); html += QLatin1Char(';'); } @@ -3204,6 +3196,33 @@ void QTextHtmlExporter::emitTable(const QTextTable *table) if (cellFormat.hasProperty(QTextFormat::TableCellBottomPadding)) styleString += QLatin1String(" padding-bottom:") + QString::number(cellFormat.bottomPadding()) + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellTopBorder)) + styleString += QLatin1String(" border-top:") + QString::number(cellFormat.topBorder()) + QLatin1String("px;"); + if (cellFormat.hasProperty(QTextFormat::TableCellRightBorder)) + styleString += QLatin1String(" border-right:") + QString::number(cellFormat.rightBorder()) + QLatin1String("px;"); + if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorder)) + styleString += QLatin1String(" border-bottom:") + QString::number(cellFormat.bottomBorder()) + QLatin1String("px;"); + if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorder)) + styleString += QLatin1String(" border-left:") + QString::number(cellFormat.leftBorder()) + QLatin1String("px;"); + + if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderBrush)) + styleString += QLatin1String(" border-top-color:") + cellFormat.topBorderBrush().color().name() + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderBrush)) + styleString += QLatin1String(" border-right-color:") + cellFormat.rightBorderBrush().color().name() + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderBrush)) + styleString += QLatin1String(" border-bottom-color:") + cellFormat.bottomBorderBrush().color().name() + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderBrush)) + styleString += QLatin1String(" border-left-color:") + cellFormat.leftBorderBrush().color().name() + QLatin1Char(';'); + + if (cellFormat.hasProperty(QTextFormat::TableCellTopBorderStyle)) + styleString += QLatin1String(" border-top-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.topBorderStyle()) + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellRightBorderStyle)) + styleString += QLatin1String(" border-right-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.rightBorderStyle()) + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellBottomBorderStyle)) + styleString += QLatin1String(" border-bottom-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.bottomBorderStyle()) + QLatin1Char(';'); + if (cellFormat.hasProperty(QTextFormat::TableCellLeftBorderStyle)) + styleString += QLatin1String(" border-left-style:") + richtextBorderStyleToHtmlBorderStyle(cellFormat.leftBorderStyle()) + QLatin1Char(';'); + if (!styleString.isEmpty()) html += QLatin1String(" style=\"") + styleString + QLatin1Char('\"'); @@ -3310,6 +3329,9 @@ void QTextHtmlExporter::emitFrameStyle(const QTextFrameFormat &format, FrameType QString::number(format.leftMargin()), QString::number(format.rightMargin())); + if (format.property(QTextFormat::TableBorderCollapse).toBool()) + html += QLatin1String(" border-collapse:collapse;"); + if (html.length() == originalHtmlLength) // nothing emitted? html.chop(styleAttribute.size()); else diff --git a/src/gui/text/qtextdocumentfragment.cpp b/src/gui/text/qtextdocumentfragment.cpp index 34698b2fb0..723e5c907c 100644 --- a/src/gui/text/qtextdocumentfragment.cpp +++ b/src/gui/text/qtextdocumentfragment.cpp @@ -986,6 +986,7 @@ QTextHtmlImporter::Table QTextHtmlImporter::scanTable(int tableNodeIdx) tableFmt.setColumns(table.columns); tableFmt.setColumnWidthConstraints(columnWidths); tableFmt.setHeaderRowCount(tableHeaderRowCount); + tableFmt.setBorderCollapse(node.borderCollapse); fmt = tableFmt; } @@ -1061,6 +1062,31 @@ QTextHtmlImporter::ProcessNodeResult QTextHtmlImporter::processBlockNode() fmt.setLeftPadding(leftPadding(currentNodeIdx)); if (rightPadding(currentNodeIdx) >= 0) fmt.setRightPadding(rightPadding(currentNodeIdx)); + if (tableCellBorder(currentNodeIdx, QCss::TopEdge) > 0) + fmt.setTopBorder(tableCellBorder(currentNodeIdx, QCss::TopEdge)); + if (tableCellBorder(currentNodeIdx, QCss::RightEdge) > 0) + fmt.setRightBorder(tableCellBorder(currentNodeIdx, QCss::RightEdge)); + if (tableCellBorder(currentNodeIdx, QCss::BottomEdge) > 0) + fmt.setBottomBorder(tableCellBorder(currentNodeIdx, QCss::BottomEdge)); + if (tableCellBorder(currentNodeIdx, QCss::LeftEdge) > 0) + fmt.setLeftBorder(tableCellBorder(currentNodeIdx, QCss::LeftEdge)); + if (tableCellBorderStyle(currentNodeIdx, QCss::TopEdge) != QTextFrameFormat::BorderStyle_None) + fmt.setTopBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::TopEdge)); + if (tableCellBorderStyle(currentNodeIdx, QCss::RightEdge) != QTextFrameFormat::BorderStyle_None) + fmt.setRightBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::RightEdge)); + if (tableCellBorderStyle(currentNodeIdx, QCss::BottomEdge) != QTextFrameFormat::BorderStyle_None) + fmt.setBottomBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::BottomEdge)); + if (tableCellBorderStyle(currentNodeIdx, QCss::LeftEdge) != QTextFrameFormat::BorderStyle_None) + fmt.setLeftBorderStyle(tableCellBorderStyle(currentNodeIdx, QCss::LeftEdge)); + if (tableCellBorderBrush(currentNodeIdx, QCss::TopEdge) != Qt::NoBrush) + fmt.setTopBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::TopEdge)); + if (tableCellBorderBrush(currentNodeIdx, QCss::RightEdge) != Qt::NoBrush) + fmt.setRightBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::RightEdge)); + if (tableCellBorderBrush(currentNodeIdx, QCss::BottomEdge) != Qt::NoBrush) + fmt.setBottomBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::BottomEdge)); + if (tableCellBorderBrush(currentNodeIdx, QCss::LeftEdge) != Qt::NoBrush) + fmt.setLeftBorderBrush(tableCellBorderBrush(currentNodeIdx, QCss::LeftEdge)); + cell.setFormat(fmt); cursor.setPosition(cell.firstPosition()); diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp index 43b32e7e2c..5d37982a8b 100644 --- a/src/gui/text/qtexthtmlparser.cpp +++ b/src/gui/text/qtexthtmlparser.cpp @@ -491,12 +491,19 @@ QTextHtmlParserNode::QTextHtmlParserNode() listStyle(QTextListFormat::ListStyleUndefined), imageWidth(-1), imageHeight(-1), tableBorder(0), tableCellRowSpan(1), tableCellColSpan(1), tableCellSpacing(2), tableCellPadding(0), borderBrush(Qt::darkGray), borderStyle(QTextFrameFormat::BorderStyle_Outset), + borderCollapse(false), userState(-1), cssListIndent(0), wsm(WhiteSpaceModeUndefined) { margin[QTextHtmlParser::MarginLeft] = 0; margin[QTextHtmlParser::MarginRight] = 0; margin[QTextHtmlParser::MarginTop] = 0; margin[QTextHtmlParser::MarginBottom] = 0; + + for (int i = 0; i < 4; ++i) { + tableCellBorderStyle[i] = QTextFrameFormat::BorderStyle_None; + tableCellBorder[i] = 0; + tableCellBorderBrush[i] = Qt::NoBrush; + } } void QTextHtmlParser::dumpHtml() @@ -1169,6 +1176,25 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration> QCss::ValueExtractor extractor(declarations); extractor.extractBox(margin, padding); + if (id == Html_td || id == Html_th) { + QCss::BorderStyle cssStyles[4]; + int cssBorder[4]; + QSize cssRadii[4]; // unused + for (int i = 0; i < 4; ++i) { + cssStyles[i] = QCss::BorderStyle_None; + cssBorder[i] = 0; + } + // this will parse (and cache) "border-width" as a list so the + // QCss::BorderWidth parsing below which expects a single value + // will not work as expected - which in this case does not matter + // because tableBorder is not relevant for cells. + extractor.extractBorder(cssBorder, tableCellBorderBrush, cssStyles, cssRadii); + for (int i = 0; i < 4; ++i) { + tableCellBorderStyle[i] = static_cast<QTextFrameFormat::BorderStyle>(cssStyles[i] - 1); + tableCellBorder[i] = static_cast<qreal>(cssBorder[i]); + } + } + for (int i = 0; i < declarations.count(); ++i) { const QCss::Declaration &decl = declarations.at(i); if (decl.d->values.isEmpty()) continue; @@ -1186,6 +1212,9 @@ void QTextHtmlParserNode::applyCssDeclarations(const QVector<QCss::Declaration> case QCss::BorderWidth: tableBorder = extractor.lengthValue(decl); break; + case QCss::BorderCollapse: + borderCollapse = decl.borderCollapseValue(); + break; case QCss::Color: charFormat.setForeground(decl.colorValue()); break; case QCss::Float: cssFloat = QTextFrameFormat::InFlow; @@ -1654,6 +1683,11 @@ void QTextHtmlParser::applyAttributes(const QStringList &attributes) if (!c.isValid()) qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData()); node->charFormat.setBackground(c); + } else if (key == QLatin1String("bordercolor")) { + QColor c; c.setNamedColor(value); + if (!c.isValid()) + qWarning("QTextHtmlParser::applyAttributes: Unknown color name '%s'",value.toLatin1().constData()); + node->borderBrush = c; } else if (key == QLatin1String("background")) { node->applyBackgroundImage(value, resourceProvider); } else if (key == QLatin1String("cellspacing")) { diff --git a/src/gui/text/qtexthtmlparser_p.h b/src/gui/text/qtexthtmlparser_p.h index ff5f5b4c35..31f558709f 100644 --- a/src/gui/text/qtexthtmlparser_p.h +++ b/src/gui/text/qtexthtmlparser_p.h @@ -195,8 +195,12 @@ struct QTextHtmlParserNode { int tableCellColSpan; qreal tableCellSpacing; qreal tableCellPadding; + qreal tableCellBorder[4]; + QBrush tableCellBorderBrush[4]; + QTextFrameFormat::BorderStyle tableCellBorderStyle[4]; QBrush borderBrush; QTextFrameFormat::BorderStyle borderStyle; + bool borderCollapse; int userState; int cssListIndent; @@ -290,6 +294,10 @@ public: inline int leftPadding(int i) const { return at(i).padding[MarginLeft]; } inline int rightPadding(int i) const { return at(i).padding[MarginRight]; } + inline qreal tableCellBorder(int i, int edge) const { return at(i).tableCellBorder[edge]; } + inline QTextFrameFormat::BorderStyle tableCellBorderStyle(int i, int edge) const { return at(i).tableCellBorderStyle[edge]; } + inline QBrush tableCellBorderBrush(int i, int edge) const { return at(i).tableCellBorderBrush[edge]; } + void dumpHtml(); void parse(const QString &text, const QTextDocument *resourceProvider); diff --git a/src/network/access/access.pri b/src/network/access/access.pri index 8a92308f12..cfb20dcd71 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -97,7 +97,8 @@ qtConfig(http) { access/qhttpnetworkrequest.cpp \ access/qhttpprotocolhandler.cpp \ access/qhttpthreaddelegate.cpp \ - access/qnetworkreplyhttpimpl.cpp + access/qnetworkreplyhttpimpl.cpp \ + access/qhttp2configuration.cpp HEADERS += \ access/qabstractprotocolhandler_p.h \ @@ -111,7 +112,8 @@ qtConfig(http) { access/qhttpnetworkrequest_p.h \ access/qhttpprotocolhandler_p.h \ access/qhttpthreaddelegate_p.h \ - access/qnetworkreplyhttpimpl_p.h + access/qnetworkreplyhttpimpl_p.h \ + access/qhttp2configuration.h qtConfig(ssl) { SOURCES += \ diff --git a/src/network/access/http2/hpack.cpp b/src/network/access/http2/hpack.cpp index 2d324d5092..b40cc29e1a 100644 --- a/src/network/access/http2/hpack.cpp +++ b/src/network/access/http2/hpack.cpp @@ -208,6 +208,11 @@ void Encoder::setMaxDynamicTableSize(quint32 size) lookupTable.setMaxDynamicTableSize(size); } +void Encoder::setCompressStrings(bool compress) +{ + compressStrings = compress; +} + bool Encoder::encodeRequestPseudoHeaders(BitOStream &outputStream, const HttpHeader &header) { diff --git a/src/network/access/http2/hpack_p.h b/src/network/access/http2/hpack_p.h index 6a1d30d87b..8c2701e7af 100644 --- a/src/network/access/http2/hpack_p.h +++ b/src/network/access/http2/hpack_p.h @@ -83,6 +83,7 @@ public: quint32 newSize); void setMaxDynamicTableSize(quint32 size); + void setCompressStrings(bool compress); private: bool encodeRequestPseudoHeaders(BitOStream &outputStream, diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp index e695b4dd9e..ce33505683 100644 --- a/src/network/access/http2/http2frames.cpp +++ b/src/network/access/http2/http2frames.cpp @@ -305,7 +305,7 @@ FrameStatus FrameReader::read(QAbstractSocket &socket) return status; } - if (Http2PredefinedParameters::maxFrameSize < frame.payloadSize()) + if (Http2PredefinedParameters::maxPayloadSize < frame.payloadSize()) return FrameStatus::sizeError; frame.buffer.resize(frame.payloadSize() + frameHeaderSize); @@ -388,7 +388,7 @@ void FrameWriter::setPayloadSize(quint32 size) auto &buffer = frame.buffer; Q_ASSERT(buffer.size() >= frameHeaderSize); - Q_ASSERT(size < maxPayloadSize); + Q_ASSERT(size <= maxPayloadSize); buffer[0] = size >> 16; buffer[1] = size >> 8; diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp index 0be72042c6..31da6fd616 100644 --- a/src/network/access/http2/http2protocol.cpp +++ b/src/network/access/http2/http2protocol.cpp @@ -43,6 +43,8 @@ #include "private/qhttpnetworkrequest_p.h" #include "private/qhttpnetworkreply_p.h" +#include <access/qhttp2configuration.h> + #include <QtCore/qbytearray.h> #include <QtCore/qstring.h> @@ -62,88 +64,29 @@ const char Http2clientPreface[clientPrefaceLength] = 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a}; -// TODO: (in 5.11) - remove it! -const char *http2ParametersPropertyName = "QT_HTTP2_PARAMETERS_PROPERTY"; - -ProtocolParameters::ProtocolParameters() -{ - settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID] = qtDefaultStreamReceiveWindowSize; - settingsFrameData[Settings::ENABLE_PUSH_ID] = 0; -} - -bool ProtocolParameters::validate() const +Frame configurationToSettingsFrame(const QHttp2Configuration &config) { - // 0. Huffman/indexing: any values are valid and allowed. - - // 1. Session receive window size (client side): HTTP/2 starts from the - // default value of 64Kb, if a client code tries to set lesser value, - // the delta would become negative, but this is not allowed. - if (maxSessionReceiveWindowSize < qint32(defaultSessionWindowSize)) { - qCWarning(QT_HTTP2, "Session receive window must be at least 65535 bytes"); - return false; - } - - // 2. HEADER_TABLE_SIZE: we do not validate HEADER_TABLE_SIZE, considering - // all values as valid. RFC 7540 and 7541 do not provide any lower/upper - // limits. If it's 0 - we do not index anything, if it's too huge - a user - // who provided such a value can potentially have a huge memory footprint, - // up to them to decide. - - // 3. SETTINGS_ENABLE_PUSH: RFC 7540, 6.5.2, a value other than 0 or 1 will - // be treated by our peer as a PROTOCOL_ERROR. - if (settingsFrameData.contains(Settings::ENABLE_PUSH_ID) - && settingsFrameData[Settings::ENABLE_PUSH_ID] > 1) { - qCWarning(QT_HTTP2, "SETTINGS_ENABLE_PUSH can be only 0 or 1"); - return false; - } - - // 4. SETTINGS_MAX_CONCURRENT_STREAMS : RFC 7540 recommends 100 as the lower - // limit, says nothing about the upper limit. The RFC allows 0, but this makes - // no sense to us at all: there is no way a user can change this later and - // we'll not be able to get any responses on such a connection. - if (settingsFrameData.contains(Settings::MAX_CONCURRENT_STREAMS_ID) - && !settingsFrameData[Settings::MAX_CONCURRENT_STREAMS_ID]) { - qCWarning(QT_HTTP2, "MAX_CONCURRENT_STREAMS must be a positive number"); - return false; - } - - // 5. SETTINGS_INITIAL_WINDOW_SIZE. - if (settingsFrameData.contains(Settings::INITIAL_WINDOW_SIZE_ID)) { - const quint32 value = settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID]; - // RFC 7540, 6.5.2 (the upper limit). The lower limit is our own - we send - // SETTINGS frame only once and will not be able to change this 0, thus - // we'll suspend all streams. - if (!value || value > quint32(maxSessionReceiveWindowSize)) { - qCWarning(QT_HTTP2, "INITIAL_WINDOW_SIZE must be in the range " - "(0, 2^31-1]"); - return false; - } - } - - // 6. SETTINGS_MAX_FRAME_SIZE: RFC 7540, 6.5.2, a value outside of the range - // [2^14-1, 2^24-1] will be treated by our peer as a PROTOCOL_ERROR. - if (settingsFrameData.contains(Settings::MAX_FRAME_SIZE_ID)) { - const quint32 value = settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID]; - if (value < maxFrameSize || value > maxPayloadSize) { - qCWarning(QT_HTTP2, "MAX_FRAME_SIZE must be in the range [2^14, 2^24-1]"); - return false; - } + // 6.5 SETTINGS + FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID); + // Server push: + builder.append(Settings::ENABLE_PUSH_ID); + builder.append(int(config.serverPushEnabled())); + // Stream receive window size: + builder.append(Settings::INITIAL_WINDOW_SIZE_ID); + builder.append(config.streamReceiveWindowSize()); + + if (config.maxFrameSize() != minPayloadLimit) { + builder.append(Settings::MAX_FRAME_SIZE_ID); + builder.append(config.maxFrameSize()); } - - // For SETTINGS_MAX_HEADER_LIST_SIZE RFC 7540 does not provide any specific - // numbers. It's clear, if a value is too small, no header can ever be sent - // by our peer at all. The default value is unlimited and we normally do not - // change this. - // - // Note: the size is calculated as the length of uncompressed (no HPACK) - // name + value + 32 bytes. - - return true; + // TODO: In future, if the need is proven, we can + // also send decoding table size and header list size. + // For now, defaults suffice. + return builder.outboundFrame(); } -QByteArray ProtocolParameters::settingsFrameToBase64() const +QByteArray settingsFrameToBase64(const Frame &frame) { - Frame frame(settingsFrame()); // SETTINGS frame's payload consists of pairs: // 2-byte-identifier | 4-byte-value == multiple of 6. Q_ASSERT(frame.payloadSize() && !(frame.payloadSize() % 6)); @@ -157,20 +100,7 @@ QByteArray ProtocolParameters::settingsFrameToBase64() const return wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); } -Frame ProtocolParameters::settingsFrame() const -{ - // 6.5 SETTINGS - FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID); - for (auto it = settingsFrameData.cbegin(), end = settingsFrameData.cend(); - it != end; ++it) { - builder.append(it.key()); - builder.append(it.value()); - } - - return builder.outboundFrame(); -} - -void ProtocolParameters::addProtocolUpgradeHeaders(QHttpNetworkRequest *request) const +void appendProtocolUpgradeHeaders(const QHttp2Configuration &config, QHttpNetworkRequest *request) { Q_ASSERT(request); // RFC 2616, 14.10 @@ -184,8 +114,10 @@ void ProtocolParameters::addProtocolUpgradeHeaders(QHttpNetworkRequest *request) request->setHeaderField("Connection", value); // This we just (re)write. request->setHeaderField("Upgrade", "h2c"); + + const Frame frame(configurationToSettingsFrame(config)); // This we just (re)write. - request->setHeaderField("HTTP2-Settings", settingsFrameToBase64()); + request->setHeaderField("HTTP2-Settings", settingsFrameToBase64(frame)); } void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h index 7142d6f1fa..b0af5aa919 100644 --- a/src/network/access/http2/http2protocol_p.h +++ b/src/network/access/http2/http2protocol_p.h @@ -55,12 +55,14 @@ #include <QtCore/qloggingcategory.h> #include <QtCore/qmetatype.h> #include <QtCore/qglobal.h> +#include <QtCore/qmap.h> // Different HTTP/2 constants/values as defined by RFC 7540. QT_BEGIN_NAMESPACE class QHttpNetworkRequest; +class QHttp2Configuration; class QHttpNetworkReply; class QByteArray; class QString; @@ -118,13 +120,19 @@ enum Http2PredefinedParameters connectionStreamID = 0, // HTTP/2, 5.1.1 frameHeaderSize = 9, // HTTP/2, 4.1 - // It's our max frame size we send in SETTINGS frame, - // it's also the default one and we also use it to later - // validate incoming frames: - maxFrameSize = 16384, // HTTP/2 6.5.2 + // The initial allowed payload size. We would use it as an + // upper limit for a frame payload we send, until our peer + // updates us with a larger SETTINGS_MAX_FRAME_SIZE. - defaultSessionWindowSize = 65535, // HTTP/2 6.5.2 + // The initial maximum payload size that an HTTP/2 frame + // can contain is 16384. It's also the minimal size that + // can be advertised via 'SETTINGS' frames. A real frame + // can have a payload smaller than 16384. + minPayloadLimit = 16384, // HTTP/2 6.5.2 + // The maximum allowed payload size. maxPayloadSize = (1 << 24) - 1, // HTTP/2 6.5.2 + + defaultSessionWindowSize = 65535, // HTTP/2 6.5.2 // Using 1000 (rather arbitrarily), just to // impose *some* upper limit: maxPeerConcurrentStreams = 1000, @@ -145,48 +153,9 @@ const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1 const qint32 maxSessionReceiveWindowSize((quint32(1) << 31) - 1); const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / maxConcurrentStreams; -// The class ProtocolParameters allows client code to customize HTTP/2 protocol -// handler, if needed. Normally, we use our own default parameters (see below). -// In 5.10 we can also use setProperty/property on a QNAM object to pass the -// non-default values to the protocol handler. In 5.11 this will probably become -// a public API. - -using RawSettings = QMap<Settings, quint32>; - -struct Q_AUTOTEST_EXPORT ProtocolParameters -{ - ProtocolParameters(); - - bool validate() const; - QByteArray settingsFrameToBase64() const; - struct Frame settingsFrame() const; - void addProtocolUpgradeHeaders(QHttpNetworkRequest *request) const; - - // HPACK: - // TODO: for now we ignore them (fix it for 5.11, would require changes in HPACK) - bool useHuffman = true; - bool indexStrings = true; - - // This parameter is not negotiated via SETTINGS frames, so we have it - // as a member and will convey it to our peer as a WINDOW_UPDATE frame. - // Note, some servers do not accept our WINDOW_UPDATE from the default - // 64 KB to the possible maximum. Let's use a half of it: - qint32 maxSessionReceiveWindowSize = Http2::maxSessionReceiveWindowSize / 2; - - // This is our default SETTINGS frame: - // - // SETTINGS_INITIAL_WINDOW_SIZE: (2^31 - 1) / 100 - // SETTINGS_ENABLE_PUSH: 0. - // - // Note, whenever we skip some value in our SETTINGS frame, our peer - // will assume the defaults recommended by RFC 7540, which in general - // are good enough, although we (and most browsers) prefer to work - // with larger window sizes. - RawSettings settingsFrameData; -}; - -// TODO: remove in 5.11 -extern const Q_AUTOTEST_EXPORT char *http2ParametersPropertyName; +struct Frame configurationToSettingsFrame(const QHttp2Configuration &configuration); +QByteArray settingsFrameToBase64(const Frame &settingsFrame); +void appendProtocolUpgradeHeaders(const QHttp2Configuration &configuration, QHttpNetworkRequest *request); extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength]; @@ -235,6 +204,5 @@ Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2) QT_END_NAMESPACE Q_DECLARE_METATYPE(Http2::Settings) -Q_DECLARE_METATYPE(Http2::ProtocolParameters) #endif diff --git a/src/network/access/qhttp2configuration.cpp b/src/network/access/qhttp2configuration.cpp new file mode 100644 index 0000000000..a32bccfd09 --- /dev/null +++ b/src/network/access/qhttp2configuration.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qhttp2configuration.h" + +#include "private/http2protocol_p.h" +#include "private/hpack_p.h" + +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QHttp2Configuration + \brief The QHttp2Configuration class controls HTTP/2 parameters and settings + \since 5.14 + + \reentrant + \inmodule QtNetwork + \ingroup network + \ingroup shared + + QHttp2Configuration controls HTTP/2 parameters and settings that + QNetworkAccessManager will use to send requests and process responses + when the HTTP/2 protocol is enabled. + + The HTTP/2 parameters that QHttp2Configuration currently supports include: + + \list + \li The session window size for connection-level flow control. + Will be sent to a remote peer when needed as 'WINDOW_UPDATE' + frames on the stream with an identifier 0. + \li The stream receiving window size for stream-level flow control. + Sent as 'SETTINGS_INITIAL_WINDOW_SIZE' parameter in the initial + SETTINGS frame and, when needed, 'WINDOW_UPDATE' frames will be + sent on streams that QNetworkAccessManager opens. + \li The maximum frame size. This parameter limits the maximum payload + a frame coming from the remote peer can have. Sent by QNetworkAccessManager + as 'SETTINGS_MAX_FRAME_SIZE' parameter in the initial 'SETTINGS' + frame. + \li The server push. Allows to enable or disable server push. Sent + as 'SETTINGS_ENABLE_PUSH' parameter in the initial 'SETTINGS' + frame. + \endlist + + The QHttp2Configuration class also controls if the header compression + algorithm (HPACK) is additionally using Huffman coding for string + compression. + + \note The configuration must be set before the first request + was sent to a given host (and thus an HTTP/2 session established). + + \note Details about flow control, server push and 'SETTINGS' + can be found in \l {https://httpwg.org/specs/rfc7540.html}{RFC 7540}. + Different modes and parameters of the HPACK compression algorithm + are described in \l {https://httpwg.org/specs/rfc7541.html}{RFC 7541}. + + \sa QNetworkRequest::setHttp2Configuration(), QNetworkRequest::http2Configuration(), QNetworkAccessManager +*/ + +class QHttp2ConfigurationPrivate : public QSharedData +{ +public: + unsigned sessionWindowSize = Http2::defaultSessionWindowSize; + // The size below is quite a limiting default value, QNetworkRequest + // by default sets a larger number, an application can change this using + // QNetworkRequest::setHttp2Configuration. + unsigned streamWindowSize = Http2::defaultSessionWindowSize; + + unsigned maxFrameSize = Http2::minPayloadLimit; // Initial (default) value of 16Kb. + + bool pushEnabled = false; + // TODO: for now those two below are noop. + bool huffmanCompressionEnabled = true; +}; + +/*! + Default constructs a QHttp2Configuration object. + + Such a configuration has the following values: + \list + \li Server push is disabled + \li Huffman string compression is enabled + \li Window size for connection-level flow control is 65535 octets + \li Window size for stream-level flow control is 65535 octets + \li Frame size is 16384 octets + \endlist +*/ +QHttp2Configuration::QHttp2Configuration() + : d(new QHttp2ConfigurationPrivate) +{ +} + +/*! + Copy-constructs this QHttp2Configuration. +*/ +QHttp2Configuration::QHttp2Configuration(const QHttp2Configuration &) = default; + +/*! + Move-constructs this QHttp2Configuration from \a other +*/ +QHttp2Configuration::QHttp2Configuration(QHttp2Configuration &&other) noexcept +{ + swap(other); +} + +/*! + Copy-assigns to this QHttp2Configuration. +*/ +QHttp2Configuration &QHttp2Configuration::operator=(const QHttp2Configuration &) = default; + +/*! + Move-assigns to this QHttp2Configuration. +*/ +QHttp2Configuration &QHttp2Configuration::operator=(QHttp2Configuration &&) noexcept = default; + +/*! + Destructor. +*/ +QHttp2Configuration::~QHttp2Configuration() +{ +} + +/*! + If \a enable is \c true, a remote server can potentially + use server push to send reponses in advance. + + \sa serverPushEnabled +*/ +void QHttp2Configuration::setServerPushEnabled(bool enable) +{ + d->pushEnabled = enable; +} + +/*! + Returns true if server push was enabled. + + \note By default, QNetworkAccessManager disables server + push via the 'SETTINGS' frame. + + \sa setServerPushEnabled +*/ +bool QHttp2Configuration::serverPushEnabled() const +{ + return d->pushEnabled; +} + +/*! + If \a enable is \c true, HPACK compression will additionally + compress string using the Huffman coding. Enabled by default. + + \note This parameter only affects 'HEADERS' frames that + QNetworkAccessManager is sending. + + \sa huffmanCompressionEnabled +*/ +void QHttp2Configuration::setHuffmanCompressionEnabled(bool enable) +{ + d->huffmanCompressionEnabled = enable; +} + +/*! + Returns \c true if the Huffman coding in HPACK is enabled. + + \sa setHuffmanCompressionEnabled +*/ +bool QHttp2Configuration::huffmanCompressionEnabled() const +{ + return d->huffmanCompressionEnabled; +} + +/*! + Sets the window size for connection-level flow control. + \a size cannot be 0 and must not exceed 2147483647 octets. + + \sa sessionReceiveWindowSize +*/ +bool QHttp2Configuration::setSessionReceiveWindowSize(unsigned size) +{ + if (!size || size > Http2::maxSessionReceiveWindowSize) { // RFC-7540, 6.9 + qCWarning(QT_HTTP2) << "Invalid session window size"; + return false; + } + + d->sessionWindowSize = size; + return true; +} + +/*! + Returns the window size for connection-level flow control. + The default value QNetworkAccessManager will be using is + 2147483647 octets. +*/ +unsigned QHttp2Configuration::sessionReceiveWindowSize() const +{ + return d->sessionWindowSize; +} + +/*! + Sets the window size for stream-level flow control. + \a size cannot be 0 and must not exceed 2147483647 octets. + + \sa streamReceiveWindowSize + */ +bool QHttp2Configuration::setStreamReceiveWindowSize(unsigned size) +{ + if (!size || size > Http2::maxSessionReceiveWindowSize) { // RFC-7540, 6.9 + qCWarning(QT_HTTP2) << "Invalid stream window size"; + return false; + } + + d->streamWindowSize = size; + return true; +} + +/*! + Returns the window size for stream-level flow control. + The default value QNetworkAccessManager will be using is + 21474836 octets. +*/ +unsigned QHttp2Configuration::streamReceiveWindowSize() const +{ + return d->streamWindowSize; +} + +/*! + Sets the maximum frame size that QNetworkAccessManager + will advertise to the server when sending its initial SETTINGS frame. + \note While this \a size is required to be within a range between + 16384 and 16777215 inclusive, the actual payload size in frames + that carry payload maybe be less than 16384. +*/ +bool QHttp2Configuration::setMaxFrameSize(unsigned size) +{ + if (size < Http2::minPayloadLimit || size > Http2::maxPayloadSize) { + qCWarning(QT_HTTP2) << "Maximum frame size to advertise is invalid"; + return false; + } + + d->maxFrameSize = size; + return true; +} + +/*! + The maximum payload size that HTTP/2 frames can + have. The default (initial) value is 16384 octets. +*/ +unsigned QHttp2Configuration::maxFrameSize() const +{ + return d->maxFrameSize; +} + +/*! + Swaps this configuration with the \a other configuration. +*/ +void QHttp2Configuration::swap(QHttp2Configuration &other) noexcept +{ + d.swap(other.d); +} + +/*! + Returns \c true if \a lhs and \a rhs have the same set of HTTP/2 + parameters. +*/ +bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs) +{ + if (lhs.d == rhs.d) + return true; + + return lhs.d->pushEnabled == rhs.d->pushEnabled + && lhs.d->huffmanCompressionEnabled == rhs.d->huffmanCompressionEnabled + && lhs.d->sessionWindowSize == rhs.d->sessionWindowSize + && lhs.d->streamWindowSize == rhs.d->streamWindowSize; +} + +QT_END_NAMESPACE diff --git a/src/network/access/qhttp2configuration.h b/src/network/access/qhttp2configuration.h new file mode 100644 index 0000000000..e5c235e2be --- /dev/null +++ b/src/network/access/qhttp2configuration.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QHTTP2CONFIGURATION_H +#define QHTTP2CONFIGURATION_H + +#include <QtNetwork/qtnetworkglobal.h> + +#include <QtCore/qshareddata.h> + +#ifndef Q_CLANG_QDOC +QT_REQUIRE_CONFIG(http); +#endif + +QT_BEGIN_NAMESPACE + +class QHttp2ConfigurationPrivate; +class Q_NETWORK_EXPORT QHttp2Configuration +{ + friend Q_NETWORK_EXPORT bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs); + +public: + QHttp2Configuration(); + QHttp2Configuration(const QHttp2Configuration &other); + QHttp2Configuration(QHttp2Configuration &&other) noexcept; + QHttp2Configuration &operator = (const QHttp2Configuration &other); + QHttp2Configuration &operator = (QHttp2Configuration &&other) noexcept; + + ~QHttp2Configuration(); + + void setServerPushEnabled(bool enable); + bool serverPushEnabled() const; + + void setHuffmanCompressionEnabled(bool enable); + bool huffmanCompressionEnabled() const; + + bool setSessionReceiveWindowSize(unsigned size); + unsigned sessionReceiveWindowSize() const; + + bool setStreamReceiveWindowSize(unsigned size); + unsigned streamReceiveWindowSize() const; + + bool setMaxFrameSize(unsigned size); + unsigned maxFrameSize() const; + + void swap(QHttp2Configuration &other) noexcept; + +private: + + QSharedDataPointer<QHttp2ConfigurationPrivate> d; +}; + +Q_DECLARE_SHARED(QHttp2Configuration) + +Q_NETWORK_EXPORT bool operator==(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs); + +inline bool operator!=(const QHttp2Configuration &lhs, const QHttp2Configuration &rhs) +{ + return !(lhs == rhs); +} + +QT_END_NAMESPACE + +#endif // QHTTP2CONFIGURATION_H diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp index 0ece5b7179..c1053882af 100644 --- a/src/network/access/qhttp2protocolhandler.cpp +++ b/src/network/access/qhttp2protocolhandler.cpp @@ -40,6 +40,7 @@ #include "qhttpnetworkconnection_p.h" #include "qhttp2protocolhandler_p.h" +#include "http2/http2frames_p.h" #include "http2/bitstreams_p.h" #include <private/qnoncontiguousbytedevice_p.h> @@ -51,6 +52,8 @@ #include <QtCore/qlist.h> #include <QtCore/qurl.h> +#include <qhttp2configuration.h> + #ifndef QT_NO_NETWORKPROXY #include <QtNetwork/qnetworkproxy.h> #endif @@ -172,30 +175,11 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan Q_ASSERT(channel && m_connection); continuedFrames.reserve(20); - const ProtocolParameters params(m_connection->http2Parameters()); - Q_ASSERT(params.validate()); - - maxSessionReceiveWindowSize = params.maxSessionReceiveWindowSize; - - const RawSettings &data = params.settingsFrameData; - for (auto param = data.cbegin(), end = data.cend(); param != end; ++param) { - switch (param.key()) { - case Settings::INITIAL_WINDOW_SIZE_ID: - streamInitialReceiveWindowSize = param.value(); - break; - case Settings::ENABLE_PUSH_ID: - pushPromiseEnabled = param.value(); - break; - case Settings::HEADER_TABLE_SIZE_ID: - case Settings::MAX_CONCURRENT_STREAMS_ID: - case Settings::MAX_FRAME_SIZE_ID: - case Settings::MAX_HEADER_LIST_SIZE_ID: - // These other settings are just recommendations to our peer. We - // only check they are not crazy in ProtocolParameters::validate(). - default: - break; - } - } + const auto h2Config = m_connection->http2Parameters(); + maxSessionReceiveWindowSize = h2Config.sessionReceiveWindowSize(); + pushPromiseEnabled = h2Config.serverPushEnabled(); + streamInitialReceiveWindowSize = h2Config.streamReceiveWindowSize(); + encoder.setCompressStrings(h2Config.huffmanCompressionEnabled()); if (!channel->ssl && m_connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { // We upgraded from HTTP/1.1 to HTTP/2. channel->request was already sent @@ -422,20 +406,17 @@ bool QHttp2ProtocolHandler::sendClientPreface() return false; // 6.5 SETTINGS - const ProtocolParameters params(m_connection->http2Parameters()); - Q_ASSERT(params.validate()); - frameWriter.setOutboundFrame(params.settingsFrame()); + frameWriter.setOutboundFrame(Http2::configurationToSettingsFrame(m_connection->http2Parameters())); Q_ASSERT(frameWriter.outboundFrame().payloadSize()); if (!frameWriter.write(*m_socket)) return false; sessionReceiveWindowSize = maxSessionReceiveWindowSize; - // ProtocolParameters::validate does not allow maxSessionReceiveWindowSize - // to be smaller than defaultSessionWindowSize, so everything is OK here with - // 'delta': + // We only send WINDOW_UPDATE for the connection if the size differs from the + // default 64 KB: const auto delta = maxSessionReceiveWindowSize - Http2::defaultSessionWindowSize; - if (!sendWINDOW_UPDATE(Http2::connectionStreamID, delta)) + if (delta && !sendWINDOW_UPDATE(Http2::connectionStreamID, delta)) return false; prefaceSent = true; @@ -1069,7 +1050,7 @@ bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 ne } if (identifier == Settings::MAX_FRAME_SIZE_ID) { - if (newValue < Http2::maxFrameSize || newValue > Http2::maxPayloadSize) { + if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) { connectionError(PROTOCOL_ERROR, "SETTGINGS max frame size is out of range"); return false; } diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h index b582123961..1943827e23 100644 --- a/src/network/access/qhttp2protocolhandler_p.h +++ b/src/network/access/qhttp2protocolhandler_p.h @@ -55,6 +55,8 @@ #include <private/qabstractprotocolhandler_p.h> #include <private/qhttpnetworkrequest_p.h> +#include <access/qhttp2configuration.h> + #include <private/http2protocol_p.h> #include <private/http2streams_p.h> #include <private/http2frames_p.h> @@ -163,8 +165,9 @@ private: static const std::deque<quint32>::size_type maxRecycledStreams; std::deque<quint32> recycledStreams; - // Peer's max frame size. - quint32 maxFrameSize = Http2::maxFrameSize; + // Peer's max frame size (this min is the default value + // we start with, that can be updated by SETTINGS frame): + quint32 maxFrameSize = Http2::minPayloadLimit; Http2::FrameReader frameReader; Http2::Frame inboundFrame; @@ -176,28 +179,28 @@ private: // Control flow: - // This is how many concurrent streams our peer expects from us: - // 100 is the default value, can be updated by the server's SETTINGS - // frame(s): + // This is how many concurrent streams our peer allows us, 100 is the + // initial value, can be updated by the server's SETTINGS frame(s): quint32 maxConcurrentStreams = Http2::maxConcurrentStreams; // While we allow sending SETTTINGS_MAX_CONCURRENT_STREAMS to limit our peer, // it's just a hint and we do not actually enforce it (and we can continue // sending requests and creating streams while maxConcurrentStreams allows). - // This is the max value, we set it in a ctor from Http2::ProtocolParameters, - // it does not change after that. + // This is our (client-side) maximum possible receive window size, we set + // it in a ctor from QHttp2Configuration, it does not change after that. + // The default is 64Kb: qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize; - // Our session receive window size, default is 64Kb. We'll update it from QNAM's - // Http2::ProtocolParameters. Signed integer since it can become negative + // Our session current receive window size, updated in a ctor from + // QHttp2Configuration. Signed integer since it can become negative // (it's still a valid window size). qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize; // Our per-stream receive window size, default is 64 Kb, will be updated - // from QNAM's Http2::ProtocolParameters. Again, signed - can become negative. + // from QHttp2Configuration. Again, signed - can become negative. qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize; // These are our peer's receive window sizes, they will be updated by the - // peer's SETTINGS and WINDOW_UPDATE frames. + // peer's SETTINGS and WINDOW_UPDATE frames, defaults presumed to be 64Kb. qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize; qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize; diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 1f1de478ea..13be1aa6b5 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -1456,21 +1456,16 @@ void QHttpNetworkConnection::setConnectionType(ConnectionType type) d->connectionType = type; } -Http2::ProtocolParameters QHttpNetworkConnection::http2Parameters() const +QHttp2Configuration QHttpNetworkConnection::http2Parameters() const { Q_D(const QHttpNetworkConnection); return d->http2Parameters; } -void QHttpNetworkConnection::setHttp2Parameters(const Http2::ProtocolParameters ¶ms) +void QHttpNetworkConnection::setHttp2Parameters(const QHttp2Configuration ¶ms) { Q_D(QHttpNetworkConnection); - if (params.validate()) { - d->http2Parameters = params; - } else { - qCWarning(QT_HTTP2) - << "invalid HTTP/2 parameters, falling back to defaults instead"; - } + d->http2Parameters = params; } // SSL support below diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 85d89f20c2..6808a0c0ac 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -57,6 +57,8 @@ #include <QtNetwork/qabstractsocket.h> #include <QtNetwork/qnetworksession.h> +#include <qhttp2configuration.h> + #include <private/qobject_p.h> #include <qauthenticator.h> #include <qnetworkproxy.h> @@ -142,8 +144,8 @@ public: ConnectionType connectionType(); void setConnectionType(ConnectionType type); - Http2::ProtocolParameters http2Parameters() const; - void setHttp2Parameters(const Http2::ProtocolParameters ¶ms); + QHttp2Configuration http2Parameters() const; + void setHttp2Parameters(const QHttp2Configuration ¶ms); #ifndef QT_NO_SSL void setSslConfiguration(const QSslConfiguration &config); @@ -294,7 +296,7 @@ public: QSharedPointer<QNetworkSession> networkSession; #endif - Http2::ProtocolParameters http2Parameters; + QHttp2Configuration http2Parameters; QString peerVerifyName; // If network status monitoring is enabled, we activate connectionMonitor diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 3c9d53c5b5..716ea6c8b2 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -40,6 +40,7 @@ #include "qhttpnetworkconnectionchannel_p.h" #include "qhttpnetworkconnection_p.h" +#include "qhttp2configuration.h" #include "private/qnoncontiguousbytedevice_p.h" #include <qpair.h> @@ -48,6 +49,7 @@ #include <private/qhttp2protocolhandler_p.h> #include <private/qhttpprotocolhandler_p.h> #include <private/qspdyprotocolhandler_p.h> +#include <private/http2protocol_p.h> #ifndef QT_NO_SSL # include <private/qsslsocket_p.h> @@ -947,9 +949,7 @@ void QHttpNetworkConnectionChannel::_q_connected() if (tryProtocolUpgrade) { // Let's augment our request with some magic headers and try to // switch to HTTP/2. - const Http2::ProtocolParameters params(connection->http2Parameters()); - Q_ASSERT(params.validate()); - params.addProtocolUpgradeHeaders(&request); + Http2::appendProtocolUpgradeHeaders(connection->http2Parameters(), &request); } sendRequest(); } diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index acc551a7c9..1900397eab 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -352,9 +352,9 @@ void QHttpThreadDelegate::startRequest() networkSession); #endif // QT_NO_BEARERMANAGEMENT if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 - && http2Parameters.validate()) { + || connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) { httpConnection->setHttp2Parameters(http2Parameters); - } // else we ignore invalid parameters and use our own defaults. + } #ifndef QT_NO_SSL // Set the QSslConfiguration from this QNetworkRequest. if (ssl) diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index 6184b39b30..355d1afc30 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -62,6 +62,7 @@ #include <QNetworkReply> #include "qhttpnetworkrequest_p.h" #include "qhttpnetworkconnection_p.h" +#include "qhttp2configuration.h" #include <QSharedPointer> #include <QScopedPointer> #include "private/qnoncontiguousbytedevice_p.h" @@ -116,7 +117,7 @@ public: qint64 removedContentLength; QNetworkReply::NetworkError incomingErrorCode; QString incomingErrorDetail; - Http2::ProtocolParameters http2Parameters; + QHttp2Configuration http2Parameters; #ifndef QT_NO_BEARERMANAGEMENT QSharedPointer<QNetworkSession> networkSession; #endif diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index 29192ae7b0..fdc3cd3b3a 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -70,6 +70,7 @@ #include "QtNetwork/private/qauthenticator_p.h" #include "QtNetwork/qsslconfiguration.h" #include "QtNetwork/qnetworkconfigmanager.h" +#include "QtNetwork/private/http2protocol_p.h" #if QT_CONFIG(http) #include "qhttpmultipart.h" @@ -489,6 +490,7 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent) qRegisterMetaType<QSharedPointer<char> >(); Q_D(QNetworkAccessManager); + if (QNetworkStatusMonitor::isEnabled()) { connect(&d->statusMonitor, SIGNAL(onlineStateChanged(bool)), SLOT(_q_onlineStateChanged(bool))); @@ -1178,7 +1180,6 @@ QSharedPointer<QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession( #endif // QT_NO_BEARERMANAGEMENT - #ifndef QT_NO_SSL /*! \since 5.2 diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index b9651b35d2..b3dec282b0 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -797,10 +797,8 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq // Create the HTTP thread delegate QHttpThreadDelegate *delegate = new QHttpThreadDelegate; - // Propagate Http/2 settings if any - const QVariant blob(manager->property(Http2::http2ParametersPropertyName)); - if (blob.isValid() && blob.canConvert<Http2::ProtocolParameters>()) - delegate->http2Parameters = blob.value<Http2::ProtocolParameters>(); + // Propagate Http/2 settings: + delegate->http2Parameters = request.http2Configuration(); #ifndef QT_NO_BEARERMANAGEMENT if (!QNetworkStatusMonitor::isEnabled()) delegate->networkSession = managerPrivate->getNetworkSession(); @@ -1054,59 +1052,39 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d) if (!q->isOpen()) return; - int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1; + if (cacheEnabled && isCachingAllowed() && !cacheSaveDevice) + initCacheSaveDevice(); + + // This is going to look a little strange. When downloading data while a + // HTTP redirect is happening (and enabled), we write the redirect + // response to the cache. However, we do not append it to our internal + // buffer as that will contain the response data only for the final + // response + if (cacheSaveDevice) + cacheSaveDevice->write(d); + + if (!isHttpRedirectResponse()) { + buffer.append(d); + bytesDownloaded += d.size(); + } + bytesBuffered += d.size(); + int pendingSignals = pendingDownloadDataEmissions->fetchAndSubAcquire(1) - 1; if (pendingSignals > 0) { // Some more signal emissions to this slot are pending. // Instead of writing the downstream data, we wait // and do it in the next call we get // (signal comppression) - pendingDownloadData.append(d); return; } - pendingDownloadData.append(d); - d.clear(); - // We need to usa a copy for calling writeDownstreamData as we could - // possibly recurse into this this function when we call - // appendDownstreamDataSignalEmissions because the user might call - // processEvents() or spin an event loop when this occur. - QByteDataBuffer pendingDownloadDataCopy = pendingDownloadData; - pendingDownloadData.clear(); - - if (cacheEnabled && isCachingAllowed() && !cacheSaveDevice) { - initCacheSaveDevice(); - } - - qint64 bytesWritten = 0; - for (int i = 0; i < pendingDownloadDataCopy.bufferCount(); i++) { - QByteArray const &item = pendingDownloadDataCopy[i]; - - // This is going to look a little strange. When downloading data while a - // HTTP redirect is happening (and enabled), we write the redirect - // response to the cache. However, we do not append it to our internal - // buffer as that will contain the response data only for the final - // response - if (cacheSaveDevice) - cacheSaveDevice->write(item.constData(), item.size()); - - if (!isHttpRedirectResponse()) - buffer.append(item); - - bytesWritten += item.size(); - } - bytesBuffered += bytesWritten; - pendingDownloadDataCopy.clear(); + if (isHttpRedirectResponse()) + return; QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader); if (preMigrationDownloaded != Q_INT64_C(-1)) totalSize = totalSize.toLongLong() + preMigrationDownloaded; - if (isHttpRedirectResponse()) - return; - - bytesDownloaded += bytesWritten; - emit q->readyRead(); // emit readyRead before downloadProgress incase this will cause events to be // processed and we get into a recursive call (as in QProgressDialog). diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h index f5f01d0811..ef69ce0653 100644 --- a/src/network/access/qnetworkreplyhttpimpl_p.h +++ b/src/network/access/qnetworkreplyhttpimpl_p.h @@ -63,7 +63,6 @@ #include <QtNetwork/QNetworkCacheMetaData> #include <private/qhttpnetworkrequest_p.h> -#include <private/qbytedata_p.h> #include <private/qnetworkreply_p.h> #include <QtNetwork/QNetworkProxy> #include <QtNetwork/QNetworkSession> @@ -248,7 +247,6 @@ public: quint64 resumeOffset; qint64 preMigrationDownloaded; - QByteDataBuffer pendingDownloadData; // For signal compression qint64 bytesDownloaded; qint64 bytesBuffered; diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 37f64c3f52..118fb6b1fb 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -42,6 +42,10 @@ #include "qplatformdefs.h" #include "qnetworkcookie.h" #include "qsslconfiguration.h" +#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) +#include "qhttp2configuration.h" +#include "private/http2protocol_p.h" +#endif #include "QtCore/qshareddata.h" #include "QtCore/qlocale.h" #include "QtCore/qdatetime.h" @@ -445,6 +449,9 @@ public: sslConfiguration = new QSslConfiguration(*other.sslConfiguration); #endif peerVerifyName = other.peerVerifyName; +#if QT_CONFIG(http) + h2Configuration = other.h2Configuration; +#endif } inline bool operator==(const QNetworkRequestPrivate &other) const @@ -454,7 +461,11 @@ public: rawHeaders == other.rawHeaders && attributes == other.attributes && maxRedirectsAllowed == other.maxRedirectsAllowed && - peerVerifyName == other.peerVerifyName; + peerVerifyName == other.peerVerifyName +#if QT_CONFIG(http) + && h2Configuration == other.h2Configuration +#endif + ; // don't compare cookedHeaders } @@ -465,6 +476,9 @@ public: #endif int maxRedirectsAllowed; QString peerVerifyName; +#if QT_CONFIG(http) + QHttp2Configuration h2Configuration; +#endif }; /*! @@ -476,6 +490,15 @@ public: QNetworkRequest::QNetworkRequest() : d(new QNetworkRequestPrivate) { +#if QT_CONFIG(http) + // Initial values proposed by RFC 7540 are quite draconian, + // so unless an application will set its own parameters, we + // make stream window size larger and increase (via WINDOW_UPDATE) + // the session window size. These are our 'defaults': + d->h2Configuration.setStreamReceiveWindowSize(Http2::qtDefaultStreamReceiveWindowSize); + d->h2Configuration.setSessionReceiveWindowSize(Http2::maxSessionReceiveWindowSize); + d->h2Configuration.setServerPushEnabled(false); +#endif // QT_CONFIG(http) } /*! @@ -835,6 +858,52 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName) d->peerVerifyName = peerName; } +#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) +/*! + \since 5.14 + + Returns the current parameters that QNetworkAccessManager is + using for this request and its underlying HTTP/2 connection. + This is either a configuration previously set by an application + or a default configuration. + + The default values that QNetworkAccessManager is using are: + + \list + \li Window size for connection-level flowcontrol is 2147483647 octets + \li Window size for stream-level flowcontrol is 21474836 octets + \li Max frame size is 16384 + \endlist + + By default, server push is disabled, Huffman compression and + string indexing are enabled. + + \sa setHttp2Configuration +*/ +QHttp2Configuration QNetworkRequest::http2Configuration() const +{ + return d->h2Configuration; +} + +/*! + \since 5.14 + + Sets request's HTTP/2 parameters from \a configuration. + + \note The configuration must be set prior to making a request. + \note HTTP/2 multiplexes several streams in a single HTTP/2 + connection. This implies that QNetworkAccessManager will use + the configuration found in the first request from a series + of requests sent to the same host. + + \sa http2Configuration, QNetworkAccessManager, QHttp2Configuration +*/ +void QNetworkRequest::setHttp2Configuration(const QHttp2Configuration &configuration) +{ + d->h2Configuration = configuration; +} +#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) + static QByteArray headerName(QNetworkRequest::KnownHeaders header) { switch (header) { diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index 8ad4ab41c0..e09ff8aaae 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE class QSslConfiguration; +class QHttp2Configuration; class QNetworkRequestPrivate; class Q_NETWORK_EXPORT QNetworkRequest @@ -175,6 +176,10 @@ public: QString peerVerifyName() const; void setPeerVerifyName(const QString &peerName); +#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) + QHttp2Configuration http2Configuration() const; + void setHttp2Configuration(const QHttp2Configuration &configuration); +#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) private: QSharedDataPointer<QNetworkRequestPrivate> d; friend class QNetworkRequestPrivate; diff --git a/src/platformsupport/kmsconvenience/qkmsdevice.cpp b/src/platformsupport/kmsconvenience/qkmsdevice.cpp index 07ef60c5ff..d9d76c1146 100644 --- a/src/platformsupport/kmsconvenience/qkmsdevice.cpp +++ b/src/platformsupport/kmsconvenience/qkmsdevice.cpp @@ -854,6 +854,8 @@ void QKmsDevice::discoverPlanes() plane.crtcYPropertyId = prop->prop_id; } else if (!strcasecmp(prop->name, "zpos")) { plane.zposPropertyId = prop->prop_id; + } else if (!strcasecmp(prop->name, "blend_op")) { + plane.blendOpPropertyId = prop->prop_id; } }); diff --git a/src/platformsupport/kmsconvenience/qkmsdevice_p.h b/src/platformsupport/kmsconvenience/qkmsdevice_p.h index 403972fbb8..77070c293d 100644 --- a/src/platformsupport/kmsconvenience/qkmsdevice_p.h +++ b/src/platformsupport/kmsconvenience/qkmsdevice_p.h @@ -178,6 +178,7 @@ struct QKmsPlane uint32_t crtcwidthPropertyId = 0; uint32_t crtcheightPropertyId = 0; uint32_t zposPropertyId = 0; + uint32_t blendOpPropertyId = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QKmsPlane::Rotations) diff --git a/src/plugins/bearer/android/jar/jar.pro b/src/plugins/bearer/android/jar/jar.pro index f988019dac..8277a8abc1 100644 --- a/src/plugins/bearer/android/jar/jar.pro +++ b/src/plugins/bearer/android/jar/jar.pro @@ -1,3 +1,5 @@ +CONFIG += single_arch + TARGET = QtAndroidBearer load(qt_build_paths) diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.cpp b/src/plugins/imageformats/jpeg/qjpeghandler.cpp index 0fb21df1d3..1f1675e490 100644 --- a/src/plugins/imageformats/jpeg/qjpeghandler.cpp +++ b/src/plugins/imageformats/jpeg/qjpeghandler.cpp @@ -492,6 +492,8 @@ inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device) free_in_buffer = max_buf; } +static constexpr int maxMarkerSize = 65533; + static inline void set_text(const QImage &image, j_compress_ptr cinfo, const QString &description) { const QMap<QString, QString> text = qt_getImageText(image, description); @@ -500,12 +502,33 @@ static inline void set_text(const QImage &image, j_compress_ptr cinfo, const QSt if (!comment.isEmpty()) comment += ": "; comment += it.value().toUtf8(); - if (comment.length() > 65530) - comment.truncate(65530); + if (comment.length() > maxMarkerSize) + comment.truncate(maxMarkerSize); jpeg_write_marker(cinfo, JPEG_COM, (const JOCTET *)comment.constData(), comment.size()); } } +static inline void write_icc_profile(const QImage &image, j_compress_ptr cinfo) +{ + const QByteArray iccProfile = image.colorSpace().iccProfile(); + if (iccProfile.isEmpty()) + return; + + const QByteArray iccSignature("ICC_PROFILE", 12); + constexpr int maxIccMarkerSize = maxMarkerSize - (12 + 2); + int index = 0; + const int markers = (iccProfile.size() + (maxIccMarkerSize - 1)) / maxIccMarkerSize; + Q_ASSERT(markers < 256); + for (int marker = 1; marker <= markers; ++marker) { + const int len = std::min(iccProfile.size() - index, maxIccMarkerSize); + const QByteArray block = iccSignature + + QByteArray(1, char(marker)) + QByteArray(1, char(markers)) + + iccProfile.mid(index, len); + jpeg_write_marker(cinfo, JPEG_APP0 + 2, reinterpret_cast<const JOCTET *>(block.constData()), block.size()); + index += len; + } +} + static bool write_jpeg_image(const QImage &image, QIODevice *device, volatile int sourceQuality, const QString &description, bool optimize, bool progressive) { bool success = false; @@ -586,6 +609,8 @@ static bool write_jpeg_image(const QImage &image, QIODevice *device, volatile in jpeg_start_compress(&cinfo, TRUE); set_text(image, &cinfo, description); + if (cinfo.in_color_space == JCS_RGB) + write_icc_profile(image, &cinfo); row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components]; int w = cinfo.image_width; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp index 09a10bcc9c..2034632fb3 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp @@ -331,6 +331,9 @@ void QEglFSKmsGbmScreen::flip() static int zpos = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_ZPOS"); if (zpos) drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->zposPropertyId, zpos); + static uint blendOp = uint(qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_BLEND_OP")); + if (blendOp) + drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->blendOpPropertyId, blendOp); } #endif } else { diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp index dc7ea08dc5..cb8962d4b8 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp @@ -188,7 +188,7 @@ static QImage::Format determineFormat(const fb_var_screeninfo &info, int depth) if (memcmp(rgba, rgb888, 3 * sizeof(fb_bitfield)) == 0) { format = QImage::Format_RGB888; } else if (memcmp(rgba, bgr888, 3 * sizeof(fb_bitfield)) == 0) { - format = QImage::Format_RGB888; + format = QImage::Format_BGR888; // pixeltype = BGRPixel; } break; diff --git a/src/plugins/printsupport/cups/qppdprintdevice.cpp b/src/plugins/printsupport/cups/qppdprintdevice.cpp index ea6336c4d1..90411f2a2c 100644 --- a/src/plugins/printsupport/cups/qppdprintdevice.cpp +++ b/src/plugins/printsupport/cups/qppdprintdevice.cpp @@ -41,7 +41,9 @@ #include "qcupsprintersupport_p.h" +#if QT_CONFIG(mimetype) #include <QtCore/QMimeDatabase> +#endif #include <qdebug.h> #include "private/qcups_p.h" // Only needed for PDPK_* diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 10bbd59bfb..4c7b8a6917 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -91,7 +91,7 @@ FILE *openProcess(const QString &command) #if defined(Q_OS_WIN32) QString processedCommand = QLatin1Char('\"') + command + QLatin1Char('\"'); #else - QString processedCommand = command; + const QString& processedCommand = command; #endif return popen(processedCommand.toLocal8Bit().constData(), QT_POPEN_READ); @@ -99,7 +99,7 @@ FILE *openProcess(const QString &command) struct QtDependency { - QtDependency(QString rpath, QString apath) : relativePath(rpath), absolutePath(apath) {} + QtDependency(const QString &rpath, const QString &apath) : relativePath(rpath), absolutePath(apath) {} bool operator==(const QtDependency &other) const { @@ -127,7 +127,6 @@ struct Options , sectionsOnly(false) , protectedAuthenticationPath(false) , jarSigner(false) - , gdbServer(Auto) , installApk(false) , uninstallApk(false) {} @@ -150,7 +149,6 @@ struct Options bool generateAssetsFileList; bool build; bool auxMode; - bool stripLibraries = true; ActionTimer timer; // External tools @@ -175,22 +173,22 @@ struct Options // lib c++ path QString stdCppPath; - QString stdCppName = QStringLiteral("gnustl_shared"); + QString stdCppName = QStringLiteral("c++_shared"); // Build information QString androidPlatform; - QString architecture; - QString toolchainVersion; + QHash<QString, QString> architectures; + QString currentArchitecture; QString toolchainPrefix; - QString toolPrefix; - bool useLLVM = false; QString ndkHost; // Package information DeploymentMechanism deploymentMechanism; QString packageName; QStringList extraLibs; + QHash<QString, QStringList> archExtraLibs; QStringList extraPlugins; + QHash<QString, QStringList> archExtraPlugins; // Signing information bool releasePackage; @@ -211,25 +209,36 @@ struct Options bool jarSigner; QString apkPath; - // Gdbserver - TriState gdbServer; - // Installation information bool installApk; bool uninstallApk; QString installLocation; - // Collected information + // Per architecture collected information + void clear(const QString &arch) + { + currentArchitecture = arch; + } typedef QPair<QString, QString> BundledFile; - QList<BundledFile> bundledFiles; - QList<QtDependency> qtDependencies; - QStringList localLibs; + QHash<QString, QList<BundledFile>> bundledFiles; + QHash<QString, QList<QtDependency>> qtDependencies; + QHash<QString, QStringList> localLibs; + bool usesOpenGL = false; + + // Per package collected information QStringList localJars; QStringList initClasses; QStringList permissions; QStringList features; }; +static const QHash<QByteArray, QByteArray> elfArchitecures = { + {"aarch64", "arm64-v8a"}, + {"arm", "armeabi-v7a"}, + {"i386", "x86"}, + {"x86_64", "x86_64"} +}; + // Copy-pasted from qmake/library/ioutil.cpp inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16]) { @@ -301,6 +310,59 @@ static QString shellQuote(const QString &arg) return shellQuoteUnix(arg); } +QString architecureFromName(const QString &name) +{ + QRegExp architecture(QStringLiteral(".*_(.*)\\.so")); + if (!architecture.exactMatch(name)) + return {}; + return architecture.capturedTexts().last(); +} + +QString fileArchitecture(const Options &options, const QString &path) +{ + auto arch = architecureFromName(path); + if (!arch.isEmpty()) + return arch; + + QString readElf = QStringLiteral("%1/toolchains/%2/prebuilt/%3/bin/llvm-readobj").arg(options.ndkPath, + options.toolchainPrefix, + options.ndkHost); +#if defined(Q_OS_WIN32) + readElf += QStringLiteral(".exe"); +#endif + + if (!QFile::exists(readElf)) { + fprintf(stderr, "Command does not exist: %s\n", qPrintable(readElf)); + return {}; + } + + readElf = QStringLiteral("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(path)); + + FILE *readElfCommand = openProcess(readElf); + if (!readElfCommand) { + fprintf(stderr, "Cannot execute command %s\n", qPrintable(readElf)); + return {}; + } + + char buffer[512]; + while (fgets(buffer, sizeof(buffer), readElfCommand) != nullptr) { + QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer)); + QString library; + line = line.trimmed(); + if (line.startsWith("Arch: ")) { + auto it = elfArchitecures.find(line.mid(6)); + pclose(readElfCommand); + return it != elfArchitecures.constEnd() ? QString::fromLatin1(it.value()) : QString{}; + } + } + pclose(readElfCommand); + return {}; +} + +bool checkArchitecture(const Options &options, const QString &fileName) +{ + return fileArchitecture(options, fileName) == options.currentArchitecture; +} void deleteMissingFiles(const Options &options, const QDir &srcDir, const QDir &dstDir) { @@ -388,10 +450,6 @@ Options parseOptions() options.installLocation = arguments.at(++i); } else if (argument.compare(QLatin1String("--release"), Qt::CaseInsensitive) == 0) { options.releasePackage = true; - } else if (argument.compare(QLatin1String("--gdbserver"), Qt::CaseInsensitive) == 0) { - options.gdbServer = Options::True; - } else if (argument.compare(QLatin1String("--no-gdbserver"), Qt::CaseInsensitive) == 0) { - options.gdbServer = Options::False; } else if (argument.compare(QLatin1String("--jdk"), Qt::CaseInsensitive) == 0) { if (i + 1 == arguments.size()) options.helpRequested = true; @@ -462,8 +520,6 @@ Options parseOptions() options.generateAssetsFileList = false; } else if (argument.compare(QLatin1String("--aux-mode"), Qt::CaseInsensitive) == 0) { options.auxMode = true; - } else if (argument.compare(QLatin1String("--no-strip"), Qt::CaseInsensitive) == 0) { - options.stripLibraries = false; } } @@ -533,10 +589,6 @@ void printHelp() " --protected: Keystore has protected authentication path.\n" " --jarsigner: Force jarsigner usage, otherwise apksigner will be\n" " used if available.\n" - " --gdbserver: Adds the gdbserver to the package. By default the gdbserver\n" - " is bundled for debug pacakges.\n" - " --no-gdbserver: Prevents the gdbserver from being added to the package\n" - " By default the gdbserver is bundled for debug pacakges.\n" " --jdk <path/to/jdk>: Used to find the jarsigner tool when used\n" " in combination with the --release argument. By default,\n" " an attempt is made to detect the tool using the JAVA_HOME and\n" @@ -549,7 +601,6 @@ void printHelp() " --aux-mode: Operate in auxiliary mode. This will only copy the\n" " dependencies into the build directory and update the XML templates.\n" " The project will not be built or installed.\n" - " --no-strip: Do not strip debug symbols from libraries.\n" " --apk <path/where/to/copy/the/apk>: Path where to copy the built apk.\n" " --help: Displays this information.\n\n", qPrintable(QCoreApplication::arguments().at(0)) @@ -580,9 +631,10 @@ bool alwaysOverwritableFile(const QString &fileName) || fileName.endsWith(QLatin1String("/src/org/qtproject/qt5/android/bindings/QtActivity.java"))); } + bool copyFileIfNewer(const QString &sourceFileName, const QString &destinationFileName, - bool verbose, + const Options &options, bool forceOverwrite = false) { if (QFile::exists(destinationFileName)) { @@ -592,7 +644,7 @@ bool copyFileIfNewer(const QString &sourceFileName, if (!forceOverwrite && sourceFileInfo.lastModified() <= destinationFileInfo.lastModified() && !alwaysOverwritableFile(destinationFileName)) { - if (verbose) + if (options.verbose) fprintf(stdout, " -- Skipping file %s. Same or newer file already in place.\n", qPrintable(sourceFileName)); return true; } else { @@ -611,11 +663,10 @@ bool copyFileIfNewer(const QString &sourceFileName, if (!QFile::exists(destinationFileName) && !QFile::copy(sourceFileName, destinationFileName)) { fprintf(stderr, "Failed to copy %s to %s.\n", qPrintable(sourceFileName), qPrintable(destinationFileName)); return false; - } else if (verbose) { + } else if (options.verbose) { fprintf(stdout, " -- Copied %s\n", qPrintable(destinationFileName)); fflush(stdout); } - return true; } @@ -666,7 +717,7 @@ QString cleanPackageName(QString packageName) } } if (keywords.contains(word)) { - packageName.insert(next, QLatin1String("_")); + packageName.insert(next, QStringLiteral("_")); index = next + 1; } else { index = next; @@ -678,7 +729,7 @@ QString cleanPackageName(QString packageName) QString detectLatestAndroidPlatform(const QString &sdkPath) { - QDir dir(sdkPath + QLatin1String("/platforms")); + QDir dir(sdkPath + QStringLiteral("/platforms")); if (!dir.exists()) { fprintf(stderr, "Directory %s does not exist\n", qPrintable(dir.absolutePath())); return QString(); @@ -795,53 +846,22 @@ bool readInputFile(Options *options) } { - const QJsonValue applicationBinary = jsonObject.value(QStringLiteral("application-binary")); - if (applicationBinary.isUndefined()) { - fprintf(stderr, "No application binary defined in json file.\n"); - return false; - } - options->applicationBinary = applicationBinary.toString(); - - if (!QFile::exists(options->applicationBinary)) { - fprintf(stderr, "Cannot find application binary %s.\n", qPrintable(options->applicationBinary)); + const QJsonObject targetArchitectures = jsonObject.value(QStringLiteral("architectures")).toObject(); + if (targetArchitectures.isEmpty()) { + fprintf(stderr, "No target architecture defined in json file.\n"); return false; } - } - - { - const QJsonValue deploymentDependencies = jsonObject.value(QStringLiteral("deployment-dependencies")); - if (!deploymentDependencies.isUndefined()) { - QString deploymentDependenciesString = deploymentDependencies.toString(); - const auto dependencies = deploymentDependenciesString.splitRef(QLatin1Char(',')); - for (const QStringRef &dependency : dependencies) { - QString path = options->qtInstallDirectory + QLatin1Char('/') + dependency; - if (QFileInfo(path).isDir()) { - QDirIterator iterator(path, QDirIterator::Subdirectories); - while (iterator.hasNext()) { - iterator.next(); - if (iterator.fileInfo().isFile()) { - QString subPath = iterator.filePath(); - options->qtDependencies.append(QtDependency(subPath.mid(options->qtInstallDirectory.length() + 1), - subPath)); - } - } - } else { - options->qtDependencies.append(QtDependency(dependency.toString(), path)); - } + for (auto it = targetArchitectures.constBegin(); it != targetArchitectures.constEnd(); ++it) { + if (it.value().isUndefined()) { + fprintf(stderr, "Invalid architecure.\n"); + return false; } + if (it.value().isNull()) + continue; + options->architectures.insert(it.key(), it.value().toString()); } } - - { - const QJsonValue targetArchitecture = jsonObject.value(QStringLiteral("target-architecture")); - if (targetArchitecture.isUndefined()) { - fprintf(stderr, "No target architecture defined in json file.\n"); - return false; - } - options->architecture = targetArchitecture.toString(); - } - { const QJsonValue ndk = jsonObject.value(QStringLiteral("ndk")); if (ndk.isUndefined()) { @@ -852,11 +872,6 @@ bool readInputFile(Options *options) } { - const QJsonValue value = jsonObject.value(QStringLiteral("useLLVM")); - options->useLLVM = value.toBool(false); - } - - { const QJsonValue toolchainPrefix = jsonObject.value(QStringLiteral("toolchain-prefix")); if (toolchainPrefix.isUndefined()) { fprintf(stderr, "No toolchain prefix defined in json file.\n"); @@ -866,25 +881,6 @@ bool readInputFile(Options *options) } { - const QJsonValue toolPrefix = jsonObject.value(QStringLiteral("tool-prefix")); - if (toolPrefix.isUndefined()) { - fprintf(stderr, "Warning: No tool prefix defined in json file.\n"); - options->toolPrefix = options->toolchainPrefix; - } else { - options->toolPrefix = toolPrefix.toString(); - } - } - - if (!options->useLLVM) { - const QJsonValue toolchainVersion = jsonObject.value(QStringLiteral("toolchain-version")); - if (toolchainVersion.isUndefined()) { - fprintf(stderr, "No toolchain version defined in json file.\n"); - return false; - } - options->toolchainVersion = toolchainVersion.toString(); - } - - { const QJsonValue ndkHost = jsonObject.value(QStringLiteral("ndk-host")); if (ndkHost.isUndefined()) { fprintf(stderr, "No NDK host defined in json file.\n"); @@ -893,10 +889,6 @@ bool readInputFile(Options *options) options->ndkHost = ndkHost.toString(); } - options->packageName = packageNameFromAndroidManifest(options->androidSourceDirectory + QLatin1String("/AndroidManifest.xml")); - if (options->packageName.isEmpty()) - options->packageName = cleanPackageName(QString::fromLatin1("org.qtproject.example.%1").arg(QFileInfo(options->applicationBinary).baseName().mid(sizeof("lib") - 1))); - { const QJsonValue extraLibs = jsonObject.value(QStringLiteral("android-extra-libs")); if (!extraLibs.isUndefined()) @@ -916,12 +908,6 @@ bool readInputFile(Options *options) return false; } options->stdCppPath = stdcppPath.toString(); - auto name = QFileInfo(options->stdCppPath).baseName(); - if (!name.startsWith(QLatin1String("lib"))) { - fprintf(stderr, "Invalid STD C++ library name.\n"); - return false; - } - options->stdCppName = name.mid(3); } { @@ -935,10 +921,68 @@ bool readInputFile(Options *options) if (!qmlImportPaths.isUndefined()) options->qmlImportPaths = qmlImportPaths.toString().split(QLatin1Char(',')); } + + { + const QJsonValue applicationBinary = jsonObject.value(QStringLiteral("application-binary")); + if (applicationBinary.isUndefined()) { + fprintf(stderr, "No application binary defined in json file.\n"); + return false; + } + options->applicationBinary = applicationBinary.toString(); + if (options->build) { + for (auto it = options->architectures.constBegin(); it != options->architectures.constEnd(); ++it) { + if (!QFile::exists(QStringLiteral("%1/libs/%2/lib%3_%2.so").arg(options->outputDirectory, it.key(), options->applicationBinary))) { + fprintf(stderr, "Cannot find application binary %s.\n", qPrintable(options->applicationBinary)); + return false; + } + } + } + } + + { + const QJsonValue deploymentDependencies = jsonObject.value(QStringLiteral("deployment-dependencies")); + if (!deploymentDependencies.isUndefined()) { + QString deploymentDependenciesString = deploymentDependencies.toString(); + const auto dependencies = deploymentDependenciesString.splitRef(QLatin1Char(',')); + for (const QStringRef &dependency : dependencies) { + QString path = options->qtInstallDirectory + QLatin1Char('/') + dependency; + if (QFileInfo(path).isDir()) { + QDirIterator iterator(path, QDirIterator::Subdirectories); + while (iterator.hasNext()) { + iterator.next(); + if (iterator.fileInfo().isFile()) { + QString subPath = iterator.filePath(); + auto arch = fileArchitecture(*options, subPath); + if (!arch.isEmpty()) { + options->qtDependencies[arch].append(QtDependency(subPath.mid(options->qtInstallDirectory.length() + 1), + subPath)); + } else if (options->verbose) { + fprintf(stderr, "Skipping \"%s\", unknown architecture\n", qPrintable(subPath)); + fflush(stderr); + } + } + } + } else { + auto arch = fileArchitecture(*options, path); + if (!arch.isEmpty()) { + options->qtDependencies[arch].append(QtDependency(dependency.toString(), path)); + } else if (options->verbose) { + fprintf(stderr, "Skipping \"%s\", unknown architecture\n", qPrintable(path)); + fflush(stderr); + } + } + } + } + } + + options->packageName = packageNameFromAndroidManifest(options->androidSourceDirectory + QStringLiteral("/AndroidManifest.xml")); + if (options->packageName.isEmpty()) + options->packageName = cleanPackageName(QStringLiteral("org.qtproject.example.%1").arg(options->applicationBinary)); + return true; } -bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, bool verbose, bool forceOverwrite = false) +bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, const Options &options, bool forceOverwrite = false) { const QFileInfoList entries = sourceDirectory.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); for (const QFileInfo &entry : entries) { @@ -949,11 +993,11 @@ bool copyFiles(const QDir &sourceDirectory, const QDir &destinationDirectory, bo return false; } - if (!copyFiles(dir, QDir(destinationDirectory.path() + QLatin1String("/") + dir.dirName()), verbose, forceOverwrite)) + if (!copyFiles(dir, QDir(destinationDirectory.path() + QStringLiteral("/") + dir.dirName()), options, forceOverwrite)) return false; } else { QString destination = destinationDirectory.absoluteFilePath(entry.fileName()); - if (!copyFileIfNewer(entry.absoluteFilePath(), destination, verbose, forceOverwrite)) + if (!copyFileIfNewer(entry.absoluteFilePath(), destination, options, forceOverwrite)) return false; } } @@ -993,7 +1037,7 @@ bool copyAndroidTemplate(const Options &options, const QString &androidTemplate, return false; } - return copyFiles(sourceDirectory, QDir(outDir), options.verbose); + return copyFiles(sourceDirectory, QDir(outDir), options); } bool copyGradleTemplate(const Options &options) @@ -1010,7 +1054,7 @@ bool copyGradleTemplate(const Options &options) return false; } - return copyFiles(sourceDirectory, QDir(outDir), options.verbose); + return copyFiles(sourceDirectory, QDir(outDir), options); } bool copyAndroidTemplate(const Options &options) @@ -1041,38 +1085,42 @@ bool copyAndroidSources(const Options &options) return false; } - return copyFiles(sourceDirectory, QDir(options.outputDirectory), options.verbose, true); + return copyFiles(sourceDirectory, QDir(options.outputDirectory), options, true); } -bool copyAndroidExtraLibs(const Options &options) +bool copyAndroidExtraLibs(Options *options) { - if (options.extraLibs.isEmpty()) + if (options->extraLibs.isEmpty()) return true; - if (options.verbose) - fprintf(stdout, "Copying %d external libraries to package.\n", options.extraLibs.size()); + if (options->verbose) + fprintf(stdout, "Copying %d external libraries to package.\n", options->extraLibs.size()); - for (const QString &extraLib : options.extraLibs) { + for (const QString &extraLib : options->extraLibs) { QFileInfo extraLibInfo(extraLib); if (!extraLibInfo.exists()) { fprintf(stderr, "External library %s does not exist!\n", qPrintable(extraLib)); return false; } - - if (!extraLibInfo.fileName().startsWith(QLatin1String("lib")) || extraLibInfo.suffix() != QLatin1String("so")) { + if (!checkArchitecture(*options, extraLibInfo.filePath())) { + if (options->verbose) + fprintf(stdout, "Skipping \"%s\", architecture mismatch.\n", qPrintable(extraLib)); + continue; + } + if (!extraLibInfo.fileName().startsWith(QStringLiteral("lib")) || extraLibInfo.suffix() != QStringLiteral("so")) { fprintf(stderr, "The file name of external library %s must begin with \"lib\" and end with the suffix \".so\".\n", qPrintable(extraLib)); return false; } - - QString destinationFile(options.outputDirectory - + QLatin1String("/libs/") - + options.architecture + QString destinationFile(options->outputDirectory + + QStringLiteral("/libs/") + + options->currentArchitecture + QLatin1Char('/') + extraLibInfo.fileName()); - if (!copyFileIfNewer(extraLib, destinationFile, options.verbose)) + if (!copyFileIfNewer(extraLib, destinationFile, *options)) return false; + options->archExtraLibs[options->currentArchitecture] += extraLib; } return true; @@ -1093,15 +1141,15 @@ QStringList allFilesInside(const QDir& current, const QDir& rootDir) return result; } -bool copyAndroidExtraResources(const Options &options) +bool copyAndroidExtraResources(Options *options) { - if (options.extraPlugins.isEmpty()) + if (options->extraPlugins.isEmpty()) return true; - if (options.verbose) - fprintf(stdout, "Copying %d external resources to package.\n", options.extraPlugins.size()); + if (options->verbose) + fprintf(stdout, "Copying %d external resources to package.\n", options->extraPlugins.size()); - for (const QString &extraResource : options.extraPlugins) { + for (const QString &extraResource : options->extraPlugins) { QFileInfo extraResourceInfo(extraResource); if (!extraResourceInfo.exists() || !extraResourceInfo.isDir()) { fprintf(stderr, "External resource %s does not exist or not a correct directory!\n", qPrintable(extraResource)); @@ -1109,20 +1157,22 @@ bool copyAndroidExtraResources(const Options &options) } QDir resourceDir(extraResource); - QString assetsDir = options.outputDirectory + QStringLiteral("/assets/") + resourceDir.dirName() + QLatin1Char('/'); - QString libsDir = options.outputDirectory + QStringLiteral("/libs/") + options.architecture + QLatin1Char('/'); + QString assetsDir = options->outputDirectory + QStringLiteral("/assets/") + resourceDir.dirName() + QLatin1Char('/'); + QString libsDir = options->outputDirectory + QStringLiteral("/libs/") + options->currentArchitecture + QLatin1Char('/'); const QStringList files = allFilesInside(resourceDir, resourceDir); for (const QString &resourceFile : files) { QString originFile(resourceDir.filePath(resourceFile)); QString destinationFile; - if (!resourceFile.endsWith(QLatin1String(".so"))) { + if (!resourceFile.endsWith(QStringLiteral(".so"))) { destinationFile = assetsDir + resourceFile; } else { + if (!checkArchitecture(*options, originFile)) + continue; destinationFile = libsDir + QStringLiteral("/lib") + QString(resourceDir.dirName() + QLatin1Char('/') + resourceFile).replace(QLatin1Char('/'), QLatin1Char('_')); + options->archExtraPlugins[options->currentArchitecture] += resourceFile; } - - if (!copyFileIfNewer(originFile, destinationFile, options.verbose)) + if (!copyFileIfNewer(originFile, destinationFile, *options)) return false; } } @@ -1174,75 +1224,118 @@ bool updateFile(const QString &fileName, const QHash<QString, QString> &replacem } -bool updateLibsXml(const Options &options) +bool updateLibsXml(Options *options) { - if (options.verbose) + if (options->verbose) fprintf(stdout, " -- res/values/libs.xml\n"); - QString fileName = options.outputDirectory + QLatin1String("/res/values/libs.xml"); + QString fileName = options->outputDirectory + QStringLiteral("/res/values/libs.xml"); if (!QFile::exists(fileName)) { fprintf(stderr, "Cannot find %s in prepared packaged. This file is required.\n", qPrintable(fileName)); return false; } - QString libsPath = QLatin1String("libs/") + options.architecture + QLatin1Char('/'); - - QString qtLibs = QLatin1String("<item>") + options.stdCppName + QLatin1String("</item>\n"); + QString qtLibs; QString bundledInLibs; QString bundledInAssets; - for (const Options::BundledFile &bundledFile : options.bundledFiles) { - if (bundledFile.second.startsWith(QLatin1String("lib/"))) { - QString s = bundledFile.second.mid(sizeof("lib/lib") - 1); - s.chop(sizeof(".so") - 1); - qtLibs += QString::fromLatin1("<item>%1</item>\n").arg(s); - } else if (bundledFile.first.startsWith(libsPath)) { - QString s = bundledFile.first.mid(libsPath.length()); - bundledInLibs += QString::fromLatin1("<item>%1:%2</item>\n") - .arg(s).arg(bundledFile.second); - } else if (bundledFile.first.startsWith(QLatin1String("assets/"))) { - QString s = bundledFile.first.mid(sizeof("assets/") - 1); - bundledInAssets += QString::fromLatin1("<item>%1:%2</item>\n") - .arg(s).arg(bundledFile.second); + QString allLocalLibs; + QString extraLibs; + + for (auto it = options->architectures.constBegin(); it != options->architectures.constEnd(); ++it) { + QString libsPath = QStringLiteral("libs/") + it.key() + QLatin1Char('/'); + + qtLibs += QStringLiteral(" <item>%1;%2</item>\n").arg(it.key(), options->stdCppName); + for (const Options::BundledFile &bundledFile : options->bundledFiles[it.key()]) { + if (bundledFile.second.startsWith(QStringLiteral("lib/"))) { + QString s = bundledFile.second.mid(sizeof("lib/lib") - 1); + s.chop(sizeof(".so") - 1); + qtLibs += QStringLiteral(" <item>%1;%2</item>\n").arg(it.key(), s); + } else if (bundledFile.first.startsWith(libsPath)) { + QString s = bundledFile.first.mid(libsPath.length()); + bundledInLibs += QString::fromLatin1(" <item>%1;%2:%3</item>\n") + .arg(it.key(), s, bundledFile.second); + } else if (bundledFile.first.startsWith(QStringLiteral("assets/"))) { + QString s = bundledFile.first.mid(sizeof("assets/") - 1); + bundledInAssets += QString::fromLatin1(" <item>%1:%2</item>\n") + .arg(s).arg(bundledFile.second); + } } - } - if (!options.extraPlugins.isEmpty()) { - for (const QString &extraRes : options.extraPlugins) { - QDir resourceDir(extraRes); - const QStringList files = allFilesInside(resourceDir, resourceDir); - for (const QString &file : files) { - QString destinationPath = resourceDir.dirName() + QLatin1Char('/') + file; - if (!file.endsWith(QLatin1String(".so"))) { - bundledInAssets += QStringLiteral("<item>%1:%1</item>\n") - .arg(destinationPath); - } else { - bundledInLibs += QStringLiteral("<item>lib%1:%2</item>\n") - .arg(QString(destinationPath).replace(QLatin1Char('/'), QLatin1Char('_'))) - .arg(destinationPath); + if (!options->archExtraPlugins[it.key()].isEmpty()) { + for (const QString &extraRes : options->archExtraPlugins[it.key()]) { + QDir resourceDir(extraRes); + const QStringList files = allFilesInside(resourceDir, resourceDir); + for (const QString &file : files) { + QString destinationPath = resourceDir.dirName() + QLatin1Char('/') + file; + if (!file.endsWith(QStringLiteral(".so"))) { + bundledInAssets += QStringLiteral(" <item>%1:%1</item>\n") + .arg(destinationPath); + } else { + bundledInLibs += QStringLiteral(" <item>%1;lib%2:%3</item>\n") + .arg(it.key(), + QString(destinationPath).replace(QLatin1Char('/'), QLatin1Char('_')), + destinationPath); + } } } } - } - QHash<QString, QString> replacements; - replacements[QLatin1String("<!-- %%INSERT_QT_LIBS%% -->")] = qtLibs; + if (!options->archExtraLibs[it.key()].isEmpty()) { + for (const QString &extraLib : options->archExtraLibs[it.key()]) { + QFileInfo extraLibInfo(extraLib); + QString name = extraLibInfo.fileName().mid(sizeof("lib") - 1); + name.chop(sizeof(".so") - 1); + extraLibs += QStringLiteral(" <item>%1;%2").arg(it.key(), name); + } + } - if (options.deploymentMechanism == Options::Bundled) { - replacements[QLatin1String("<!-- %%INSERT_BUNDLED_IN_LIB%% -->")] = bundledInLibs; - replacements[QLatin1String("<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->")] = bundledInAssets; - } + QStringList localLibs; + localLibs = options->localLibs[it.key()]; + // If .pro file overrides dependency detection, we need to see which platform plugin they picked + if (localLibs.isEmpty()) { + QString plugin; + for (const QtDependency &qtDependency : options->qtDependencies[it.key()]) { + if (qtDependency.relativePath.endsWith(QStringLiteral("libqtforandroid.so")) + || qtDependency.relativePath.endsWith(QStringLiteral("libqtforandroidGL.so"))) { + if (!plugin.isEmpty() && plugin != qtDependency.relativePath) { + fprintf(stderr, "Both platform plugins libqtforandroid.so and libqtforandroidGL.so included in package. Please include only one.\n"); + return false; + } - QString extraLibs; - if (!options.extraLibs.isEmpty()) { - for (const QString extraLib : options.extraLibs) { - QFileInfo extraLibInfo(extraLib); - QString name = extraLibInfo.fileName().mid(sizeof("lib") - 1); - name.chop(sizeof(".so") - 1); + plugin = qtDependency.relativePath; + } + if (qtDependency.relativePath.contains(QStringLiteral("libQt5OpenGL")) + || qtDependency.relativePath.contains(QStringLiteral("libQt5Quick"))) { + options->usesOpenGL |= true; + break; + } + } - extraLibs += QLatin1String("<item>") + name + QLatin1String("</item>\n"); + if (plugin.isEmpty()) { + fflush(stdout); + fprintf(stderr, "No platform plugin, neither libqtforandroid.so or libqtforandroidGL.so, included in package. Please include one.\n"); + fflush(stderr); + return false; + } + + localLibs.append(plugin); + if (options->verbose) + fprintf(stdout, " -- Using platform plugin %s\n", qPrintable(plugin)); } + allLocalLibs += QStringLiteral(" <item>%1;%2</item>\n").arg(it.key(), localLibs.join(QLatin1Char(':')) + .replace(QStringLiteral("lib/"), QString{}) + .replace(QLatin1Char('/'), QLatin1Char('_'))); + } + + QHash<QString, QString> replacements; + replacements[QStringLiteral("<!-- %%INSERT_QT_LIBS%% -->")] += qtLibs.trimmed(); + replacements[QStringLiteral("<!-- %%INSERT_LOCAL_LIBS%% -->")] = allLocalLibs.trimmed(); + replacements[QStringLiteral("<!-- %%INSERT_EXTRA_LIBS%% -->")] = extraLibs.trimmed(); + + if (options->deploymentMechanism == Options::Bundled) { + replacements[QStringLiteral("<!-- %%INSERT_BUNDLED_IN_LIB%% -->")] += bundledInLibs.trimmed(); + replacements[QStringLiteral("<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->")] += bundledInAssets.trimmed(); } - replacements[QLatin1String("<!-- %%INSERT_EXTRA_LIBS%% -->")] = extraLibs; if (!updateFile(fileName, replacements)) return false; @@ -1256,9 +1349,9 @@ bool updateStringsXml(const Options &options) fprintf(stdout, " -- res/values/strings.xml\n"); QHash<QString, QString> replacements; - replacements[QStringLiteral("<!-- %%INSERT_APP_NAME%% -->")] = QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1); + replacements[QStringLiteral("<!-- %%INSERT_APP_NAME%% -->")] = options.applicationBinary; - QString fileName = options.outputDirectory + QLatin1String("/res/values/strings.xml"); + QString fileName = options.outputDirectory + QStringLiteral("/res/values/strings.xml"); if (!QFile::exists(fileName)) { if (options.verbose) fprintf(stdout, " -- Create strings.xml since it's missing.\n"); @@ -1268,7 +1361,7 @@ bool updateStringsXml(const Options &options) return false; } file.write(QByteArray("<?xml version='1.0' encoding='utf-8'?><resources><string name=\"app_name\" translatable=\"false\">") - .append(QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1).toLatin1()) + .append(options.applicationBinary.toLatin1()) .append("</string></resources>\n")); return true; } @@ -1284,71 +1377,34 @@ bool updateAndroidManifest(Options &options) if (options.verbose) fprintf(stdout, " -- AndroidManifest.xml \n"); - QStringList localLibs = options.localLibs; - - // If .pro file overrides dependency detection, we need to see which platform plugin they picked - if (localLibs.isEmpty()) { - QString plugin; - for (const QtDependency &qtDependency : qAsConst(options.qtDependencies)) { - if (qtDependency.relativePath.endsWith(QLatin1String("libqtforandroid.so")) - || qtDependency.relativePath.endsWith(QLatin1String("libqtforandroidGL.so"))) { - if (!plugin.isEmpty() && plugin != qtDependency.relativePath) { - fprintf(stderr, "Both platform plugins libqtforandroid.so and libqtforandroidGL.so included in package. Please include only one.\n"); - return false; - } - - plugin = qtDependency.relativePath; - } - } - - if (plugin.isEmpty()) { - fprintf(stderr, "No platform plugin, neither libqtforandroid.so or libqtforandroidGL.so, included in package. Please include one.\n"); - return false; - } - - localLibs.append(plugin); - if (options.verbose) - fprintf(stdout, " -- Using platform plugin %s\n", qPrintable(plugin)); - } - - bool usesGL = false; - for (const QtDependency &qtDependency : qAsConst(options.qtDependencies)) { - if (qtDependency.relativePath.endsWith(QLatin1String("libQt5OpenGL.so")) - || qtDependency.relativePath.endsWith(QLatin1String("libQt5Quick.so"))) { - usesGL = true; - break; - } - } - options.localJars.removeDuplicates(); options.initClasses.removeDuplicates(); QHash<QString, QString> replacements; - replacements[QLatin1String("-- %%INSERT_APP_NAME%% --")] = QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1); - replacements[QLatin1String("-- %%INSERT_APP_LIB_NAME%% --")] = QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1); - replacements[QLatin1String("-- %%INSERT_LOCAL_LIBS%% --")] = localLibs.join(QLatin1Char(':')); - replacements[QLatin1String("-- %%INSERT_LOCAL_JARS%% --")] = options.localJars.join(QLatin1Char(':')); - replacements[QLatin1String("-- %%INSERT_INIT_CLASSES%% --")] = options.initClasses.join(QLatin1Char(':')); - replacements[QLatin1String("-- %%INSERT_VERSION_NAME%% --")] = options.versionName; - replacements[QLatin1String("-- %%INSERT_VERSION_CODE%% --")] = options.versionCode; - replacements[QLatin1String("package=\"org.qtproject.example\"")] = QString::fromLatin1("package=\"%1\"").arg(options.packageName); - replacements[QLatin1String("-- %%BUNDLE_LOCAL_QT_LIBS%% --")] - = (options.deploymentMechanism == Options::Bundled) ? QString::fromLatin1("1") : QString::fromLatin1("0"); - replacements[QLatin1String("-- %%USE_LOCAL_QT_LIBS%% --")] - = (options.deploymentMechanism != Options::Ministro) ? QString::fromLatin1("1") : QString::fromLatin1("0"); + replacements[QStringLiteral("-- %%INSERT_APP_NAME%% --")] = options.applicationBinary; + replacements[QStringLiteral("-- %%INSERT_APP_LIB_NAME%% --")] = options.applicationBinary; + replacements[QStringLiteral("-- %%INSERT_LOCAL_JARS%% --")] = options.localJars.join(QLatin1Char(':')); + replacements[QStringLiteral("-- %%INSERT_INIT_CLASSES%% --")] = options.initClasses.join(QLatin1Char(':')); + replacements[QStringLiteral("-- %%INSERT_VERSION_NAME%% --")] = options.versionName; + replacements[QStringLiteral("-- %%INSERT_VERSION_CODE%% --")] = options.versionCode; + replacements[QStringLiteral("package=\"org.qtproject.example\"")] = QStringLiteral("package=\"%1\"").arg(options.packageName); + replacements[QStringLiteral("-- %%BUNDLE_LOCAL_QT_LIBS%% --")] + = (options.deploymentMechanism == Options::Bundled) ? QStringLiteral("1") : QStringLiteral("0"); + replacements[QStringLiteral("-- %%USE_LOCAL_QT_LIBS%% --")] + = (options.deploymentMechanism != Options::Ministro) ? QStringLiteral("1") : QStringLiteral("0"); QString permissions; for (const QString &permission : qAsConst(options.permissions)) - permissions += QString::fromLatin1(" <uses-permission android:name=\"%1\" />\n").arg(permission); - replacements[QLatin1String("<!-- %%INSERT_PERMISSIONS -->")] = permissions; + permissions += QStringLiteral(" <uses-permission android:name=\"%1\" />\n").arg(permission); + replacements[QStringLiteral("<!-- %%INSERT_PERMISSIONS -->")] = permissions.trimmed(); QString features; for (const QString &feature : qAsConst(options.features)) features += QStringLiteral(" <uses-feature android:name=\"%1\" android:required=\"false\" />\n").arg(feature); - if (usesGL) + if (options.usesOpenGL) features += QStringLiteral(" <uses-feature android:glEsVersion=\"0x00020000\" android:required=\"true\" />"); - replacements[QLatin1String("<!-- %%INSERT_FEATURES -->")] = features; + replacements[QStringLiteral("<!-- %%INSERT_FEATURES -->")] = features.trimmed(); QString androidManifestPath = options.outputDirectory + QLatin1String("/AndroidManifest.xml"); if (!updateFile(androidManifestPath, replacements)) @@ -1409,7 +1465,7 @@ bool updateAndroidFiles(Options &options) if (options.verbose) fprintf(stdout, "Updating Android package files with project settings.\n"); - if (!updateLibsXml(options)) + if (!updateLibsXml(&options)) return false; if (!updateAndroidManifest(options)) @@ -1466,7 +1522,7 @@ bool readAndroidDependencyXml(Options *options, QSet<QString> *usedDependencies, QSet<QString> *remainingDependencies) { - QString androidDependencyName = absoluteFilePath(options, QString::fromLatin1("/lib/%1-android-dependencies.xml").arg(moduleName)); + QString androidDependencyName = absoluteFilePath(options, QStringLiteral("/lib/%1-android-dependencies.xml").arg(moduleName)); QFile androidDependencyFile(androidDependencyName); if (androidDependencyFile.exists()) { @@ -1505,7 +1561,7 @@ bool readAndroidDependencyXml(Options *options, if (options->verbose) fprintf(stdout, "Appending dependency from xml: %s\n", qPrintable(fileName.relativePath)); - options->qtDependencies.append(fileName); + options->qtDependencies[options->currentArchitecture].append(fileName); } } else if (reader.name() == QLatin1String("jar")) { int bundling = reader.attributes().value(QLatin1String("bundling")).toInt(); @@ -1513,7 +1569,7 @@ bool readAndroidDependencyXml(Options *options, if (bundling == (options->deploymentMechanism == Options::Bundled)) { QtDependency dependency(fileName, absoluteFilePath(options, fileName)); if (!usedDependencies->contains(dependency.absolutePath)) { - options->qtDependencies.append(dependency); + options->qtDependencies[options->currentArchitecture].append(dependency); usedDependencies->insert(dependency.absolutePath); } } @@ -1529,15 +1585,15 @@ bool readAndroidDependencyXml(Options *options, if (reader.attributes().hasAttribute(QLatin1String("replaces"))) { QString replaces = reader.attributes().value(QLatin1String("replaces")).toString(); for (int i=0; i<options->localLibs.size(); ++i) { - if (options->localLibs.at(i) == replaces) { - options->localLibs[i] = fileName; + if (options->localLibs[options->currentArchitecture].at(i) == replaces) { + options->localLibs[options->currentArchitecture][i] = fileName; break; } } } else if (!fileName.isEmpty()) { - options->localLibs.append(fileName); + options->localLibs[options->currentArchitecture].append(fileName); } - if (fileName.endsWith(QLatin1String(".so"))) { + if (fileName.endsWith(QStringLiteral(".so")) && checkArchitecture(*options, fileName)) { remainingDependencies->insert(fileName); } } else if (reader.name() == QLatin1String("permission")) { @@ -1557,26 +1613,19 @@ bool readAndroidDependencyXml(Options *options, } else if (options->verbose) { fprintf(stdout, "No android dependencies for %s\n", qPrintable(moduleName)); } + options->permissions.removeDuplicates(); + options->features.removeDuplicates(); return true; } QStringList getQtLibsFromElf(const Options &options, const QString &fileName) { - QString readElf = options.ndkPath - + QLatin1String("/toolchains/") - + options.toolchainPrefix; - - if (!options.useLLVM) - readElf += QLatin1Char('-') + options.toolchainVersion; - - readElf += QLatin1String("/prebuilt/") - + options.ndkHost - + QLatin1String("/bin/") - + options.toolPrefix + - (options.useLLVM ? QLatin1String("-readobj") : QLatin1String("-readelf")); + QString readElf = QStringLiteral("%1/toolchains/%2/prebuilt/%3/bin/llvm-readobj").arg(options.ndkPath, + options.toolchainPrefix, + options.ndkHost); #if defined(Q_OS_WIN32) - readElf += QLatin1String(".exe"); + readElf += QStringLiteral(".exe"); #endif if (!QFile::exists(readElf)) { @@ -1584,14 +1633,11 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName) return QStringList(); } - if (options.useLLVM) - readElf = QString::fromLatin1("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(fileName)); - else - readElf = QString::fromLatin1("%1 -d -W %2").arg(shellQuote(readElf), shellQuote(fileName)); + readElf = QStringLiteral("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(fileName)); FILE *readElfCommand = openProcess(readElf); if (!readElfCommand) { - fprintf(stderr, "Cannot execute command %s", qPrintable(readElf)); + fprintf(stderr, "Cannot execute command %s\n", qPrintable(readElf)); return QStringList(); } @@ -1599,23 +1645,26 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName) bool readLibs = false; char buffer[512]; - while (fgets(buffer, sizeof(buffer), readElfCommand) != 0) { + while (fgets(buffer, sizeof(buffer), readElfCommand) != nullptr) { QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer)); QString library; - if (options.useLLVM) { - line = line.trimmed(); - if (!readLibs) { - readLibs = line.startsWith("NeededLibraries"); - continue; + line = line.trimmed(); + if (!readLibs) { + if (line.startsWith("Arch: ")) { + auto it = elfArchitecures.find(line.mid(6)); + if (it == elfArchitecures.constEnd() || *it != options.currentArchitecture.toLatin1()) { + if (options.verbose) + fprintf(stdout, "Skipping \"%s\", architecture mismatch\n", qPrintable(fileName)); + return {}; + } } - if (!line.startsWith("lib")) - continue; - library = QString::fromLatin1(line); - } else if (line.contains("(NEEDED)") && line.contains("Shared library:")) { - const int pos = line.lastIndexOf('[') + 1; - library = QString::fromLatin1(line.mid(pos, line.length() - pos - 2)); + readLibs = line.startsWith("NeededLibraries"); + continue; } - QString libraryName = QLatin1String("lib/") + library; + if (!line.startsWith("lib")) + continue; + library = QString::fromLatin1(line); + QString libraryName = QStringLiteral("lib/") + library; if (QFile::exists(absoluteFilePath(&options, libraryName))) ret += libraryName; } @@ -1653,7 +1702,7 @@ bool readDependenciesFromElf(Options *options, return false; } - options->qtDependencies.append(QtDependency(dependency, absoluteDependencyPath)); + options->qtDependencies[options->currentArchitecture].append(QtDependency(dependency, absoluteDependencyPath)); if (options->verbose) fprintf(stdout, "Appending dependency: %s\n", qPrintable(dependency)); dependenciesToCheck.append(dependency); @@ -1797,10 +1846,10 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies) fprintf(stdout, " -- Appending dependency found by qmlimportscanner: %s\n", qPrintable(fileName.absolutePath)); // Put all imports in default import path in assets - fileName.relativePath.prepend(QLatin1String("qml/")); - options->qtDependencies.append(fileName); + fileName.relativePath.prepend(QStringLiteral("qml/")); + options->qtDependencies[options->currentArchitecture].append(fileName); - if (fileName.absolutePath.endsWith(QLatin1String(".so"))) { + if (fileName.absolutePath.endsWith(QStringLiteral(".so")) && checkArchitecture(*options, fileName.absolutePath)) { QSet<QString> remainingDependencies; if (!readDependenciesFromElf(options, fileName.absolutePath, usedDependencies, &remainingDependencies)) return false; @@ -1819,7 +1868,7 @@ bool readDependencies(Options *options) fprintf(stdout, "Detecting dependencies of application.\n"); // Override set in .pro file - if (!options->qtDependencies.isEmpty()) { + if (!options->qtDependencies[options->currentArchitecture].isEmpty()) { if (options->verbose) fprintf(stdout, "\tDependencies explicitly overridden in .pro file. No detection needed.\n"); return true; @@ -1829,11 +1878,7 @@ bool readDependencies(Options *options) QSet<QString> remainingDependencies; // Add dependencies of application binary first - if (!readDependenciesFromElf(options, options->applicationBinary, &usedDependencies, &remainingDependencies)) - return false; - - // Jam in the dependencies of the platform plugin, since the application will crash without it - if (!readDependenciesFromElf(options, options->qtInstallDirectory + QLatin1String("/plugins/platforms/android/libqtforandroid.so"), &usedDependencies, &remainingDependencies)) + if (!readDependenciesFromElf(options, QStringLiteral("%1/libs/%2/lib%3_%2.so").arg(options->outputDirectory, options->currentArchitecture, options->applicationBinary), &usedDependencies, &remainingDependencies)) return false; while (!remainingDependencies.isEmpty()) { @@ -1853,14 +1898,14 @@ bool readDependencies(Options *options) } } - QStringList::iterator it = options->localLibs.begin(); - while (it != options->localLibs.end()) { + QStringList::iterator it = options->localLibs[options->currentArchitecture].begin(); + while (it != options->localLibs[options->currentArchitecture].end()) { QStringList unmetDependencies; if (!goodToCopy(options, absoluteFilePath(options, *it), &unmetDependencies)) { fprintf(stdout, "Skipping %s due to unmet dependencies: %s\n", qPrintable(*it), qPrintable(unmetDependencies.join(QLatin1Char(',')))); - it = options->localLibs.erase(it); + it = options->localLibs[options->currentArchitecture].erase(it); } else { ++it; } @@ -1872,94 +1917,33 @@ bool readDependencies(Options *options) return true; } -bool stripFile(const Options &options, const QString &fileName) -{ - QString strip = options.ndkPath - + QLatin1String("/toolchains/") - + options.toolchainPrefix; - - if (!options.useLLVM) - strip += QLatin1Char('-') + options.toolchainVersion; - - strip += QLatin1String("/prebuilt/") - + options.ndkHost - + QLatin1String("/bin/") - + options.toolPrefix - + QLatin1String("-strip"); -#if defined(Q_OS_WIN32) - strip += QLatin1String(".exe"); -#endif - - if (!QFile::exists(strip)) { - fprintf(stderr, "Command does not exist: %s\n", qPrintable(strip)); - return false; - } - - if (options.useLLVM) - strip = QString::fromLatin1("%1 -strip-all %2").arg(shellQuote(strip), shellQuote(fileName)); - else - strip = QString::fromLatin1("%1 %2").arg(shellQuote(strip), shellQuote(fileName)); - - FILE *stripCommand = openProcess(strip); - if (stripCommand == 0) { - fprintf(stderr, "Cannot execute command %s", qPrintable(strip)); - return false; - } - - pclose(stripCommand); - - return true; -} - -bool stripLibraries(const Options &options) +bool containsApplicationBinary(Options *options) { - if (!options.stripLibraries) + if (!options->build) return true; - if (options.verbose) - fprintf(stdout, "Stripping libraries to minimize size.\n"); - - - QString libraryPath = options.outputDirectory - + QLatin1String("/libs/") - + options.architecture; - const QStringList libraries = QDir(libraryPath).entryList(QDir::Files); - for (const QString &library : libraries) { - if (library.endsWith(QLatin1String(".so"))) { - if (!stripFile(options, libraryPath + QLatin1Char('/') + library)) - return false; - } - } - - return true; -} - -bool containsApplicationBinary(const Options &options) -{ - if (options.verbose) + if (options->verbose) fprintf(stdout, "Checking if application binary is in package.\n"); - QFileInfo applicationBinary(options.applicationBinary); - QString destinationFileName = options.outputDirectory - + QLatin1String("/libs/") - + options.architecture - + QLatin1Char('/') - + applicationBinary.fileName(); + QFileInfo applicationBinary(options->applicationBinary); + QString applicationFileName = QStringLiteral("lib%1_%2.so").arg(options->applicationBinary, + options->currentArchitecture); - if (!QFile::exists(destinationFileName)) { + QString applicationPath = QStringLiteral("%1/libs/%2/%3").arg(options->outputDirectory, + options->currentArchitecture, + applicationFileName); + if (!QFile::exists(applicationPath)) { #if defined(Q_OS_WIN32) QLatin1String makeTool("mingw32-make"); // Only Mingw host builds supported on Windows currently #else QLatin1String makeTool("make"); #endif - fprintf(stderr, "Application binary is not in output directory: %s. Please run '%s install INSTALL_ROOT=%s' first.\n", - qPrintable(destinationFileName), + qPrintable(applicationFileName), qPrintable(makeTool), - qPrintable(options.outputDirectory)); + qPrintable(options->outputDirectory)); return false; } - return true; } @@ -1997,10 +1981,13 @@ bool goodToCopy(const Options *options, const QString &file, QStringList *unmetD if (!file.endsWith(QLatin1String(".so"))) return true; + if (!checkArchitecture(*options, file)) + return false; + bool ret = true; const auto libs = getQtLibsFromElf(*options, file); for (const QString &lib : libs) { - if (!options->qtDependencies.contains(QtDependency(lib, absoluteFilePath(options, lib)))) { + if (!options->qtDependencies[options->currentArchitecture].contains(QtDependency(lib, absoluteFilePath(options, lib)))) { ret = false; unmetDependencies->append(lib); } @@ -2027,25 +2014,22 @@ bool copyQtFiles(Options *options) QString libsDirectory = QLatin1String("libs/"); + // Copy other Qt dependencies - QString libDestinationDirectory = libsDirectory + options->architecture + QLatin1Char('/'); - QString assetsDestinationDirectory = QLatin1String("assets/--Added-by-androiddeployqt--/"); - for (const QtDependency &qtDependency : qAsConst(options->qtDependencies)) { + QString assetsDestinationDirectory = QStringLiteral("assets/--Added-by-androiddeployqt--/"); + for (const QtDependency &qtDependency : qAsConst(options->qtDependencies[options->currentArchitecture])) { QString sourceFileName = qtDependency.absolutePath; QString destinationFileName; - if (qtDependency.relativePath.endsWith(QLatin1String(".so"))) { + if (qtDependency.relativePath.endsWith(QStringLiteral(".so"))) { QString garbledFileName; - if (qtDependency.relativePath.startsWith(QLatin1String("lib/"))) { + if (qtDependency.relativePath.startsWith(QStringLiteral("lib/"))) { garbledFileName = qtDependency.relativePath.mid(sizeof("lib/") - 1); } else { - garbledFileName = QLatin1String("lib") - + QString(qtDependency.relativePath).replace(QLatin1Char('/'), QLatin1Char('_')); - + garbledFileName = QString(qtDependency.relativePath).replace(QLatin1Char('/'), QLatin1Char('_')); } - destinationFileName = libDestinationDirectory + garbledFileName; - - } else if (qtDependency.relativePath.startsWith(QLatin1String("jar/"))) { + destinationFileName = libsDirectory + options->currentArchitecture + QLatin1Char('/') + garbledFileName; + } else if (qtDependency.relativePath.startsWith(QStringLiteral("jar/"))) { destinationFileName = libsDirectory + qtDependency.relativePath.mid(sizeof("jar/") - 1); } else { destinationFileName = assetsDestinationDirectory + qtDependency.relativePath; @@ -2058,20 +2042,34 @@ bool copyQtFiles(Options *options) QStringList unmetDependencies; if (!goodToCopy(options, sourceFileName, &unmetDependencies)) { - fprintf(stdout, " -- Skipping %s. It has unmet dependencies: %s.\n", - qPrintable(sourceFileName), - qPrintable(unmetDependencies.join(QLatin1Char(',')))); + if (unmetDependencies.isEmpty()) { + if (options->verbose) { + fprintf(stdout, " -- Skipping %s, architecture mismatch.\n", + qPrintable(sourceFileName)); + } + } else { + if (unmetDependencies.isEmpty()) { + if (options->verbose) { + fprintf(stdout, " -- Skipping %s, architecture mismatch.\n", + qPrintable(sourceFileName)); + } + } else { + fprintf(stdout, " -- Skipping %s. It has unmet dependencies: %s.\n", + qPrintable(sourceFileName), + qPrintable(unmetDependencies.join(QLatin1Char(',')))); + } + } continue; } if (options->deploymentMechanism == Options::Bundled && !copyFileIfNewer(sourceFileName, options->outputDirectory + QLatin1Char('/') + destinationFileName, - options->verbose)) { + *options)) { return false; } - options->bundledFiles += qMakePair(destinationFileName, qtDependency.relativePath); + options->bundledFiles[options->currentArchitecture] += qMakePair(destinationFileName, qtDependency.relativePath); } return true; @@ -2409,21 +2407,18 @@ bool copyStdCpp(Options *options) if (options->verbose) fprintf(stdout, "Copying STL library\n"); - if (!QFile::exists(options->stdCppPath)) { - fprintf(stderr, "STL library does not exist at %s\n", qPrintable(options->stdCppPath)); - return false; - } - - const QString destinationDirectory = options->outputDirectory - + QLatin1String("/libs/") + options->architecture; - - if (!copyFileIfNewer(options->stdCppPath, destinationDirectory + QLatin1String("/lib") - + options->stdCppName + QLatin1String(".so"), - options->verbose)) { + QString stdCppPath = QStringLiteral("%1/%2/lib%3.so").arg(options->stdCppPath, options->architectures[options->currentArchitecture], options->stdCppName); + if (!QFile::exists(stdCppPath)) { + fprintf(stderr, "STL library does not exist at %s\n", qPrintable(stdCppPath)); + fflush(stdout); + fflush(stderr); return false; } - return true; + const QString destinationFile = QStringLiteral("%1/libs/%2/lib%3.so").arg(options->outputDirectory, + options->currentArchitecture, + options->stdCppName); + return copyFileIfNewer(stdCppPath, destinationFile, *options); } bool jarSignerSignPackage(const Options &options) @@ -2654,55 +2649,6 @@ bool signPackage(const Options &options) return apkSignerRunner() && QFile::remove(apkPath(options, UnsignedAPK)); } -bool copyGdbServer(const Options &options) -{ - if (options.verbose) - fprintf(stdout, "Copying gdbserver into package.\n"); - - QString architectureSubDirectory; - if (options.architecture == QLatin1String("arm64-v8a")) - architectureSubDirectory = QLatin1String("android-arm64"); - else if (options.architecture.startsWith(QLatin1String("arm"))) - architectureSubDirectory = QLatin1String("android-arm"); - else - architectureSubDirectory = QLatin1String("android-") + options.architecture; - - QString gdbServerBinary = options.ndkPath - + QLatin1String("/prebuilt/") - + architectureSubDirectory - + QLatin1String("/gdbserver/gdbserver"); - if (!QFile::exists(gdbServerBinary)) { - fprintf(stderr, "Cannot find gdbserver at %s.\n", qPrintable(gdbServerBinary)); - return false; - } - - QString gdbServerTarget = options.outputDirectory + QLatin1String("/libs/") + options.architecture; - - if (!copyFileIfNewer(gdbServerBinary, - gdbServerTarget + QLatin1String("/gdbserver"), - options.verbose) - || !copyFileIfNewer(gdbServerBinary, - gdbServerTarget + QLatin1String("/libgdbserver.so"), - options.verbose)) { - return false; - } - - QString addedByAndroidDeployQtPath = options.outputDirectory + QLatin1String("/assets/--Added-by-androiddeployqt--/"); - if (!QDir().mkpath(addedByAndroidDeployQtPath)) { - fprintf(stderr, "Failed to create directory '%s'", qPrintable(addedByAndroidDeployQtPath)); - return false; - } - QFile f(addedByAndroidDeployQtPath + QLatin1String("debugger.command")); - if (!f.open(QIODevice::WriteOnly)) { - fprintf(stderr, "Failed to create directory '%s'", qPrintable(addedByAndroidDeployQtPath)); - return false; - } - f.write("lib/libgdbserver.so --multi +"); - f.close(); - - return true; -} - bool generateAssetsFileList(const Options &options) { if (options.verbose) @@ -2764,8 +2710,6 @@ enum ErrorCode CannotCopyGnuStl = 5, CannotCopyQtFiles = 6, CannotFindApplicationBinary = 7, - CannotCopyGdbServer = 8, - CannotStripLibraries = 9, CannotCopyAndroidExtraLibs = 10, CannotCopyAndroidSources = 11, CannotUpdateAndroidFiles = 12, @@ -2813,25 +2757,7 @@ int main(int argc, char *argv[]) : "No" ); - if (options.auxMode) { - if (!readDependencies(&options)) - return CannotReadDependencies; - if (!copyQtFiles(&options)) - return CannotCopyQtFiles; - if (!copyAndroidExtraResources(options)) - return CannotCopyAndroidExtraResources; - if (!copyAndroidExtraLibs(options)) - return CannotCopyAndroidExtraLibs; - if (!stripLibraries(options)) - return CannotStripLibraries; - if (!updateAndroidFiles(options)) - return CannotUpdateAndroidFiles; - if (options.generateAssetsFileList && !generateAssetsFileList(options)) - return CannotGenerateAssetsFileList; - return 0; - } - - if (options.build) { + if (options.build && !options.auxMode) { cleanAndroidFiles(options); if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Cleaned Android file\n", options.timer.elapsed()); @@ -2843,60 +2769,68 @@ int main(int argc, char *argv[]) fprintf(stdout, "[TIMING] %d ms: Copied Android template\n", options.timer.elapsed()); } - if (!readDependencies(&options)) - return CannotReadDependencies; - - if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Read dependencies\n", options.timer.elapsed()); + for (auto it = options.architectures.constBegin(); it != options.architectures.constEnd(); ++it) { + options.clear(it.key()); - if (options.deploymentMechanism != Options::Ministro && !copyStdCpp(&options)) - return CannotCopyGnuStl; + if (!readDependencies(&options)) + return CannotReadDependencies; - if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Copied GNU STL\n", options.timer.elapsed()); + if (Q_UNLIKELY(options.timing)) + fprintf(stdout, "[TIMING] %d ms: Read dependencies\n", options.timer.elapsed()); - if (!copyQtFiles(&options)) - return CannotCopyQtFiles; + if (!copyQtFiles(&options)) + return CannotCopyQtFiles; - if (options.build) { if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Copied Qt files\n", options.timer.elapsed()); - if (!containsApplicationBinary(options)) - return CannotFindApplicationBinary; + if (!copyAndroidExtraLibs(&options)) + return CannotCopyAndroidExtraLibs; if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Checked for application binary\n", options.timer.elapsed()); + fprintf(stdout, "[TIMING] %d ms: Copied extra libs\n", options.timer.elapsed()); - bool needToCopyGdbServer = options.gdbServer == Options::True - || (options.gdbServer == Options::Auto && !options.releasePackage); - if (needToCopyGdbServer && !copyGdbServer(options)) - return CannotCopyGdbServer; + if (!copyAndroidExtraResources(&options)) + return CannotCopyAndroidExtraResources; if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Copied GDB server\n", options.timer.elapsed()); + fprintf(stdout, "[TIMING] %d ms: Copied extra resources\n", options.timer.elapsed()); - if (!copyAndroidExtraLibs(options)) - return CannotCopyAndroidExtraLibs; + if (!options.auxMode) { + if (options.deploymentMechanism != Options::Ministro && !copyStdCpp(&options)) + return CannotCopyGnuStl; + + if (Q_UNLIKELY(options.timing)) + fprintf(stdout, "[TIMING] %d ms: Copied GNU STL\n", options.timer.elapsed()); + } + + if (!containsApplicationBinary(&options)) + return CannotFindApplicationBinary; if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Copied extra libs\n", options.timer.elapsed()); + fprintf(stdout, "[TIMING] %d ms: Checked for application binary\n", options.timer.elapsed()); - if (!copyAndroidExtraResources(options)) - return CannotCopyAndroidExtraResources; + if (options.deploymentMechanism != Options::Ministro) { + if (Q_UNLIKELY(options.timing)) + fprintf(stdout, "[TIMING] %d ms: Bundled Qt libs\n", options.timer.elapsed()); + } + } + if (options.auxMode) { + if (!updateAndroidFiles(options)) + return CannotUpdateAndroidFiles; + if (options.generateAssetsFileList && !generateAssetsFileList(options)) + return CannotGenerateAssetsFileList; + return 0; + } + + if (options.build) { if (!copyAndroidSources(options)) return CannotCopyAndroidSources; if (Q_UNLIKELY(options.timing)) fprintf(stdout, "[TIMING] %d ms: Copied android sources\n", options.timer.elapsed()); - if (!stripLibraries(options)) - return CannotStripLibraries; - - if (Q_UNLIKELY(options.timing)) - fprintf(stdout, "[TIMING] %d ms: Stripped libraries\n", options.timer.elapsed()); - if (!updateAndroidFiles(options)) return CannotUpdateAndroidFiles; diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index e6d6f72354..f61d407d4a 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -362,7 +362,7 @@ static bool parseTestArgs() g_options.testArgs += QStringLiteral(" -o output.%1,%1").arg(format); g_options.testArgs += unhandledArgs; - g_options.testArgs = QStringLiteral("shell am start -e applicationArguments \"'%1'\" -n %2/%3").arg(shellQuote(g_options.testArgs.trimmed()), + g_options.testArgs = QStringLiteral("shell am start -e applicationArguments \\\"%1\\\" -n %2/%3").arg(shellQuote(g_options.testArgs.trimmed()), g_options.package, g_options.activity); return true; @@ -409,7 +409,7 @@ static bool pullFiles() return false; } auto checkerIt = g_options.checkFiles.find(it.key()); - ret &= checkerIt != g_options.checkFiles.end() && checkerIt.value()(output); + ret = ret && checkerIt != g_options.checkFiles.end() && checkerIt.value()(output); if (it.value() == QStringLiteral("-")){ fprintf(stdout, "%s", output.constData()); fflush(stdout); diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index a89192e76f..a1b9003c1c 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -2448,6 +2448,51 @@ void QFileDialog::getOpenFileContent(const QString &nameFilter, const std::funct } /*! + This is a convenience static function that saves \a fileContent to a file, using + a file name and location chosen by the user. \a fileNameHint can be provided to + suggest a file name to the user. + + This function is used to save files to the local file system on Qt for WebAssembly, where + the web sandbox places restrictions on how such access may happen. Its implementation will + make the browser display a native file dialog, where the user makes the file selection. + + It can also be used on other platforms, where it will fall back to using QFileDialog. + + The function is asynchronous and returns immediately. + + \snippet code/src_gui_dialogs_qfiledialog.cpp 16 + \since 5.14 +*/ +void QFileDialog::saveFileContent(const QByteArray &fileContent, const QString &fileNameHint) +{ +#ifdef Q_OS_WASM + QWasmLocalFileAccess::saveFile(fileContent.constData(), fileContent.size(), fileNameHint.toStdString()); +#else + QFileDialog *dialog = new QFileDialog(); + dialog->setAcceptMode(QFileDialog::AcceptSave); + dialog->setFileMode(QFileDialog::AnyFile); + dialog->selectFile(fileNameHint); + + auto fileSelected = [=](const QString &fileName) { + if (!fileName.isNull()) { + QFile selectedFile(fileName); + if (selectedFile.open(QIODevice::WriteOnly)) + selectedFile.write(fileContent); + } + }; + + auto dialogClosed = [=](int code) { + Q_UNUSED(code); + delete dialog; + }; + + connect(dialog, &QFileDialog::fileSelected, fileSelected); + connect(dialog, &QFileDialog::finished, dialogClosed); + dialog->show(); +#endif +} + +/*! This is a convenience static function that will return a file name selected by the user. The file does not have to exist. diff --git a/src/widgets/dialogs/qfiledialog.h b/src/widgets/dialogs/qfiledialog.h index 354a1f928b..790f52f2e7 100644 --- a/src/widgets/dialogs/qfiledialog.h +++ b/src/widgets/dialogs/qfiledialog.h @@ -284,6 +284,7 @@ public: static void getOpenFileContent(const QString &nameFilter, const std::function<void(const QString &, const QByteArray &)> &fileContentsReady); + static void saveFileContent(const QByteArray &fileContent, const QString &fileNameHint = QString()); protected: QFileDialog(const QFileDialogArgs &args); diff --git a/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp b/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp index 39aca459db..7ccd827a04 100644 --- a/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp +++ b/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp @@ -155,3 +155,8 @@ auto fileOpenCompleted = [](const QString &fileName, const QByteArray &fileConte } QFileDialog::getOpenFileContent("Images (*.png *.xpm *.jpg)", fileContentReady); //! [15] + +//! [16] +QByteArray imageData; // obtained from e.g. QImage::save() +QFileDialog::saveFile("myimage.png", imageData); +//! [16] diff --git a/src/widgets/graphicsview/qgraphicsproxywidget.cpp b/src/widgets/graphicsview/qgraphicsproxywidget.cpp index 2b6712075f..7413a26261 100644 --- a/src/widgets/graphicsview/qgraphicsproxywidget.cpp +++ b/src/widgets/graphicsview/qgraphicsproxywidget.cpp @@ -404,7 +404,7 @@ void QGraphicsProxyWidgetPrivate::_q_removeWidgetSlot() { Q_Q(QGraphicsProxyWidget); if (!widget.isNull()) { - if (QWExtra *extra = widget->d_func()->extra) + if (const auto &extra = widget->d_func()->extra) extra->proxyWidget = 0; } widget = 0; @@ -477,8 +477,8 @@ void QGraphicsProxyWidgetPrivate::updateProxyInputMethodAcceptanceFromWidget() */ void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin) { - QWExtra *extra; - if (!((extra = subWin->d_func()->extra) && extra->proxyWidget)) { + const auto &extra = subWin->d_func()->extra; + if (!extra || !extra->proxyWidget) { QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func(), subWin->windowFlags()); subProxy->d_func()->setWidget_helper(subWin, false); } @@ -631,7 +631,7 @@ void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool auto if (!newWidget) return; if (!newWidget->isWindow()) { - QWExtra *extra = newWidget->parentWidget()->d_func()->extra; + const auto &extra = newWidget->parentWidget()->d_func()->extra; if (!extra || !extra->proxyWidget) { qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p " "which is not a toplevel widget, and is not a child of an embedded widget", newWidget); @@ -641,10 +641,10 @@ void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool auto // Register this proxy within the widget's private. // ### This is a bit backdoorish - QWExtra *extra = newWidget->d_func()->extra; + QWExtra *extra = newWidget->d_func()->extra.get(); if (!extra) { newWidget->d_func()->createExtra(); - extra = newWidget->d_func()->extra; + extra = newWidget->d_func()->extra.get(); } QGraphicsProxyWidget **proxyWidget = &extra->proxyWidget; if (*proxyWidget) { diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 94b14ecb4f..629c696544 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -3388,7 +3388,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) #if QT_CONFIG(graphicsview) // QGraphicsProxyWidget handles its own propagation, // and we must not change QDragManagers currentTarget. - QWExtra *extra = w->window()->d_func()->extra; + const auto &extra = w->window()->d_func()->extra; if (extra && extra->proxyWidget) { res = d->notify_helper(w, dragEvent); break; @@ -3416,7 +3416,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e) #if QT_CONFIG(graphicsview) // QGraphicsProxyWidget handles its own propagation, // and we must not change QDragManagers currentTarget. - QWExtra *extra = w->window()->d_func()->extra; + const auto &extra = w->window()->d_func()->extra; bool isProxyWidget = extra && extra->proxyWidget; if (!isProxyWidget) #endif diff --git a/src/widgets/kernel/qshortcut.cpp b/src/widgets/kernel/qshortcut.cpp index a4ebcdfc84..39b08dbc1e 100644 --- a/src/widgets/kernel/qshortcut.cpp +++ b/src/widgets/kernel/qshortcut.cpp @@ -178,7 +178,7 @@ static bool correctWidgetContext(Qt::ShortcutContext context, QWidget *w, QWidge // Below is Qt::WindowShortcut context QWidget *tlw = w->window(); #if QT_CONFIG(graphicsview) - if (auto topData = static_cast<QWidgetPrivate *>(QObjectPrivate::get(tlw))->extra) { + if (auto topData = static_cast<QWidgetPrivate *>(QObjectPrivate::get(tlw))->extra.get()) { if (topData->proxyWidget) { bool res = correctGraphicsWidgetContext(context, topData->proxyWidget, active_window); return res; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index c0c7f5cf3d..c024f7f1f3 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -130,7 +130,6 @@ extern QDesktopWidget *qt_desktopWidget; // qapplication.cpp QWidgetPrivate::QWidgetPrivate(int version) : QObjectPrivate(version) - , extra(0) , focus_next(0) , focus_prev(0) , focus_child(0) @@ -1460,7 +1459,7 @@ QWidget::~QWidget() while (w->d_func()->extra && w->d_func()->extra->focus_proxy) w = w->d_func()->extra->focus_proxy; QWidget *window = w->window(); - QWExtra *e = window ? window->d_func()->extra : 0; + QWExtra *e = window ? window->d_func()->extra.get() : nullptr ; if (!e || !e->proxyWidget || (w->parentWidget() && w->parentWidget()->d_func()->focus_child == this)) #endif clearFocus(); @@ -1618,7 +1617,7 @@ void QWidgetPrivate::createTLExtra() void QWidgetPrivate::createExtra() { if (!extra) { // if not exists - extra = new QWExtra; + extra = qt_make_unique<QWExtra>(); extra->glContext = 0; #if QT_CONFIG(graphicsview) extra->proxyWidget = 0; @@ -1666,9 +1665,8 @@ void QWidgetPrivate::deleteExtra() deleteTLSysExtra(); // extra->topextra->backingStore destroyed in QWidgetPrivate::deleteTLSysExtra() } - delete extra; // extra->xic destroyed in QWidget::destroy() - extra = 0; + extra.reset(); } } @@ -1739,7 +1737,7 @@ QRegion QWidgetPrivate::overlappedRegion(const QRect &rect, bool breakAfterFirst const QRect siblingRect = sibling->d_func()->effectiveRectFor(sibling->data->crect); if (qRectIntersects(siblingRect, r)) { - const QWExtra *siblingExtra = sibling->d_func()->extra; + const auto &siblingExtra = sibling->d_func()->extra; if (siblingExtra && siblingExtra->hasMask && !sibling->d_func()->graphicsEffect && !siblingExtra->mask.translated(sibling->data->crect.topLeft()).intersects(r)) { continue; @@ -3733,7 +3731,7 @@ QSize QWidget::sizeIncrement() const QSize QWidget::baseSize() const { Q_D(const QWidget); - return (d->extra != 0 && d->extra->topextra != 0) + return (d->extra && d->extra->topextra) ? QSize(d->extra->topextra->basew, d->extra->topextra->baseh) : QSize(0, 0); } @@ -5694,7 +5692,7 @@ QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint * QGraphicsProxyWidget *QWidgetPrivate::nearestGraphicsProxyWidget(const QWidget *origin) { if (origin) { - QWExtra *extra = origin->d_func()->extra; + const auto &extra = origin->d_func()->extra; if (extra && extra->proxyWidget) return extra->proxyWidget; return nearestGraphicsProxyWidget(origin->parentWidget()); @@ -6240,7 +6238,7 @@ bool QWidget::hasFocus() const w = w->d_func()->extra->focus_proxy; #if QT_CONFIG(graphicsview) if (QWidget *window = w->window()) { - QWExtra *e = window->d_func()->extra; + const auto &e = window->d_func()->extra; if (e && e->proxyWidget && e->proxyWidget->hasFocus() && window->focusWidget() == w) return true; } @@ -6297,7 +6295,7 @@ void QWidget::setFocus(Qt::FocusReason reason) #if QT_CONFIG(graphicsview) QWidget *previousProxyFocus = 0; - if (QWExtra *topData = window()->d_func()->extra) { + if (const auto &topData = window()->d_func()->extra) { if (topData->proxyWidget && topData->proxyWidget->hasFocus()) { previousProxyFocus = topData->proxyWidget->widget()->focusWidget(); if (previousProxyFocus && previousProxyFocus->focusProxy()) @@ -6310,7 +6308,7 @@ void QWidget::setFocus(Qt::FocusReason reason) #if QT_CONFIG(graphicsview) // Update proxy state - if (QWExtra *topData = window()->d_func()->extra) { + if (const auto &topData = window()->d_func()->extra) { if (topData->proxyWidget && !topData->proxyWidget->hasFocus()) { f->d_func()->updateFocusChild(); topData->proxyWidget->d_func()->focusFromWidgetToProxy = 1; @@ -6351,7 +6349,7 @@ void QWidget::setFocus(Qt::FocusReason reason) } #endif #if QT_CONFIG(graphicsview) - if (QWExtra *topData = window()->d_func()->extra) { + if (const auto &topData = window()->d_func()->extra) { if (topData->proxyWidget) { if (previousProxyFocus && previousProxyFocus != f) { // Send event to self @@ -6364,7 +6362,7 @@ void QWidget::setFocus(Qt::FocusReason reason) if (!isHidden()) { #if QT_CONFIG(graphicsview) // Update proxy state - if (QWExtra *topData = window()->d_func()->extra) + if (const auto &topData = window()->d_func()->extra) if (topData->proxyWidget && topData->proxyWidget->hasFocus()) topData->proxyWidget->d_func()->updateProxyInputMethodAcceptanceFromWidget(); #endif @@ -6501,7 +6499,7 @@ void QWidget::clearFocus() } #if QT_CONFIG(graphicsview) - QWExtra *topData = d_func()->extra; + const auto &topData = d_func()->extra; if (topData && topData->proxyWidget) topData->proxyWidget->clearFocus(); #endif @@ -6660,7 +6658,7 @@ bool QWidget::isActiveWindow() const return true; #if QT_CONFIG(graphicsview) - if (QWExtra *tlwExtra = tlw->d_func()->extra) { + if (const auto &tlwExtra = tlw->d_func()->extra) { if (isVisible() && tlwExtra->proxyWidget) return tlwExtra->proxyWidget->isActiveWindow(); } @@ -10050,7 +10048,7 @@ void QWidget::setSizePolicy(QSizePolicy policy) d->size_policy = policy; #if QT_CONFIG(graphicsview) - if (QWExtra *extra = d->extra) { + if (const auto &extra = d->extra) { if (extra->proxyWidget) extra->proxyWidget->setSizePolicy(policy); } diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index cfb9a1ad8c..7d8a3ae737 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -626,7 +626,7 @@ public: // Variables. // Regular pointers (keep them together to avoid gaps on 64 bit architectures). - QWExtra *extra; + std::unique_ptr<QWExtra> extra; QWidget *focus_next; QWidget *focus_prev; QWidget *focus_child; @@ -803,7 +803,7 @@ public: inline QWExtra *QWidgetPrivate::extraData() const { - return extra; + return extra.get(); } inline QTLWExtra *QWidgetPrivate::topData() const diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp index 9be99c4723..5900326087 100644 --- a/src/widgets/widgets/qdockarealayout.cpp +++ b/src/widgets/widgets/qdockarealayout.cpp @@ -138,11 +138,8 @@ bool QDockAreaLayoutItem::skip() const QSize QDockAreaLayoutItem::minimumSize() const { - if (widgetItem != 0) { - int left, top, right, bottom; - widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom); - return widgetItem->minimumSize() + QSize(left+right, top+bottom); - } + if (widgetItem) + return widgetItem->minimumSize().grownBy(widgetItem->widget()->contentsMargins()); if (subinfo != 0) return subinfo->minimumSize(); return QSize(0, 0); @@ -150,11 +147,8 @@ QSize QDockAreaLayoutItem::minimumSize() const QSize QDockAreaLayoutItem::maximumSize() const { - if (widgetItem != 0) { - int left, top, right, bottom; - widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom); - return widgetItem->maximumSize()+ QSize(left+right, top+bottom); - } + if (widgetItem) + return widgetItem->maximumSize().grownBy(widgetItem->widget()->contentsMargins()); if (subinfo != 0) return subinfo->maximumSize(); return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); @@ -180,11 +174,8 @@ QSize QDockAreaLayoutItem::sizeHint() const { if (placeHolderItem != 0) return QSize(0, 0); - if (widgetItem != 0) { - int left, top, right, bottom; - widgetItem->widget()->getContentsMargins(&left, &top, &right, &bottom); - return widgetItem->sizeHint() + QSize(left+right, top+bottom); - } + if (widgetItem) + return widgetItem->sizeHint().grownBy(widgetItem->widget()->contentsMargins()); if (subinfo != 0) return subinfo->sizeHint(); return QSize(-1, -1); diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index 1ba34e0fed..05ec3aface 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -381,11 +381,10 @@ QSize QDockWidgetLayout::sizeFromContent(const QSize &content, bool floating) co if (content.height() < 0) result.setHeight(-1); - int left, top, right, bottom; - w->getContentsMargins(&left, &top, &right, &bottom); + const QMargins margins = w->contentsMargins(); //we need to subtract the contents margin (it will be added by the caller) - QSize min = w->minimumSize() - QSize(left + right, top + bottom); - QSize max = w->maximumSize() - QSize(left + right, top + bottom); + QSize min = w->minimumSize().shrunkBy(margins); + QSize max = w->maximumSize().shrunkBy(margins); /* A floating dockwidget will automatically get its minimumSize set to the layout's minimum size + deco. We're *not* interested in this, we only take minimumSize() |