[graphics/krita] /: WebP: add libwebp based file format plugin

L. E. Segovia null at kde.org
Mon Jul 19 06:40:47 BST 2021


Git commit 0d811d3464a8ba10f3c8dc705a302f030326a73d by L. E. Segovia.
Committed on 18/07/2021 at 18:04.
Pushed by dkazakov into branch 'master'.

WebP: add libwebp based file format plugin

This commit incorporates a new file format plugin based on the official
libwebp codec. It also brings new import/export dialogs that enable
access to all the available configuration parameters in libwebp as of
1.20. (I have ifdef'd the most recent bits so it should compile with the
latest LTS release.)

BUG: 414444

CCMAIL: kimageshop at kde.org

M  +2    -0    3rdparty/CMakeLists.txt
A  +25   -0    3rdparty/ext_webp/0001-Fix-CMake-targets-install-directory.patch
A  +15   -0    3rdparty/ext_webp/CMakeLists.txt
M  +12   -0    CMakeLists.txt
M  +1    -1    build-tools/windows/build-msvc.cmd
M  +1    -1    build-tools/windows/build.cmd
A  +224  -0    cmake/modules/FindWebP.cmake
M  +1    -0    packaging/android/androidbuild.sh
M  +1    -0    packaging/linux/appimage/build-deps.sh
M  +1    -1    packaging/macos/osxbuild.sh
M  +4    -0    plugins/impex/CMakeLists.txt
M  +2    -2    plugins/impex/qimageio/krita_qimageio_export.json
M  +2    -2    plugins/impex/qimageio/krita_qimageio_import.json
A  +27   -0    plugins/impex/webp/CMakeLists.txt
A  +166  -0    plugins/impex/webp/dlg_webp_export.cpp     [License: GPL(v2.0+)]
A  +28   -0    plugins/impex/webp/dlg_webp_export.h     [License: GPL(v2.0+)]
A  +822  -0    plugins/impex/webp/dlg_webp_export.ui
A  +181  -0    plugins/impex/webp/dlg_webp_import.cpp     [License: GPL(v2.0+)]
A  +54   -0    plugins/impex/webp/dlg_webp_import.h     [License: GPL(v2.0+)]
A  +421  -0    plugins/impex/webp/dlg_webp_import.ui
A  +237  -0    plugins/impex/webp/kis_webp_export.cpp     [License: GPL(v2.0+)]
A  +29   -0    plugins/impex/webp/kis_webp_export.h     [License: GPL(v2.0+)]
A  +191  -0    plugins/impex/webp/kis_webp_import.cpp     [License: GPL(v2.0+)]
A  +32   -0    plugins/impex/webp/kis_webp_import.h     [License: GPL(v2.0+)]
A  +72   -0    plugins/impex/webp/krita_webp.desktop
A  +12   -0    plugins/impex/webp/krita_webp_export.json
A  +12   -0    plugins/impex/webp/krita_webp_import.json

https://invent.kde.org/graphics/krita/commit/0d811d3464a8ba10f3c8dc705a302f030326a73d

diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt
index ae8ed32c24..4b0747abf3 100644
--- a/3rdparty/CMakeLists.txt
+++ b/3rdparty/CMakeLists.txt
@@ -321,3 +321,5 @@ add_subdirectory(ext_mypaint)
 if (UNIX AND NOT APPLE)
     add_subdirectory(ext_fcitx-qt)
 endif()
+
+add_subdirectory(ext_webp)
diff --git a/3rdparty/ext_webp/0001-Fix-CMake-targets-install-directory.patch b/3rdparty/ext_webp/0001-Fix-CMake-targets-install-directory.patch
new file mode 100644
index 0000000000..9a97c7d793
--- /dev/null
+++ b/3rdparty/ext_webp/0001-Fix-CMake-targets-install-directory.patch
@@ -0,0 +1,25 @@
+From 63b57144f0cfea10abbd50674bc20a2fa33cb549 Mon Sep 17 00:00:00 2001
+From: "L. E. Segovia" <amy at amyspark.me>
+Date: Sun, 6 Jun 2021 13:47:25 +0000
+Subject: [PATCH] Fix CMake targets install directory
+
+---
+ CMakeLists.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/CMakeLists.txt b/CMakeLists.txt
+index 7c0e98e..3054fbc 100644
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -668,7 +668,7 @@ install(TARGETS ${INSTALLED_LIBRARIES}
+         ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+         LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+         RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+-set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/)
++set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME})
+ install(EXPORT ${PROJECT_NAME}Targets
+         NAMESPACE ${PROJECT_NAME}::
+         DESTINATION ${ConfigPackageLocation})
+-- 
+2.31.1
+
diff --git a/3rdparty/ext_webp/CMakeLists.txt b/3rdparty/ext_webp/CMakeLists.txt
new file mode 100644
index 0000000000..88f3a65ecf
--- /dev/null
+++ b/3rdparty/ext_webp/CMakeLists.txt
@@ -0,0 +1,15 @@
+SET(EXTPREFIX_webp "${EXTPREFIX}" )
+
+ExternalProject_Add( ext_webp
+    DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
+    URL https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.2.0.tar.gz
+    URL_HASH SHA512=c46b41899a543cc80914c89646dd607dbb5d025a9727dd83ef70994b9310eedc697666bd812141a90aa16632a9a354a031d9360a9ee1112295c7e154e69f0b74
+
+    PATCH_COMMAND  ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/0001-Fix-CMake-targets-install-directory.patch
+
+    # Google specifies types for most of its library components
+    # forcing a shared lib makes it not export a .lib on MSVC
+    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTPREFIX_webp} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} -DBUILD_SHARED_LIBS=OFF ${GLOBAL_PROFILE}
+
+    UPDATE_COMMAND ""
+)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f299712f79..f1339af7c9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -697,6 +697,18 @@ endif()
 ############################
 ###########################
 
+
+find_package(WebP 0.6.1)
+set_package_properties(WebP PROPERTIES
+    URL "https://developers.google.com/speed/webp"
+    TYPE OPTIONAL
+    PURPOSE "Required by the WebP plugin"
+)
+if(WebP_FOUND)
+    get_target_property(WebP_LIBRARY WebP::webp "LOCATION")
+    list (APPEND ANDROID_EXTRA_LIBS ${WebP_LIBRARY})
+endif()
+
 ##
 ## Test for KSeExpr
 ##
diff --git a/build-tools/windows/build-msvc.cmd b/build-tools/windows/build-msvc.cmd
index 01c011f7fe..d2ec5b1ac0 100644
--- a/build-tools/windows/build-msvc.cmd
+++ b/build-tools/windows/build-msvc.cmd
@@ -861,7 +861,7 @@ set EXT_TARGETS=%EXT_TARGETS% ocio openexr png icoutils tiff gsl vc libraw
 set EXT_TARGETS=%EXT_TARGETS% giflib qt kwindowsystem drmingw gmic freetype poppler 
 set EXT_TARGETS=%EXT_TARGETS% python sip pyqt
 set EXT_TARGETS=%EXT_TARGETS% lzma quazip openjpeg libde265 libx265 libheif
-set EXT_TARGETS=%EXT_TARGETS% seexpr mypaint
+set EXT_TARGETS=%EXT_TARGETS% seexpr mypaint webp
 
 for %%a in (%EXT_TARGETS%) do (
     echo Building ext_%%a...
diff --git a/build-tools/windows/build.cmd b/build-tools/windows/build.cmd
index aba319ba4d..91f442f4fe 100644
--- a/build-tools/windows/build.cmd
+++ b/build-tools/windows/build.cmd
@@ -891,7 +891,7 @@ set EXT_TARGETS=%EXT_TARGETS% ocio openexr png icoutils tiff gsl vc libraw
 set EXT_TARGETS=%EXT_TARGETS% giflib qt kwindowsystem drmingw gmic freetype poppler 
 set EXT_TARGETS=%EXT_TARGETS% python sip pyqt
 set EXT_TARGETS=%EXT_TARGETS% lzma quazip openjpeg libde265 libx265 libheif
-set EXT_TARGETS=%EXT_TARGETS% seexpr mypaint 
+set EXT_TARGETS=%EXT_TARGETS% seexpr mypaint webp
 
 for %%a in (%EXT_TARGETS%) do (
     echo Building ext_%%a...
diff --git a/cmake/modules/FindWebP.cmake b/cmake/modules/FindWebP.cmake
new file mode 100644
index 0000000000..b652243ec3
--- /dev/null
+++ b/cmake/modules/FindWebP.cmake
@@ -0,0 +1,224 @@
+# Copyright (C) 2020 Sony Interactive Entertainment Inc.
+# Copyright (C) 2012 Raphael Kubo da Costa <rakuco at webkit.org>
+# Copyright (C) 2013 Igalia S.L.
+# Copyright (C) 2021 L. E. Segovia <amy at amyspark.me>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
+# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#[=======================================================================[.rst:
+FindWebP
+--------------
+
+Find WebP headers and libraries.
+
+Imported Targets
+^^^^^^^^^^^^^^^^
+
+``WebP::webp``
+  The WebP library, if found.
+
+``WebP::webpdemux``
+  The WebP demux library, if found.
+
+``WebP::webpmux``
+  The WebP mux library, if found.
+
+``WebP::webpdecoder``
+  The WebP decoder library, if found.
+
+Result Variables
+^^^^^^^^^^^^^^^^
+
+This will define the following variables in your project:
+
+``WebP_FOUND``
+  true if (the requested version of) WebP is available.
+``WebP_VERSION``
+  the version of WebP.
+``WebP_LIBRARIES``
+  the libraries to link against to use WebP.
+``WebP_INCLUDE_DIRS``
+  where to find the WebP headers.
+``WebP_COMPILE_OPTIONS``
+  this should be passed to target_compile_options(), if the
+  target is not used for linking
+
+#]=======================================================================]
+
+include(FindPackageHandleStandardArgs)
+
+set(PC_WEBP_CONFIG_DIR)
+foreach(_dir ${CMAKE_PREFIX_PATH})
+    list(APPEND PC_WEBP_CONFIG_DIR ${_dir}/share/WebP/cmake)
+endforeach()
+mark_as_advanced(PC_WEBP_CONFIG_DIR)
+
+find_package(WebP QUIET NO_MODULE
+    HINTS ${PC_WEBP_CONFIG_DIR} /usr/share/WebP/cmake /usr/local/share/WebP/cmake
+)
+mark_as_advanced(WebP_DIR)
+
+# if we found the WebP CMake package then we are done, and
+# can print what we found and return.
+if(WebP_FOUND)
+    find_package_handle_standard_args(WebP HANDLE_COMPONENTS CONFIG_MODE)
+    message(STATUS "WebP found using CMake Config module.")
+    return()
+endif()
+
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_WEBP QUIET libwebp)
+set(WebP_COMPILE_OPTIONS ${PC_WEBP_CFLAGS_OTHER})
+set(WebP_VERSION ${PC_WEBP_CFLAGS_VERSION})
+
+find_path(WebP_INCLUDE_DIR
+    NAMES webp/decode.h
+    HINTS ${PC_WEBP_INCLUDEDIR} ${PC_WEBP_INCLUDE_DIRS}
+)
+
+find_library(WebP_LIBRARY
+    NAMES ${WebP_NAMES} webp
+    HINTS ${PC_WEBP_LIBDIR} ${PC_WEBP_LIBRARY_DIRS}
+)
+
+# There's nothing in the WebP headers that could be used to detect the exact
+# WebP version being used so don't attempt to do so. A version can only be found
+# through pkg-config
+if ("${WebP_FIND_VERSION}" VERSION_GREATER "${WebP_VERSION}")
+    if (WebP_VERSION)
+        message(FATAL_ERROR "Required version (" ${WebP_FIND_VERSION} ") is higher than found version (" ${WebP_VERSION} ")")
+    else ()
+        message(WARNING "Cannot determine WebP version without pkg-config")
+    endif ()
+endif ()
+
+# Find components
+if (WebP_INCLUDE_DIR AND WebP_LIBRARY)
+    set(_WebP_REQUIRED_LIBS_FOUND ON)
+    set(WebP_LIBS_FOUND "WebP (required): ${WebP_LIBRARY}")
+else ()
+    set(_WebP_REQUIRED_LIBS_FOUND OFF)
+    set(WebP_LIBS_NOT_FOUND "WebP (required)")
+endif ()
+
+find_library(WebP_DEMUX_LIBRARY
+    NAMES ${WebP_DEMUX_NAMES} webpdemux
+    HINTS ${PC_WEBP_LIBDIR} ${PC_WEBP_LIBRARY_DIRS}
+)
+
+if (WebP_DEMUX_LIBRARY)
+    list(APPEND WebP_LIBS_FOUND "webpdemux: ${WebP_DEMUX_LIBRARY}")
+else ()
+    list(APPEND WebP_LIBS_NOT_FOUND "webpdemux")
+endif ()
+
+find_library(WebP_MUX_LIBRARY
+    NAMES ${WebP_MUX_NAMES} webpmux
+    HINTS ${PC_WEBP_LIBDIR} ${PC_WEBP_LIBRARY_DIRS}
+)
+
+if (WebP_MUX_LIBRARY)
+    list(APPEND WebP_LIBS_FOUND "webpmux: ${WebP_MUX_LIBRARY}")
+else ()
+    list(APPEND WebP_LIBS_NOT_FOUND "webpmux")
+endif ()
+
+find_library(WebP_DECODER_LIBRARY
+    NAMES ${WebP_DECODER_NAMES} webpdecoder
+    HINTS ${PC_WEBP_LIBDIR} ${PC_WEBP_LIBRARY_DIRS}
+)
+
+if (WebP_DECODER_LIBRARY)
+    list(APPEND WebP_LIBS_FOUND "webpdecoder: ${WebP_DECODER_LIBRARY}")
+else ()
+    list(APPEND WebP_LIBS_NOT_FOUND "webpdecoder")
+endif ()
+
+if (NOT WebP_FIND_QUIETLY)
+    if (WebP_LIBS_FOUND)
+        message(STATUS "Found the following WebP libraries:")
+        foreach (found ${WebP_LIBS_FOUND})
+            message(STATUS " ${found}")
+        endforeach ()
+    endif ()
+    if (WebP_LIBS_NOT_FOUND)
+        message(STATUS "The following WebP libraries were not found:")
+        foreach (found ${WebP_LIBS_NOT_FOUND})
+            message(STATUS " ${found}")
+        endforeach ()
+    endif ()
+endif ()
+
+find_package_handle_standard_args(WebP
+    FOUND_VAR WebP_FOUND
+    REQUIRED_VARS WebP_INCLUDE_DIR WebP_LIBRARY _WebP_REQUIRED_LIBS_FOUND
+    VERSION_VAR WebP_VERSION
+)
+
+if (WebP_LIBRARY AND NOT TARGET WebP::webp)
+    add_library(WebP::webp UNKNOWN IMPORTED GLOBAL)
+    set_target_properties(WebP::webp PROPERTIES
+        IMPORTED_LOCATION "${WebP_LIBRARY}"
+        INTERFACE_COMPILE_OPTIONS "${WebP_COMPILE_OPTIONS}"
+        INTERFACE_INCLUDE_DIRECTORIES "${WebP_INCLUDE_DIR}"
+    )
+endif ()
+
+if (WebP_DEMUX_LIBRARY AND NOT TARGET WebP::webpdemux)
+    add_library(WebP::webpdemux UNKNOWN IMPORTED GLOBAL)
+    set_target_properties(WebP::webpdemux PROPERTIES
+        IMPORTED_LOCATION "${WebP_DEMUX_LIBRARY}"
+        INTERFACE_COMPILE_OPTIONS "${WebP_COMPILE_OPTIONS}"
+        INTERFACE_INCLUDE_DIRECTORIES "${WebP_INCLUDE_DIR}"
+    )
+endif ()
+
+if (WebP_MUX_LIBRARY AND NOT TARGET WebP::webpmux)
+    add_library(WebP::webpmux UNKNOWN IMPORTED GLOBAL)
+    set_target_properties(WebP::webpmux PROPERTIES
+        IMPORTED_LOCATION "${WebP_MUX_LIBRARY}"
+        INTERFACE_COMPILE_OPTIONS "${WebP_COMPILE_OPTIONS}"
+        INTERFACE_INCLUDE_DIRECTORIES "${WebP_INCLUDE_DIR}"
+    )
+endif ()
+
+if (WebP_DECODER_LIBRARY AND NOT TARGET WebP::webpdecoder)
+    add_library(WebP::webpdecoder UNKNOWN IMPORTED GLOBAL)
+    set_target_properties(WebP::webpdecoder PROPERTIES
+        IMPORTED_LOCATION "${WebP_DECODER_LIBRARY}"
+        INTERFACE_COMPILE_OPTIONS "${WebP_COMPILE_OPTIONS}"
+        INTERFACE_INCLUDE_DIRECTORIES "${WebP_INCLUDE_DIR}"
+    )
+endif ()
+
+mark_as_advanced(
+    WebP_INCLUDE_DIR
+    WebP_LIBRARY
+    WebP_DEMUX_LIBRARY
+    WebP_MUX_LIBRARY
+    WebP_DECODER_LIBRARY
+)
+
+if (WebP_FOUND)
+    set(WebP_LIBRARIES ${WebP_LIBRARY} ${WebP_DEMUX_LIBRARY} ${WebP_MUX_LIBRARY} ${WebP_DECODER_LIBRARY})
+    set(WebP_INCLUDE_DIRS ${WebP_INCLUDE_DIR})
+endif ()
diff --git a/packaging/android/androidbuild.sh b/packaging/android/androidbuild.sh
index 8438f0b8d6..7a7c32b374 100755
--- a/packaging/android/androidbuild.sh
+++ b/packaging/android/androidbuild.sh
@@ -99,6 +99,7 @@ build_ext() {
     cmake --build . --config $BUILD_TYPE --target ext_eigen3 -- -j$PROC_COUNT
     cmake --build . --config $BUILD_TYPE --target ext_seexpr -- -j$PROC_COUNT
     cmake --build . --config $BUILD_TYPE --target ext_mypaint -- -j$PROC_COUNT
+    cmake --build . --config $BUILD_TYPE --target ext_webp -- -j$PROC_COUNT
 
     cd $BUILD_ROOT
 }
diff --git a/packaging/linux/appimage/build-deps.sh b/packaging/linux/appimage/build-deps.sh
index 91f6116e42..84e5248e39 100755
--- a/packaging/linux/appimage/build-deps.sh
+++ b/packaging/linux/appimage/build-deps.sh
@@ -122,3 +122,4 @@ cmake --build . --config RelWithDebInfo --target ext_libheif
 cmake --build . --config RelWithDebInfo --target ext_seexpr
 cmake --build . --config RelWithDebInfo --target ext_mypaint
 cmake --build . --config RelWithDebInfo --target ext_fcitx-qt5
+cmake --build . --config RelWithDebInfo --target ext_webp
diff --git a/packaging/macos/osxbuild.sh b/packaging/macos/osxbuild.sh
index 41d481b81d..a3235b67e9 100755
--- a/packaging/macos/osxbuild.sh
+++ b/packaging/macos/osxbuild.sh
@@ -392,7 +392,7 @@ build_3rdparty () {
 
     cmake_3rdparty ext_seexpr
     cmake_3rdparty ext_mypaint
-
+    cmake_3rdparty ext_webp
 
 
     ## All builds done, creating a new install onlydeps install dir
diff --git a/plugins/impex/CMakeLists.txt b/plugins/impex/CMakeLists.txt
index c94674b381..5f31eb3ae9 100644
--- a/plugins/impex/CMakeLists.txt
+++ b/plugins/impex/CMakeLists.txt
@@ -56,3 +56,7 @@ if (HEIF_FOUND)
 endif()
 
 add_subdirectory(krz)
+
+if (WebP_FOUND)
+    add_subdirectory(webp)
+endif()
diff --git a/plugins/impex/qimageio/krita_qimageio_export.json b/plugins/impex/qimageio/krita_qimageio_export.json
index b7009943ef..420c7c74ff 100644
--- a/plugins/impex/qimageio/krita_qimageio_export.json
+++ b/plugins/impex/qimageio/krita_qimageio_export.json
@@ -2,11 +2,11 @@
     "Id": "Krita QImageIO Export Filter",
     "NoDisplay": "true",
     "Type": "Service",
-    "X-KDE-Export": "image/bmp,image/x-xpixmap,image/x-xbitmap,image/webp,image/vnd.microsoft.icon,image/x-portable-pixmap,image/x-portable-graymap,image/x-portable-bitmap",
+    "X-KDE-Export": "image/bmp,image/x-xpixmap,image/x-xbitmap,image/vnd.microsoft.icon,image/x-portable-pixmap,image/x-portable-graymap,image/x-portable-bitmap",
     "X-KDE-Library": "kritaqimageioexport",
     "X-KDE-ServiceTypes": [
         "Krita/FileFilter"
     ],
     "X-KDE-Weight": "1",
-    "X-KDE-Extensions" : "bmp,xpm,xbm,ico,webp,ppm"
+    "X-KDE-Extensions" : "bmp,xpm,xbm,ico,ppm"
 }
diff --git a/plugins/impex/qimageio/krita_qimageio_import.json b/plugins/impex/qimageio/krita_qimageio_import.json
index 9fe6cd908f..4fa358b640 100644
--- a/plugins/impex/qimageio/krita_qimageio_import.json
+++ b/plugins/impex/qimageio/krita_qimageio_import.json
@@ -2,11 +2,11 @@
     "Id": "Krita QImageIO Import Filter",
     "NoDisplay": "true",
     "Type": "Service",
-    "X-KDE-Import": "image/bmp,image/x-xpixmap,image/x-xbitmap,image/webp,image/vnd.microsoft.icon,image/x-portable-pixmap,image/x-portable-graymap,image/x-portable-bitmap",
+    "X-KDE-Import": "image/bmp,image/x-xpixmap,image/x-xbitmap,image/vnd.microsoft.icon,image/x-portable-pixmap,image/x-portable-graymap,image/x-portable-bitmap",
     "X-KDE-Library": "kritaqimageioimport",
     "X-KDE-ServiceTypes": [
         "Krita/FileFilter"
     ],
     "X-KDE-Weight": "1",
-    "X-KDE-Extensions" : "bmp,webp,xbm,ico,ppm"
+    "X-KDE-Extensions" : "bmp,xbm,ico,ppm"
 }
diff --git a/plugins/impex/webp/CMakeLists.txt b/plugins/impex/webp/CMakeLists.txt
new file mode 100644
index 0000000000..6f59d66c38
--- /dev/null
+++ b/plugins/impex/webp/CMakeLists.txt
@@ -0,0 +1,27 @@
+if (WebP_FOUND)
+    set(kritawebpexport_SOURCES
+        dlg_webp_export.cpp
+        kis_webp_export.cpp
+    )
+    ki18n_wrap_ui(kritawebpexport_SOURCES dlg_webp_export.ui)
+
+    add_library(kritawebpexport MODULE ${kritawebpexport_SOURCES})
+    target_include_directories(kritawebpexport PRIVATE ${WebP_INCLUDE_DIRS})
+    target_link_libraries(kritawebpexport kritaui kritalibkra kritaimpex WebP::webp)
+
+    install(TARGETS kritawebpexport  DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
+
+
+    set(kritawebpimport_SOURCES
+        dlg_webp_import.cpp
+        kis_webp_import.cpp
+    )
+    ki18n_wrap_ui(kritawebpimport_SOURCES dlg_webp_import.ui)
+
+    add_library(kritawebpimport MODULE ${kritawebpimport_SOURCES})
+    target_include_directories(kritawebpimport PRIVATE ${WebP_INCLUDE_DIRS})
+    target_link_libraries(kritawebpimport kritaui WebP::webp)
+
+    install(TARGETS kritawebpimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
+    install(PROGRAMS krita_webp.desktop DESTINATION ${XDG_APPS_INSTALL_DIR})
+endif()
diff --git a/plugins/impex/webp/dlg_webp_export.cpp b/plugins/impex/webp/dlg_webp_export.cpp
new file mode 100644
index 0000000000..a64af28e36
--- /dev/null
+++ b/plugins/impex/webp/dlg_webp_export.cpp
@@ -0,0 +1,166 @@
+/*
+ * This file is part of Krita
+ *
+ * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <webp/encode.h>
+
+#include <kis_properties_configuration.h>
+
+#include "dlg_webp_export.h"
+
+KisDlgWebPExport::KisDlgWebPExport(QWidget *parent)
+    : KisConfigWidget(parent)
+{
+    setupUi(this);
+
+#if WEBP_ENCODER_ABI_VERSION < 0x020f
+    qMin->setEnabled(false);
+    qMax->setEnabled(false);
+#endif
+
+    preset->addItem(i18nc("WebP presets", "Default"), WEBP_PRESET_DEFAULT);
+    preset->addItem(i18nc("WebP presets", "Portrait"), WEBP_PRESET_PICTURE);
+    preset->addItem(i18nc("WebP presets", "Outdoor photo"), WEBP_PRESET_PHOTO);
+    preset->addItem(i18nc("WebP presets", "Line drawing"), WEBP_PRESET_DRAWING);
+    preset->addItem(i18nc("WebP presets", "Icon"), WEBP_PRESET_ICON);
+    preset->addItem(i18nc("WebP presets", "Text"), WEBP_PRESET_TEXT);
+
+    filterType->addItem(i18nc("WebP filters", "Simple"), 0);
+    filterType->addItem(i18nc("WebP filters", "Strong"), 1);
+
+    alphaCompression->addItem(i18nc("WebP alpha plane compression", "None"), 0);
+    alphaCompression->addItem(i18nc("WebP alpha plane compression", "Lossless"), 1);
+
+    preprocessing->addItem(i18nc("WebP preprocessing filters", "None"), 0);
+    preprocessing->addItem(i18nc("WebP preprocessing filters", "Segment-smooth"), 1);
+    preprocessing->addItem(i18nc("WebP preprocessing filters", "Pseudo-random dithering"), 2);
+
+    targetPSNR->setDisplayUnit(false);
+    targetPSNR->setSuffix(" dB");
+
+    connect(preset, SIGNAL(currentIndexChanged(int)), this, SLOT(changePreset(void)));
+    connect(lossless, SIGNAL(toggled(bool)), this, SLOT(changePreset(void)));
+}
+
+void KisDlgWebPExport::setConfiguration(const KisPropertiesConfigurationSP cfg)
+{
+    preset->setCurrentIndex(cfg->getInt("preset", 0));
+    lossless->setChecked(cfg->getBool("lossless", true));
+    quality->setValue(cfg->getDouble("quality", 75.0));
+    tradeoff->setValue(cfg->getInt("method", 4));
+    dithering->setChecked(cfg->getBool("dithering", true));
+
+    targetSize->setValue(cfg->getInt("target_size", 0));
+    targetPSNR->setValue(cfg->getDouble("target_PSNR", 0.0));
+    segments->setValue(cfg->getInt("segments", 4));
+    snsStrength->setValue(cfg->getInt("sns_strength", 50));
+    filterStrength->setValue(cfg->getInt("filter_strength", 60));
+    filterSharpness->setValue(cfg->getInt("filter_sharpness", 0));
+    filterType->setCurrentIndex(cfg->getInt("filter_type", 1));
+    autofilter->setChecked(cfg->getBool("autofilter", false));
+    alphaCompression->setCurrentIndex(cfg->getInt("alpha_compression", 1));
+    alphaFiltering->setValue(cfg->getInt("alpha_filtering", 1));
+    alphaQuality->setValue(cfg->getInt("alpha_quality", 100));
+    pass->setValue(cfg->getInt("pass", 1));
+    showCompressed->setChecked(cfg->getBool("show_compressed", false));
+    preprocessing->setCurrentIndex(cfg->getInt("preprocessing", 0));
+    partitions->setValue(cfg->getInt("partitions", 0));
+    partitionLimit->setValue(cfg->getInt("partition_limit", 0));
+    emulateJPEGSize->setChecked(cfg->getBool("emulate_jpeg_size", false));
+    threadLevel->setChecked(cfg->getBool("thread_level", false));
+    lowMemory->setChecked(cfg->getBool("low_memory", false));
+    nearLossless->setValue(cfg->getInt("near_lossless", 100));
+    exact->setChecked(cfg->getBool("exact", 0));
+    useSharpYUV->setChecked(cfg->getBool("use_sharp_yuv", false));
+#if WEBP_ENCODER_ABI_VERSION >= 0x020f
+    qMin->setValue(cfg->getInt("qmin", 0));
+    qMax->setValue(cfg->getInt("qmax", 100));
+#endif
+}
+
+void KisDlgWebPExport::changePreset()
+{
+    WebPConfig preset;
+
+    if (!WebPConfigPreset(&preset, static_cast<WebPPreset>(this->preset->currentData().value<int>()), static_cast<float>(this->quality->value()))) {
+        return;
+    }
+
+    if (this->lossless->isChecked() && !WebPConfigLosslessPreset(&preset, this->tradeoff->value())) {
+        return;
+    }
+
+    quality->setValue(static_cast<double>(preset.quality));
+    tradeoff->setValue(preset.method);
+
+    targetSize->setValue(preset.target_size);
+    targetPSNR->setValue(static_cast<double>(preset.target_PSNR));
+    segments->setValue(preset.segments);
+    snsStrength->setValue(preset.sns_strength);
+    filterStrength->setValue(preset.filter_strength);
+    filterSharpness->setValue(preset.filter_sharpness);
+    filterType->setCurrentIndex(preset.filter_type);
+    autofilter->setChecked(preset.autofilter == 1);
+    alphaCompression->setCurrentIndex(alphaCompression->findData(preset.alpha_compression));
+    alphaFiltering->setValue(preset.alpha_filtering);
+    alphaQuality->setValue(preset.alpha_quality);
+    pass->setValue(preset.pass);
+    showCompressed->setChecked(preset.show_compressed == 1);
+    preprocessing->setCurrentIndex(preprocessing->findData(preset.preprocessing));
+    partitions->setValue(preset.partitions);
+    partitionLimit->setValue(preset.partition_limit);
+    emulateJPEGSize->setChecked(preset.emulate_jpeg_size == 1);
+    threadLevel->setChecked(preset.thread_level > 0);
+    lowMemory->setChecked(preset.low_memory == 1);
+    nearLossless->setValue(preset.near_lossless);
+    exact->setChecked(preset.exact == 1);
+    useSharpYUV->setChecked(preset.use_sharp_yuv);
+#if WEBP_ENCODER_ABI_VERSION >= 0x020f
+    qMin->setValue(preset.qmin);
+    qMax->setValue(preset.qmax);
+#endif
+}
+
+KisPropertiesConfigurationSP KisDlgWebPExport::configuration() const
+{
+    KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
+
+    cfg->setProperty("preset", preset->currentIndex());
+    cfg->setProperty("lossless", lossless->isChecked());
+    cfg->setProperty("quality", quality->value());
+    cfg->setProperty("method", tradeoff->value());
+    cfg->setProperty("dithering", dithering->isChecked());
+
+    cfg->setProperty("target_size", targetSize->value());
+    cfg->setProperty("target_PSNR", targetPSNR->value());
+    cfg->setProperty("segments", segments->value());
+    cfg->setProperty("sns_strength", snsStrength->value());
+    cfg->setProperty("filter_strength", filterStrength->value());
+    cfg->setProperty("filter_sharpness", filterSharpness->value());
+    cfg->setProperty("filter_type", filterType->currentData().value<int>());
+    cfg->setProperty("autofilter", autofilter->isChecked());
+    cfg->setProperty("alpha_compression", alphaCompression->currentData().value<int>());
+    cfg->setProperty("alpha_filtering", alphaFiltering->value());
+    cfg->setProperty("alpha_quality", alphaQuality->value());
+    cfg->setProperty("pass", pass->value());
+    cfg->setProperty("show_compressed", showCompressed->isChecked());
+    cfg->setProperty("preprocessing", preprocessing->currentData().value<int>());
+    cfg->setProperty("partitions", partitions->value());
+    cfg->setProperty("partition_limit", partitionLimit->value());
+    cfg->setProperty("emulate_jpeg_size", emulateJPEGSize->isChecked());
+    cfg->setProperty("thread_level", threadLevel->isChecked());
+    cfg->setProperty("low_memory", lowMemory->isChecked());
+    cfg->setProperty("near_lossless", nearLossless->value());
+    cfg->setProperty("exact", exact->isChecked());
+    cfg->setProperty("use_sharp_yuv", useSharpYUV->isChecked());
+#if WEBP_ENCODER_ABI_VERSION >= 0x020f
+    cfg->setProperty("qmin", qMin->value());
+    cfg->setProperty("qmax", qMax->value());
+#endif
+
+    return cfg;
+}
diff --git a/plugins/impex/webp/dlg_webp_export.h b/plugins/impex/webp/dlg_webp_export.h
new file mode 100644
index 0000000000..1844d6181f
--- /dev/null
+++ b/plugins/impex/webp/dlg_webp_export.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of Krita
+ *
+ * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef DLG_WEBP_EXPORT_H
+#define DLG_WEBP_EXPORT_H
+
+#include <kis_config_widget.h>
+
+#include "ui_dlg_webp_export.h"
+
+class KisDlgWebPExport : public KisConfigWidget, public Ui::DlgWebPExport
+{
+    Q_OBJECT
+
+public:
+    KisDlgWebPExport(QWidget *parent);
+    void setConfiguration(const KisPropertiesConfigurationSP cfg) override;
+    KisPropertiesConfigurationSP configuration() const override;
+private Q_SLOTS:
+    void changePreset();
+};
+
+#endif // DLG_WEBP_EXPORT_H
diff --git a/plugins/impex/webp/dlg_webp_export.ui b/plugins/impex/webp/dlg_webp_export.ui
new file mode 100644
index 0000000000..2a82440498
--- /dev/null
+++ b/plugins/impex/webp/dlg_webp_export.ui
@@ -0,0 +1,822 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <author>
+  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+  SPDX-License-Identifier: GPL-3.0-or-later
+ </author>
+ <class>DlgWebPExport</class>
+ <widget class="QWidget" name="DlgWebPExport">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>557</width>
+    <height>529</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="general">
+      <attribute name="title">
+       <string>General</string>
+      </attribute>
+      <layout class="QFormLayout" name="formLayout">
+       <property name="formAlignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+       <item row="0" column="0">
+        <widget class="QLabel" name="lblPreset">
+         <property name="text">
+          <string>Preset</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QComboBox" name="preset"/>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="lblLossless">
+         <property name="text">
+          <string>Lossless compression</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QCheckBox" name="lossless">
+         <property name="text">
+          <string>Enabled</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QLabel" name="lblQuality">
+         <property name="toolTip">
+          <string>If Lossless is enabled: 0 = fastest but larger files, 100 = slower but better. If Lossless is disabled: 0 = smallest size, 100 = largest size.</string>
+         </property>
+         <property name="text">
+          <string>Quality</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="KisDoubleSliderSpinBox" name="quality">
+         <property name="toolTip">
+          <string>If Lossless is enabled: 0 = fastest but larger files, 100 = slower but better. If Lossless is disabled: 0 = smallest size, 100 = largest size.</string>
+         </property>
+         <property name="suffix">
+          <string>%</string>
+         </property>
+         <property name="maximum">
+          <number>100</number>
+         </property>
+         <property name="value">
+          <number>100</number>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="0">
+        <widget class="QLabel" name="lblTradeoff">
+         <property name="text">
+          <string>Tradeoff</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="1">
+        <layout class="QGridLayout" name="gridLayout_3">
+         <item row="0" column="0">
+          <widget class="QLabel" name="lblFaster">
+           <property name="text">
+            <string>Faster</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QLabel" name="lblSlowerBetter">
+           <property name="text">
+            <string>Slower/Better</string>
+           </property>
+           <property name="alignment">
+            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" colspan="2">
+          <widget class="KisSliderSpinBox" name="tradeoff">
+           <property name="maximum">
+            <number>6</number>
+           </property>
+           <property name="value">
+            <number>3</number>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="4" column="0">
+        <widget class="QLabel" name="lblDithering">
+         <property name="text">
+          <string>Dithering</string>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="1">
+        <widget class="QCheckBox" name="dithering">
+         <property name="text">
+          <string>Enabled</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="advanced">
+      <attribute name="title">
+       <string>Advanced</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <widget class="QScrollArea" name="scrollArea">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>535</width>
+           <height>476</height>
+          </size>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::NoFrame</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Plain</enum>
+         </property>
+         <property name="horizontalScrollBarPolicy">
+          <enum>Qt::ScrollBarAlwaysOff</enum>
+         </property>
+         <property name="sizeAdjustPolicy">
+          <enum>QAbstractScrollArea::AdjustToContents</enum>
+         </property>
+         <property name="widgetResizable">
+          <bool>true</bool>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignHCenter|Qt::AlignTop</set>
+         </property>
+         <widget class="QWidget" name="scrollAreaWidgetContents">
+          <property name="geometry">
+           <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>521</width>
+            <height>858</height>
+           </rect>
+          </property>
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <layout class="QFormLayout" name="formLayout_2">
+           <property name="formAlignment">
+            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+           </property>
+           <item row="0" column="0">
+            <widget class="QLabel" name="lblTargetSize">
+             <property name="toolTip">
+              <string>If non-zero, sets the desired target size in bytes. Takes precedence over the compression parameters.</string>
+             </property>
+             <property name="text">
+              <string>Target Size</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QSpinBox" name="targetSize">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>If non-zero, sets the desired target size in bytes. Takes precedence over the compression parameters.</string>
+             </property>
+             <property name="suffix">
+              <string> kB</string>
+             </property>
+             <property name="maximum">
+              <number>1000000000</number>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="lblTargetPSNR">
+             <property name="toolTip">
+              <string>If non-zero, specifies the minimal distortion to try to achieve. Takes precedence over Target Size.</string>
+             </property>
+             <property name="text">
+              <string>Target PSNR</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="KisDoubleParseUnitSpinBox" name="targetPSNR">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>If non-zero, specifies the minimal distortion to try to achieve. Takes precedence over Target Size.</string>
+             </property>
+             <property name="decimals">
+              <number>2</number>
+             </property>
+             <property name="maximum">
+              <double>100.000000000000000</double>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="lblSegments">
+             <property name="toolTip">
+              <string>Maximum number of segments (1 to 4).</string>
+             </property>
+             <property name="text">
+              <string>Segments</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="KisSliderSpinBox" name="segments">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Maximum number of segments (1 to 4).</string>
+             </property>
+             <property name="minimum">
+              <number>1</number>
+             </property>
+             <property name="maximum">
+              <number>4</number>
+             </property>
+             <property name="pageStep" stdset="0">
+              <number>2</number>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="lblSNSStrength">
+             <property name="toolTip">
+              <string>Spatial Noise Shaping: 0 = off, 100 = maximum.</string>
+             </property>
+             <property name="text">
+              <string>SNS Strength</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="KisSliderSpinBox" name="snsStrength">
+             <property name="toolTip">
+              <string>Spatial Noise Shaping: 0 = off, 100 = maximum.</string>
+             </property>
+             <property name="suffix">
+              <string>%</string>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="0">
+            <widget class="QLabel" name="lblFilterStrength">
+             <property name="toolTip">
+              <string>Filter strength: 0 = off, 100 = strongest.</string>
+             </property>
+             <property name="text">
+              <string>Filter Strength</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="1">
+            <widget class="KisSliderSpinBox" name="filterStrength">
+             <property name="toolTip">
+              <string>Filter strength: 0 = off, 100 = strongest.</string>
+             </property>
+             <property name="suffix">
+              <string>%</string>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="0">
+            <widget class="QLabel" name="lblFilterSharpness">
+             <property name="toolTip">
+              <string>Filter sharpness: 0 = off, 7 = least sharp.</string>
+             </property>
+             <property name="text">
+              <string>Filter Sharpness</string>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="1">
+            <widget class="KisSliderSpinBox" name="filterSharpness">
+             <property name="toolTip">
+              <string>Filter sharpness: 0 = off, 7 = least sharp.</string>
+             </property>
+             <property name="maximum">
+              <number>7</number>
+             </property>
+             <property name="pageStep" stdset="0">
+              <number>2</number>
+             </property>
+            </widget>
+           </item>
+           <item row="6" column="0">
+            <widget class="QLabel" name="lblFilterType">
+             <property name="toolTip">
+              <string>0 = Simple, 1 = Strong. Only used if Filter Strength is higher than 0 or Auto Adjust Filter is enabled.</string>
+             </property>
+             <property name="text">
+              <string>Filter Type</string>
+             </property>
+            </widget>
+           </item>
+           <item row="6" column="1">
+            <widget class="QComboBox" name="filterType">
+             <property name="toolTip">
+              <string>0 = Simple, 1 = Strong. Only used if Filter Strength is higher than 0 or Auto Adjust Filter is enabled.</string>
+             </property>
+            </widget>
+           </item>
+           <item row="7" column="0">
+            <widget class="QLabel" name="lblAutofilter">
+             <property name="text">
+              <string>Auto Adjust Filter Strength</string>
+             </property>
+            </widget>
+           </item>
+           <item row="7" column="1">
+            <widget class="QCheckBox" name="autofilter">
+             <property name="text">
+              <string>Enabled</string>
+             </property>
+            </widget>
+           </item>
+           <item row="8" column="0">
+            <widget class="QLabel" name="lblAlphaCompression">
+             <property name="toolTip">
+              <string>Algorithm for encoding the alpha plane (0 = None, 1 = compressed with WebP lossless). Default is 1.</string>
+             </property>
+             <property name="text">
+              <string>Alpha Plane Compression</string>
+             </property>
+            </widget>
+           </item>
+           <item row="8" column="1">
+            <widget class="QComboBox" name="alphaCompression"/>
+           </item>
+           <item row="9" column="0">
+            <widget class="QLabel" name="lblAlphaFiltering">
+             <property name="text">
+              <string>Predictive Filtering for Alpha Plane</string>
+             </property>
+            </widget>
+           </item>
+           <item row="9" column="1">
+            <layout class="QGridLayout" name="gridLayout">
+             <item row="0" column="1">
+              <widget class="QLabel" name="lblFast">
+               <property name="text">
+                <string>Fast</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="0">
+              <widget class="QLabel" name="lblNone">
+               <property name="text">
+                <string>None</string>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="2">
+              <widget class="QLabel" name="lblBest">
+               <property name="text">
+                <string>Best</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0" colspan="3">
+              <widget class="KisSliderSpinBox" name="alphaFiltering">
+               <property name="maximum">
+                <number>2</number>
+               </property>
+               <property name="pageStep" stdset="0">
+                <number>1</number>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item row="10" column="0">
+            <widget class="QLabel" name="lblAlphaQuality">
+             <property name="toolTip">
+              <string>0 = smallest size, 100 = lossless. Default is 100.</string>
+             </property>
+             <property name="text">
+              <string>Alpha Plane Quality</string>
+             </property>
+            </widget>
+           </item>
+           <item row="10" column="1">
+            <widget class="KisSliderSpinBox" name="alphaQuality">
+             <property name="toolTip">
+              <string>0 = smallest size, 100 = lossless. Default is 100.</string>
+             </property>
+             <property name="suffix">
+              <string>%</string>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+             <property name="value">
+              <number>100</number>
+             </property>
+            </widget>
+           </item>
+           <item row="11" column="0">
+            <widget class="QLabel" name="lblPass">
+             <property name="toolTip">
+              <string>Number of entropy-analysis passes (1 to 10).</string>
+             </property>
+             <property name="text">
+              <string>Entropy Passes</string>
+             </property>
+            </widget>
+           </item>
+           <item row="11" column="1">
+            <widget class="KisSliderSpinBox" name="pass">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="toolTip">
+              <string>Number of entropy-analysis passes (1 to 10).</string>
+             </property>
+             <property name="minimum">
+              <number>1</number>
+             </property>
+             <property name="maximum">
+              <number>10</number>
+             </property>
+             <property name="singleStep">
+              <number>1</number>
+             </property>
+             <property name="pageStep" stdset="0">
+              <number>2</number>
+             </property>
+            </widget>
+           </item>
+           <item row="12" column="0">
+            <widget class="QLabel" name="lblShowCompressed">
+             <property name="toolTip">
+              <string>If enabled, export the compressed picture back. In-loop filtering is not applied.</string>
+             </property>
+             <property name="text">
+              <string>Show Compressed</string>
+             </property>
+            </widget>
+           </item>
+           <item row="12" column="1">
+            <widget class="QCheckBox" name="showCompressed">
+             <property name="toolTip">
+              <string>If enabled, export the compressed picture back. In-loop filtering is not applied.</string>
+             </property>
+             <property name="text">
+              <string>Enabled</string>
+             </property>
+            </widget>
+           </item>
+           <item row="13" column="0">
+            <widget class="QLabel" name="lblPreprocessing">
+             <property name="text">
+              <string>Preprocessing Filter</string>
+             </property>
+            </widget>
+           </item>
+           <item row="13" column="1">
+            <widget class="QComboBox" name="preprocessing"/>
+           </item>
+           <item row="14" column="0">
+            <widget class="QLabel" name="lblPartitions">
+             <property name="toolTip">
+              <string>log2(number of token partitions) between 0 and 3. Default is 0 for easier progressive decoding.</string>
+             </property>
+             <property name="text">
+              <string>Partitions</string>
+             </property>
+            </widget>
+           </item>
+           <item row="14" column="1">
+            <widget class="KisSliderSpinBox" name="partitions">
+             <property name="toolTip">
+              <string>log2(number of token partitions) between 0 and 3. Default is 0 for easier progressive decoding.</string>
+             </property>
+             <property name="maximum">
+              <number>3</number>
+             </property>
+            </widget>
+           </item>
+           <item row="15" column="0">
+            <widget class="QLabel" name="lblPartitionLimit">
+             <property name="toolTip">
+              <string>Quality degradation allowed to fit the 512KB limit on prediction modes coding (0 = no degradation, 100 = maximum possible degradation).</string>
+             </property>
+             <property name="text">
+              <string>Partition Limit</string>
+             </property>
+            </widget>
+           </item>
+           <item row="15" column="1">
+            <layout class="QGridLayout" name="gridLayout_2">
+             <item row="0" column="1">
+              <widget class="QLabel" name="lblMaximumDegradation">
+               <property name="text">
+                <string>Maximum degradation</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="0">
+              <widget class="QLabel" name="lblNoDegradation">
+               <property name="text">
+                <string>No degradation</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0" colspan="2">
+              <widget class="KisSliderSpinBox" name="partitionLimit">
+               <property name="toolTip">
+                <string>Quality degradation allowed to fit the 512KB limit on prediction modes coding (0 = no degradation, 100 = maximum possible degradation).</string>
+               </property>
+               <property name="suffix">
+                <string>%</string>
+               </property>
+               <property name="maximum">
+                <number>100</number>
+               </property>
+               <property name="value">
+                <number>100</number>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item row="16" column="0">
+            <widget class="QLabel" name="lblEmulateJPEGSize">
+             <property name="toolTip">
+              <string>If enabled, compression parameters will be remapped to better match the expected output size from JPEG compression. Generally, the output size will be similar but the degradation will be lower.</string>
+             </property>
+             <property name="text">
+              <string>Emulate JPEG Size</string>
+             </property>
+            </widget>
+           </item>
+           <item row="16" column="1">
+            <widget class="QCheckBox" name="emulateJPEGSize">
+             <property name="toolTip">
+              <string>If enabled, compression parameters will be remapped to better match the expected output size from JPEG compression. Generally, the output size will be similar but the degradation will be lower.</string>
+             </property>
+             <property name="text">
+              <string>Enabled</string>
+             </property>
+            </widget>
+           </item>
+           <item row="17" column="0">
+            <widget class="QLabel" name="lblThreadLevel">
+             <property name="toolTip">
+              <string>If enabled, try and use multithreaded encoding.</string>
+             </property>
+             <property name="text">
+              <string>Multithreaded Encoding</string>
+             </property>
+            </widget>
+           </item>
+           <item row="17" column="1">
+            <widget class="QCheckBox" name="threadLevel">
+             <property name="toolTip">
+              <string>If enabled, try and use multithreaded encoding.</string>
+             </property>
+             <property name="text">
+              <string>Enabled</string>
+             </property>
+            </widget>
+           </item>
+           <item row="18" column="0">
+            <widget class="QLabel" name="lblLowMemory">
+             <property name="toolTip">
+              <string>If enabled, reduce memory usage (but increase CPU use).</string>
+             </property>
+             <property name="text">
+              <string>Reduce Memory Usage</string>
+             </property>
+            </widget>
+           </item>
+           <item row="18" column="1">
+            <widget class="QCheckBox" name="lowMemory">
+             <property name="toolTip">
+              <string>If enabled, reduce memory usage (but increase CPU use).</string>
+             </property>
+             <property name="text">
+              <string>Enabled</string>
+             </property>
+            </widget>
+           </item>
+           <item row="19" column="0">
+            <widget class="QLabel" name="lblNearLossless">
+             <property name="toolTip">
+              <string>Near lossless encoding: 0 = max loss, 100 = off. Default is 100.</string>
+             </property>
+             <property name="text">
+              <string>Near Lossless</string>
+             </property>
+            </widget>
+           </item>
+           <item row="19" column="1">
+            <widget class="KisSliderSpinBox" name="nearLossless">
+             <property name="toolTip">
+              <string>Near lossless encoding: 0 = max loss, 100 = off. Default is 100.</string>
+             </property>
+             <property name="suffix">
+              <string>%</string>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+             <property name="value">
+              <number>100</number>
+             </property>
+            </widget>
+           </item>
+           <item row="20" column="0">
+            <widget class="QLabel" name="lblExact">
+             <property name="toolTip">
+              <string>If enabled, preserve the exact RGB values under transparent areas. Otherwise, discard this invisible RGB information for better compression. The default is disabled.</string>
+             </property>
+             <property name="text">
+              <string>Exact</string>
+             </property>
+            </widget>
+           </item>
+           <item row="20" column="1">
+            <widget class="QCheckBox" name="exact">
+             <property name="toolTip">
+              <string>If enabled, preserve the exact RGB values under transparent areas. Otherwise, discard this invisible RGB information for better compression. The default is disabled.</string>
+             </property>
+             <property name="text">
+              <string>Enabled</string>
+             </property>
+            </widget>
+           </item>
+           <item row="21" column="0">
+            <widget class="QLabel" name="lblUseSharpYUV">
+             <property name="toolTip">
+              <string>If enabled, allows use of the sharp (and slow) RGB to YUV conversion.</string>
+             </property>
+             <property name="text">
+              <string>Use Sharp YUV</string>
+             </property>
+            </widget>
+           </item>
+           <item row="21" column="1">
+            <widget class="QCheckBox" name="useSharpYUV">
+             <property name="toolTip">
+              <string>If enabled, allows use of the sharp (and slow) RGB to YUV conversion.</string>
+             </property>
+             <property name="text">
+              <string>Enabled</string>
+             </property>
+            </widget>
+           </item>
+           <item row="22" column="0">
+            <widget class="QLabel" name="lblQMin">
+             <property name="toolTip">
+              <string>Minimum permissible quality factor. Default is 0.</string>
+             </property>
+             <property name="text">
+              <string>Minimum Quality</string>
+             </property>
+            </widget>
+           </item>
+           <item row="22" column="1">
+            <widget class="KisSliderSpinBox" name="qMin">
+             <property name="toolTip">
+              <string>Minimum permissible quality factor. Default is 0.</string>
+             </property>
+             <property name="suffix">
+              <string>%</string>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+            </widget>
+           </item>
+           <item row="23" column="0">
+            <widget class="QLabel" name="lblQMax">
+             <property name="toolTip">
+              <string>Maximum permissible quality factor. Default is 100.</string>
+             </property>
+             <property name="text">
+              <string>Maximum Quality</string>
+             </property>
+            </widget>
+           </item>
+           <item row="23" column="1">
+            <widget class="KisSliderSpinBox" name="qMax">
+             <property name="toolTip">
+              <string>Maximum permissible quality factor. Default is 100.</string>
+             </property>
+             <property name="suffix">
+              <string>%</string>
+             </property>
+             <property name="maximum">
+              <number>100</number>
+             </property>
+             <property name="value">
+              <number>100</number>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KisSliderSpinBox</class>
+   <extends>QSpinBox</extends>
+   <header location="global">kis_slider_spin_box.h</header>
+  </customwidget>
+  <customwidget>
+   <class>KisDoubleSliderSpinBox</class>
+   <extends>QSpinBox</extends>
+   <header location="global">kis_slider_spin_box.h</header>
+  </customwidget>
+  <customwidget>
+   <class>KisDoubleParseUnitSpinBox</class>
+   <extends>QDoubleSpinBox</extends>
+   <header location="global">kis_double_parse_unit_spin_box.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/plugins/impex/webp/dlg_webp_import.cpp b/plugins/impex/webp/dlg_webp_import.cpp
new file mode 100644
index 0000000000..c951e4cbd1
--- /dev/null
+++ b/plugins/impex/webp/dlg_webp_import.cpp
@@ -0,0 +1,181 @@
+/*
+ * This file is part of Krita
+ *
+ * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <webp/encode.h>
+
+#include <kis_properties_configuration.h>
+
+#include "dlg_webp_import.h"
+
+KisDlgWebPImport::KisDlgWebPImport()
+    : KoDialog()
+    , m_rawWidget(new DlgWebPImport(this))
+    , m_keepAspect(false)
+    , m_aspectRatio(1)
+    , m_newWidth(0)
+    , m_newHeight(0)
+{
+    enableButtonApply(false);
+
+    setMainWidget(m_rawWidget.get());
+
+    m_rawWidget->hasAnimation->setAttribute(Qt::WA_TransparentForMouseEvents);
+    m_rawWidget->hasTransparency->setAttribute(Qt::WA_TransparentForMouseEvents);
+    m_rawWidget->isLossless->setAttribute(Qt::WA_TransparentForMouseEvents);
+
+    m_rawWidget->crop->setChecked(false);
+    m_rawWidget->cropX->setUnit(KoUnit(KoUnit::Pixel));
+    m_rawWidget->cropY->setUnit(KoUnit(KoUnit::Pixel));
+    m_rawWidget->cropWidth->setUnit(KoUnit(KoUnit::Pixel));
+    m_rawWidget->cropHeight->setUnit(KoUnit(KoUnit::Pixel));
+
+    m_widthUnitManager = new KisDocumentAwareSpinBoxUnitManager(this);
+    m_heightUnitManager = new KisDocumentAwareSpinBoxUnitManager(this, KisDocumentAwareSpinBoxUnitManager::PIX_DIR_Y);
+    m_widthUnitManager->setApparentUnitFromSymbol("px");
+    m_heightUnitManager->setApparentUnitFromSymbol("px");
+
+    m_rawWidget->scale->setChecked(false);
+    m_rawWidget->aspectRatioBtn->setKeepAspectRatio(m_keepAspect);
+    m_rawWidget->constrainProportionsCkb->setChecked(m_keepAspect);
+    m_rawWidget->newWidthDouble->setUnitManager(m_widthUnitManager);
+    m_rawWidget->newHeightDouble->setUnitManager(m_heightUnitManager);
+    m_rawWidget->newWidthDouble->setDecimals(2);
+    m_rawWidget->newHeightDouble->setDecimals(2);
+    m_rawWidget->newWidthDouble->setDisplayUnit(false);
+    m_rawWidget->newHeightDouble->setDisplayUnit(false);
+    m_rawWidget->widthUnit->setModel(m_widthUnitManager);
+    m_rawWidget->heightUnit->setModel(m_heightUnitManager);
+    m_rawWidget->widthUnit->setCurrentIndex(m_widthUnitManager->getsUnitSymbolList().indexOf(m_widthUnitManager->getApparentUnitSymbol()));
+    m_rawWidget->heightUnit->setCurrentIndex(m_heightUnitManager->getsUnitSymbolList().indexOf(m_widthUnitManager->getApparentUnitSymbol()));
+
+    connect(m_rawWidget->newWidthDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotWidthChanged(double)));
+    connect(m_rawWidget->newHeightDouble, SIGNAL(valueChangedPt(double)), this, SLOT(slotHeightChanged(double)));
+    connect(m_widthUnitManager, SIGNAL(unitChanged(int)), m_rawWidget->widthUnit, SLOT(setCurrentIndex(int)));
+    connect(m_heightUnitManager, SIGNAL(unitChanged(int)), m_rawWidget->heightUnit, SLOT(setCurrentIndex(int)));
+    connect(m_rawWidget->widthUnit, SIGNAL(currentIndexChanged(int)), m_widthUnitManager, SLOT(selectApparentUnitFromIndex(int)));
+    connect(m_rawWidget->heightUnit, SIGNAL(currentIndexChanged(int)), m_heightUnitManager, SLOT(selectApparentUnitFromIndex(int)));
+    connect(m_rawWidget->constrainProportionsCkb, SIGNAL(toggled(bool)), this, SLOT(slotAspectChanged(bool)));
+    connect(m_rawWidget->aspectRatioBtn, SIGNAL(keepAspectRatioChanged(bool)), this, SLOT(slotAspectChanged(bool)));
+}
+
+void KisDlgWebPImport::slotAspectChanged(bool keep)
+{
+    m_rawWidget->aspectRatioBtn->blockSignals(true);
+    m_rawWidget->constrainProportionsCkb->blockSignals(true);
+
+    m_rawWidget->aspectRatioBtn->setKeepAspectRatio(keep);
+    m_rawWidget->constrainProportionsCkb->setChecked(keep);
+
+    m_rawWidget->aspectRatioBtn->blockSignals(false);
+    m_rawWidget->constrainProportionsCkb->blockSignals(false);
+
+    m_keepAspect = keep;
+}
+
+void KisDlgWebPImport::slotWidthChanged(double v)
+{
+    // this slot receiv values in pt from the unitspinbox, so just use the resolution.
+    const double resValue = v * m_widthUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px");
+    m_newWidth = qRound(resValue);
+
+    if (m_keepAspect) {
+        m_newHeight = qRound(m_newWidth / m_aspectRatio);
+        m_rawWidget->newHeightDouble->blockSignals(true);
+        m_rawWidget->newHeightDouble->changeValue(v * m_aspectRatio);
+        m_rawWidget->newHeightDouble->blockSignals(false);
+    } else {
+        m_aspectRatio = m_newWidth / m_newHeight;
+    }
+}
+
+void KisDlgWebPImport::slotHeightChanged(double v)
+{
+    // this slot receiv values in pt from the unitspinbox, so just use the resolution.
+    const double resValue = v * m_heightUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px");
+    m_newHeight = qRound(resValue);
+
+    if (m_keepAspect) {
+        m_newWidth = qRound(m_newHeight * m_aspectRatio);
+        m_rawWidget->newWidthDouble->blockSignals(true);
+        m_rawWidget->newWidthDouble->changeValue(v * m_aspectRatio);
+        m_rawWidget->newWidthDouble->blockSignals(false);
+    } else {
+        m_aspectRatio = m_newWidth / m_newHeight;
+    }
+}
+
+void KisDlgWebPImport::setConfiguration(const KisPropertiesConfigurationSP cfg)
+{
+    m_rawWidget->originalHeight->setText(i18n("%1 px", cfg->getInt("original_height", 0)));
+    m_rawWidget->originalWidth->setText(i18n("%1 px", cfg->getInt("original_width", 0)));
+    m_rawWidget->originalWidth->setText(i18n("%1 px", cfg->getInt("original_width", 0)));
+
+    m_rawWidget->hasTransparency->setChecked(cfg->getBool("has_transparency", false));
+    switch (cfg->getInt("format", 0)) {
+        case 1:
+            m_rawWidget->isLossless->setCheckState(Qt::Unchecked);
+            break;
+        case 2:
+            m_rawWidget->isLossless->setCheckState(Qt::Checked);
+            break;
+        case 0:
+        default:
+            m_rawWidget->isLossless->setCheckState(Qt::PartiallyChecked);
+            break;
+    }
+    m_rawWidget->hasAnimation->setChecked(cfg->getBool("has_animation", false));
+
+    m_rawWidget->crop->setChecked(cfg->getBool("use_cropping", false));
+    m_rawWidget->cropX->setValue(cfg->getDouble("crop_left", 0));
+    m_rawWidget->cropY->setValue(cfg->getDouble("crop_top", 0));
+    m_rawWidget->cropWidth->setValue(cfg->getDouble("crop_width", 0));
+    m_rawWidget->cropHeight->setValue(cfg->getDouble("crop_height", 0));
+
+    m_rawWidget->scale->setChecked(cfg->getBool("use_scaling", false));
+    m_rawWidget->newWidthDouble->setValue(cfg->getDouble("scaled_width", 0));
+    m_rawWidget->newHeightDouble->setValue(cfg->getDouble("scaled_height", 0));
+
+    m_rawWidget->flip->setChecked(cfg->getBool("flip", false));
+
+    m_rawWidget->dithering->setChecked(cfg->getBool("use_dithering", false));
+    m_rawWidget->ditheringStrength->setValue(cfg->getInt("dithering_strength", 0));
+    m_rawWidget->alphaDitheringStrength->setValue(cfg->getInt("alpha_dithering_strength", 0));
+
+    m_rawWidget->noFancyUpsampling->setChecked(cfg->getBool("no_fancy_upsampling", false));
+    m_rawWidget->useThreads->setChecked(cfg->getBool("use_threads", false));
+}
+
+KisPropertiesConfigurationSP KisDlgWebPImport::configuration() const
+{
+    KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
+
+    const double conversionFactorW {m_widthUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px")};
+
+    const double conversionFactorH {m_heightUnitManager->getConversionFactor(KisSpinBoxUnitManager::LENGTH, "px")};
+
+    cfg->setProperty("use_cropping", m_rawWidget->crop->isChecked());
+    cfg->setProperty("crop_left", m_rawWidget->cropX->value());
+    cfg->setProperty("crop_top", m_rawWidget->cropY->value());
+    cfg->setProperty("crop_width", m_rawWidget->cropWidth->value());
+    cfg->setProperty("crop_height", m_rawWidget->cropHeight->value());
+
+    cfg->setProperty("use_scaling", m_rawWidget->scale->isChecked());
+    cfg->setProperty("scaled_width", qRound(m_rawWidget->newWidthDouble->value() * conversionFactorW));
+    cfg->setProperty("scaled_height", qRound(m_rawWidget->newHeightDouble->value() * conversionFactorH));
+
+    cfg->setProperty("flip", m_rawWidget->flip->isChecked());
+
+    cfg->setProperty("use_dithering", m_rawWidget->dithering->isChecked());
+    cfg->setProperty("dithering_strength", m_rawWidget->ditheringStrength->value());
+    cfg->setProperty("alpha_dithering_strength", m_rawWidget->alphaDitheringStrength->value());
+
+    cfg->setProperty("no_fancy_upsampling", m_rawWidget->noFancyUpsampling->isChecked());
+    cfg->setProperty("use_threads", m_rawWidget->useThreads->isChecked());
+
+    return cfg;
+}
diff --git a/plugins/impex/webp/dlg_webp_import.h b/plugins/impex/webp/dlg_webp_import.h
new file mode 100644
index 0000000000..8b0cce8330
--- /dev/null
+++ b/plugins/impex/webp/dlg_webp_import.h
@@ -0,0 +1,54 @@
+/*
+ * This file is part of Krita
+ *
+ * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef DLG_WEBP_IMPORT_H
+#define DLG_WEBP_IMPORT_H
+
+#include <QScopedPointer>
+
+#include <KoDialog.h>
+#include <kis_document_aware_spin_box_unit_manager.h>
+#include <kis_properties_configuration.h>
+
+#include "ui_dlg_webp_import.h"
+
+class DlgWebPImport : public QWidget, public Ui::DlgWebPImport
+{
+    Q_OBJECT
+public:
+    DlgWebPImport(QWidget *parent)
+        : QWidget(parent)
+    {
+        setupUi(this);
+    }
+};
+
+class KisDlgWebPImport : public KoDialog
+{
+    Q_OBJECT
+
+public:
+    KisDlgWebPImport();
+    void setConfiguration(const KisPropertiesConfigurationSP cfg);
+    KisPropertiesConfigurationSP configuration() const;
+
+private Q_SLOTS:
+    void slotAspectChanged(bool keep);
+
+    void slotWidthChanged(double v);
+    void slotHeightChanged(double v);
+
+private:
+    QScopedPointer<DlgWebPImport> m_rawWidget;
+    KisDocumentAwareSpinBoxUnitManager *m_heightUnitManager;
+    KisDocumentAwareSpinBoxUnitManager *m_widthUnitManager;
+    bool m_keepAspect;
+    double m_aspectRatio, m_newWidth, m_newHeight;
+};
+
+#endif // DLG_WEBP_EXPORT_H
diff --git a/plugins/impex/webp/dlg_webp_import.ui b/plugins/impex/webp/dlg_webp_import.ui
new file mode 100644
index 0000000000..6f5afa2161
--- /dev/null
+++ b/plugins/impex/webp/dlg_webp_import.ui
@@ -0,0 +1,421 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <author>
+  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+  SPDX-License-Identifier: GPL-3.0-or-later
+ </author>
+ <class>DlgWebPImport</class>
+ <widget class="QWidget" name="DlgWebPImport">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>374</width>
+    <height>552</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>WebP Image</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="dimensions">
+      <attribute name="title">
+       <string>Dimensions</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <widget class="QGroupBox" name="original">
+         <property name="title">
+          <string>Information</string>
+         </property>
+         <layout class="QFormLayout" name="formLayout_3">
+          <item row="0" column="0">
+           <widget class="QLabel" name="lblWidth">
+            <property name="text">
+             <string>Width:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QLabel" name="originalWidth">
+            <property name="text">
+             <string notr="true">Original width (in px)</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="lblHeight">
+            <property name="text">
+             <string>Height:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QLabel" name="originalHeight">
+            <property name="text">
+             <string notr="true">Original height (in px)</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="1">
+           <widget class="QCheckBox" name="isLossless">
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+            <property name="text">
+             <string>Lossless</string>
+            </property>
+            <property name="tristate">
+             <bool>true</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="1">
+           <widget class="QCheckBox" name="hasTransparency">
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+            <property name="text">
+             <string>Transparent</string>
+            </property>
+           </widget>
+          </item>
+          <item row="4" column="1">
+           <widget class="QCheckBox" name="hasAnimation">
+            <property name="focusPolicy">
+             <enum>Qt::NoFocus</enum>
+            </property>
+            <property name="text">
+             <string>Animated</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="crop">
+         <property name="title">
+          <string>Crop</string>
+         </property>
+         <property name="flat">
+          <bool>true</bool>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <item>
+           <layout class="QFormLayout" name="formLayout">
+            <item row="0" column="0">
+             <widget class="QLabel" name="lblCropX">
+              <property name="text">
+               <string>X:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="lblCropY">
+              <property name="text">
+               <string>Y:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="KisDoubleParseUnitSpinBox" name="cropX"/>
+            </item>
+            <item row="1" column="1">
+             <widget class="KisDoubleParseUnitSpinBox" name="cropY"/>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <layout class="QFormLayout" name="formLayout_2">
+            <item row="1" column="0">
+             <widget class="QLabel" name="lblCropHeight">
+              <property name="text">
+               <string>Height:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QLabel" name="lblCropWidth">
+              <property name="text">
+               <string>Width:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="KisDoubleParseUnitSpinBox" name="cropHeight"/>
+            </item>
+            <item row="0" column="1">
+             <widget class="KisDoubleParseUnitSpinBox" name="cropWidth"/>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="scale">
+         <property name="title">
+          <string>Resize</string>
+         </property>
+         <property name="flat">
+          <bool>true</bool>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="checked">
+          <bool>false</bool>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_4">
+          <item row="0" column="0">
+           <widget class="QLabel" name="lblNewWidth">
+            <property name="text">
+             <string>Width:</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="2">
+           <widget class="QComboBox" name="widthUnit"/>
+          </item>
+          <item row="0" column="1">
+           <widget class="KisDoubleParseUnitSpinBox" name="newWidthDouble">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="decimals">
+             <number>4</number>
+            </property>
+            <property name="minimum">
+             <double>0.000100000000000</double>
+            </property>
+            <property name="maximum">
+             <double>100000000.000000000000000</double>
+            </property>
+            <property name="singleStep">
+             <double>0.100000000000000</double>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="3" rowspan="2">
+           <widget class="KoAspectButton" name="aspectRatioBtn" native="true"/>
+          </item>
+          <item row="2" column="1" colspan="4">
+           <widget class="QCheckBox" name="constrainProportionsCkb">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="toolTip">
+             <string>Constrain aspect ratio</string>
+            </property>
+            <property name="text">
+             <string>Constrain proportions</string>
+            </property>
+            <property name="checked">
+             <bool>true</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="lblNewHeight">
+            <property name="text">
+             <string>Height:</string>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="KisDoubleParseUnitSpinBox" name="newHeightDouble">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="decimals">
+             <number>4</number>
+            </property>
+            <property name="minimum">
+             <double>0.000100000000000</double>
+            </property>
+            <property name="maximum">
+             <double>100000000.000000000000000</double>
+            </property>
+            <property name="singleStep">
+             <double>0.100000000000000</double>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="2">
+           <widget class="QComboBox" name="heightUnit"/>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QCheckBox" name="flip">
+         <property name="text">
+          <string>Flip vertically</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_4">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>0</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="advanced">
+      <attribute name="title">
+       <string>Advanced</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_5">
+       <item>
+        <layout class="QVBoxLayout" name="verticalLayout_6">
+         <item>
+          <widget class="QCheckBox" name="bypassFiltering">
+           <property name="text">
+            <string>Bypass filtering</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QCheckBox" name="noFancyUpsampling">
+           <property name="text">
+            <string>Use faster pointwise upsampling</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QCheckBox" name="useThreads">
+           <property name="text">
+            <string>Use multithreaded decoding</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="dithering">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="title">
+          <string>Dithering</string>
+         </property>
+         <property name="flat">
+          <bool>true</bool>
+         </property>
+         <property name="checkable">
+          <bool>true</bool>
+         </property>
+         <property name="checked">
+          <bool>false</bool>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_7">
+          <item>
+           <widget class="KisSliderSpinBox" name="ditheringStrength">
+            <property name="suffix">
+             <string> %</string>
+            </property>
+            <property name="prefix">
+             <string>Colour planes: </string>
+            </property>
+            <property name="maximum">
+             <number>100</number>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="KisSliderSpinBox" name="alphaDitheringStrength">
+            <property name="suffix">
+             <string> %</string>
+            </property>
+            <property name="prefix">
+             <string>Alpha plane: </string>
+            </property>
+            <property name="maximum">
+             <number>100</number>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_5">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KoAspectButton</class>
+   <extends>QWidget</extends>
+   <header location="global">KoAspectButton.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>KisDoubleParseUnitSpinBox</class>
+   <extends>QDoubleSpinBox</extends>
+   <header location="global">kis_double_parse_unit_spin_box.h</header>
+  </customwidget>
+  <customwidget>
+   <class>KisSliderSpinBox</class>
+   <extends>QSpinBox</extends>
+   <header location="global">kis_slider_spin_box.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/plugins/impex/webp/kis_webp_export.cpp b/plugins/impex/webp/kis_webp_export.cpp
new file mode 100644
index 0000000000..c35bb1f052
--- /dev/null
+++ b/plugins/impex/webp/kis_webp_export.cpp
@@ -0,0 +1,237 @@
+/*
+ * This file is part of Krita
+ *
+ * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <QFileInfo>
+#include <kpluginfactory.h>
+#include <memory>
+#include <webp/encode.h>
+#include <webp/types.h>
+
+#include <KisDocument.h>
+#include <KisExportCheckRegistry.h>
+#include <KisImportExportErrorCode.h>
+#include <KisImportExportManager.h>
+#include <KoColorModelStandardIds.h>
+#include <KoColorSpace.h>
+#include <KoColorSpaceRegistry.h>
+#include <kis_assert.h>
+#include <kis_debug.h>
+#include <kis_image.h>
+#include <kis_paint_device.h>
+#include <kis_paint_layer.h>
+#include <kis_random_accessor_ng.h>
+
+#include "dlg_webp_export.h"
+#include "kis_webp_export.h"
+
+K_PLUGIN_FACTORY_WITH_JSON(KisWebPExportFactory, "krita_webp_export.json", registerPlugin<KisWebPExport>();)
+
+KisWebPExport::KisWebPExport(QObject *parent, const QVariantList &)
+    : KisImportExportFilter(parent)
+{
+}
+
+KisWebPExport::~KisWebPExport()
+{
+}
+
+KisPropertiesConfigurationSP KisWebPExport::defaultConfiguration(const QByteArray & /*from*/, const QByteArray & /*to*/) const
+{
+    KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
+    WebPConfig preset {};
+
+    if (!WebPConfigInit(&preset)) {
+        return cfg;
+    }
+
+    if (!WebPConfigLosslessPreset(&preset, 6)) {
+        return cfg;
+    }
+
+    preset.thread_level = 1;
+
+    if (!WebPValidateConfig(&preset)) {
+        return cfg;
+    }
+
+    cfg->setProperty("preset", 0);
+    cfg->setProperty("lossless", preset.lossless == 1);
+    cfg->setProperty("quality", preset.quality);
+    cfg->setProperty("method", preset.method);
+    cfg->setProperty("dithering", true);
+
+    cfg->setProperty("target_size", preset.target_size);
+    cfg->setProperty("target_PSNR", preset.target_PSNR);
+    cfg->setProperty("segments", preset.segments);
+    cfg->setProperty("sns_strength", preset.sns_strength);
+    cfg->setProperty("filter_strength", preset.filter_strength);
+    cfg->setProperty("filter_sharpness", preset.filter_sharpness);
+    cfg->setProperty("filter_type", preset.filter_type);
+    cfg->setProperty("autofilter", preset.autofilter == 1);
+    cfg->setProperty("alpha_compression", preset.alpha_compression);
+    cfg->setProperty("alpha_filtering", preset.alpha_filtering);
+    cfg->setProperty("alpha_quality", preset.alpha_quality);
+    cfg->setProperty("pass", preset.pass);
+    cfg->setProperty("show_compressed", preset.show_compressed == 1);
+    cfg->setProperty("preprocessing", preset.preprocessing);
+    cfg->setProperty("partitions", preset.partitions);
+    cfg->setProperty("partition_limit", preset.partition_limit);
+    cfg->setProperty("emulate_jpeg_size", preset.emulate_jpeg_size == 1);
+    cfg->setProperty("thread_level", preset.thread_level > 0);
+    cfg->setProperty("low_memory", preset.low_memory == 1);
+    cfg->setProperty("near_lossless", preset.near_lossless);
+    cfg->setProperty("exact", preset.exact == 1);
+    cfg->setProperty("use_sharp_yuv", preset.use_sharp_yuv == 1);
+#if WEBP_ENCODER_ABI_VERSION >= 0x020f
+    cfg->setProperty("qmin", preset.qmin);
+    cfg->setProperty("qmax", preset.qmax);
+#endif
+
+    return cfg;
+}
+
+KisConfigWidget *KisWebPExport::createConfigurationWidget(QWidget *parent, const QByteArray & /*from*/, const QByteArray & /*to*/) const
+{
+    return new KisDlgWebPExport(parent);
+}
+
+KisImportExportErrorCode KisWebPExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP cfg)
+{
+    WebPConfig config;
+    if (!WebPConfigInit(&config)) {
+        dbgPlugins << "WebP config initialization failed!";
+        return ImportExportCodes::InternalError;
+    }
+
+    {
+        config.lossless = cfg->getBool("lossless", true) ? 1 : 0;
+        config.quality = cfg->getFloat("quality", 75.0);
+        config.method = cfg->getInt("method", 4);
+
+        config.target_size = cfg->getInt("target_size", 0);
+        config.target_PSNR = cfg->getFloat("target_PSNR", 0.0f);
+        config.segments = cfg->getInt("segments", 4);
+        config.sns_strength = cfg->getInt("sns_strength", 50);
+        config.filter_strength = cfg->getInt("filter_strength", 60);
+        config.filter_sharpness = cfg->getInt("filter_sharpness", 0);
+        config.filter_type = cfg->getInt("filter_type", 1);
+        config.autofilter = cfg->getBool("autofilter", false) ? 1 : 0;
+        config.alpha_compression = cfg->getInt("alpha_compression", 1);
+        config.alpha_filtering = cfg->getInt("alpha_filtering", 1);
+        config.alpha_quality = cfg->getInt("alpha_quality", 100);
+        config.pass = cfg->getInt("pass", 1);
+        config.show_compressed = cfg->getBool("show_compressed", false) ? 1 : 0;
+        config.preprocessing = cfg->getInt("preprocessing", 0);
+        config.partitions = cfg->getInt("partitions", 0);
+        config.partition_limit = cfg->getInt("partition_limit", 0);
+        config.emulate_jpeg_size = cfg->getBool("emulate_jpeg_size", false) ? 1 : 0;
+        config.thread_level = cfg->getBool("thread_level", false) ? 1 : 0;
+        config.low_memory = cfg->getBool("low_memory", false) ? 1 : 0;
+        config.near_lossless = cfg->getInt("near_lossless", 100);
+        config.exact = cfg->getBool("exact", 0) ? 1 : 0;
+        config.use_sharp_yuv = cfg->getBool("use_sharp_yuv", false) ? 1 : 0;
+#if WEBP_ENCODER_ABI_VERSION >= 0x020f
+        config.qmin = cfg->getInt("qmin", 0);
+        config.qmax = cfg->getInt("qmax", 100);
+#endif
+
+        if (!WebPValidateConfig(&config)) {
+            dbgPlugins << "WebP configuration validation failure";
+            return ImportExportCodes::InternalError;
+        }
+    }
+
+    std::unique_ptr<WebPPicture, decltype(&WebPPictureFree)> picture(new WebPPicture(), &WebPPictureFree);
+    if (picture && !WebPPictureInit(picture.get())) {
+        dbgPlugins << "WebP picture initialization failure";
+        return ImportExportCodes::InternalError;
+    }
+
+    const QRect rc = document->savingImage()->bounds();
+    picture->width = rc.width();
+    picture->height = rc.height();
+
+    const bool enableDithering = cfg->getBool("dithering", true);
+
+    KisPaintDeviceSP dst;
+    const KoColorSpace *cs = document->savingImage()->projection()->colorSpace();
+    if ((cs->colorModelId() == RGBAColorModelID && cs->colorDepthId() == Integer8BitsColorDepthID) || !enableDithering) {
+        dst = document->savingImage()->projection();
+    } else {
+        // We need to use gradient painter code's:
+        //    to convert to RGBA samedepth;
+        //    then dither to RGBA8
+        //    then convert from ARGB32 to RGBA8888
+        const KisPaintDeviceSP src = document->savingImage()->projection();
+        const KoID depthId = src->colorSpace()->colorDepthId();
+        const KoColorSpace *destCs = KoColorSpaceRegistry::instance()->rgb8();
+        const KoColorSpace *mixCs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), depthId.id(), destCs->profile());
+
+        KisPaintDeviceSP tmp(new KisPaintDevice(*src));
+        tmp->convertTo(mixCs);
+        dst = new KisPaintDevice(destCs);
+
+        const KisDitherOp *op = mixCs->ditherOp(destCs->colorDepthId().id(), enableDithering ? DITHER_BEST : DITHER_NONE);
+
+        KisRandomConstAccessorSP srcIt(tmp->createRandomConstAccessorNG());
+        KisRandomAccessorSP dstIt(dst->createRandomAccessorNG());
+
+        int rows = 1;
+        int columns = 1;
+
+        for (int y = rc.y(); y <= rc.bottom(); y += rows) {
+            rows = qMin(srcIt->numContiguousRows(y), qMin(dstIt->numContiguousRows(y), rc.bottom() - y + 1));
+
+            for (int x = rc.x(); x <= rc.right(); x += columns) {
+                columns = qMin(srcIt->numContiguousColumns(x), qMin(dstIt->numContiguousColumns(x), rc.right() - x + 1));
+
+                srcIt->moveTo(x, y);
+                dstIt->moveTo(x, y);
+
+                const qint32 srcRowStride {srcIt->rowStride(x, y)};
+                const qint32 dstRowStride {dstIt->rowStride(x, y)};
+                const quint8 *srcPtr {srcIt->rawDataConst()};
+                quint8 *dstPtr {dstIt->rawData()};
+
+                op->dither(srcPtr, srcRowStride, dstPtr, dstRowStride, x, y, columns, rows);
+            }
+        }
+    }
+
+    const QImage image = dst->convertToQImage(nullptr, 0, 0, rc.width(), rc.height()).convertToFormat(QImage::Format_RGBA8888);
+    const uchar *rgba = image.constBits();
+
+    if (!WebPPictureImportRGBA(picture.get(), rgba, rc.width() * 4)) {
+        dbgPlugins << "WebP picture conversion failure:" << picture->error_code;
+        return ImportExportCodes::InternalError;
+    }
+
+    WebPMemoryWriter writer;
+    WebPMemoryWriterInit(&writer);
+    picture->writer = WebPMemoryWrite;
+    picture->custom_ptr = &writer;
+
+    if (!WebPEncode(&config, picture.get())) {
+        dbgPlugins << "WebP encoding failure:" << picture->error_code;
+        return ImportExportCodes::ErrorWhileWriting;
+    }
+
+    QDataStream s(io);
+    s.setByteOrder(QDataStream::LittleEndian);
+    s.writeRawData(reinterpret_cast<char *>(writer.mem), static_cast<int>(writer.size));
+    return ImportExportCodes::OK;
+}
+
+void KisWebPExport::initializeCapabilities()
+{
+    QList<QPair<KoID, KoID>> supportedColorModels;
+    supportedColorModels << QPair<KoID, KoID>() << QPair<KoID, KoID>(RGBAColorModelID, Integer8BitsColorDepthID);
+    addSupportedColorModels(supportedColorModels, "WebP");
+}
+
+#include "kis_webp_export.moc"
diff --git a/plugins/impex/webp/kis_webp_export.h b/plugins/impex/webp/kis_webp_export.h
new file mode 100644
index 0000000000..be89e46838
--- /dev/null
+++ b/plugins/impex/webp/kis_webp_export.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of Krita
+ *
+ * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _KIS_WEBP_EXPORT_H_
+#define _KIS_WEBP_EXPORT_H_
+
+#include <QVariant>
+
+#include <KisImportExportFilter.h>
+
+class KisWebPExport : public KisImportExportFilter
+{
+    Q_OBJECT
+public:
+    KisWebPExport(QObject *parent, const QVariantList &);
+    ~KisWebPExport() override;
+
+    KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override;
+    KisPropertiesConfigurationSP defaultConfiguration(const QByteArray &from, const QByteArray &to) const override;
+    KisConfigWidget *createConfigurationWidget(QWidget *parent, const QByteArray &from = "", const QByteArray &to = "") const override;
+    void initializeCapabilities() override;
+};
+
+#endif
diff --git a/plugins/impex/webp/kis_webp_import.cpp b/plugins/impex/webp/kis_webp_import.cpp
new file mode 100644
index 0000000000..2bc5beb702
--- /dev/null
+++ b/plugins/impex/webp/kis_webp_import.cpp
@@ -0,0 +1,191 @@
+/*
+ * This file is part of Krita
+ *
+ * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <cstdint>
+#include <memory>
+#include <webp/decode.h>
+#include <webp/types.h>
+
+#include <KisDocument.h>
+#include <KisImportExportErrorCode.h>
+#include <KoDialog.h>
+#include <kis_group_layer.h>
+#include <kis_paint_layer.h>
+#include <kis_properties_configuration.h>
+#include <kpluginfactory.h>
+
+#include "kis_webp_import.h"
+
+K_PLUGIN_FACTORY_WITH_JSON(KisWebPImportFactory, "krita_webp_import.json", registerPlugin<KisWebPImport>();)
+
+KisWebPImport::KisWebPImport(QObject *parent, const QVariantList &)
+    : KisImportExportFilter(parent)
+    , m_dialog(new KisDlgWebPImport())
+{
+}
+
+KisWebPImport::~KisWebPImport()
+{
+}
+
+KisPropertiesConfigurationSP KisWebPImport::defaultConfiguration(const QByteArray & /*from*/, const QByteArray & /*to*/) const
+{
+    KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration());
+
+    WebPDecoderConfig preset;
+
+    if (!WebPInitDecoderConfig(&preset)) {
+        return cfg;
+    }
+
+    cfg->setProperty("alpha_dithering_strength", preset.options.alpha_dithering_strength == 1);
+    cfg->setProperty("no_fancy_upsampling", preset.options.no_fancy_upsampling);
+    cfg->setProperty("use_cropping", preset.options.use_cropping);
+    cfg->setProperty("crop_left", preset.options.crop_left);
+    cfg->setProperty("crop_top", preset.options.crop_top);
+    cfg->setProperty("crop_width", preset.options.crop_width);
+    cfg->setProperty("crop_height", preset.options.crop_height);
+    cfg->setProperty("use_scaling", preset.options.use_scaling);
+    cfg->setProperty("scaled_width", preset.options.scaled_width);
+    cfg->setProperty("scaled_height", preset.options.scaled_height);
+    cfg->setProperty("use_threads", preset.options.use_threads == 1);
+    cfg->setProperty("use_dithering", false);
+    cfg->setProperty("dithering_strength", preset.options.dithering_strength);
+    cfg->setProperty("flip", preset.options.flip == 1);
+    cfg->setProperty("alpha_dithering_strength", preset.options.alpha_dithering_strength);
+
+    return cfg;
+}
+
+KisImportExportErrorCode KisWebPImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP)
+{
+    const QByteArray buf = io->readAll();
+
+    if (buf.isEmpty()) {
+        return ImportExportCodes::ErrorWhileReading;
+    }
+
+    const uint8_t *data = reinterpret_cast<const uint8_t *>(buf.constData());
+    const size_t data_size = static_cast<size_t>(buf.size());
+
+    WebPDecoderConfig config;
+    if (!WebPInitDecoderConfig(&config)) {
+        dbgPlugins << "WebP decode config initialization failure";
+        return ImportExportCodes::InternalError;
+    }
+
+    // Check image file format.
+    {
+        const VP8StatusCode result =WebPGetFeatures(data, data_size, &config.input);
+        dbgPlugins << "WebP import validation status: " << result;
+        switch (result) {
+        case VP8_STATUS_OK:
+            break;
+        case VP8_STATUS_OUT_OF_MEMORY:
+            return ImportExportCodes::InsufficientMemory;
+        case VP8_STATUS_INVALID_PARAM:
+            return ImportExportCodes::InternalError;
+        case VP8_STATUS_BITSTREAM_ERROR:
+            return ImportExportCodes::FileFormatIncorrect;
+        case VP8_STATUS_UNSUPPORTED_FEATURE:
+            return ImportExportCodes::FormatFeaturesUnsupported;
+        case VP8_STATUS_SUSPENDED:
+            return ImportExportCodes::InternalError;
+        case VP8_STATUS_USER_ABORT:
+            return ImportExportCodes::InternalError;
+        case VP8_STATUS_NOT_ENOUGH_DATA:
+            return ImportExportCodes::FileFormatIncorrect;
+        }
+    }
+
+    {
+        KisPropertiesConfigurationSP cfg(defaultConfiguration(QByteArray(), QByteArray()));
+
+        cfg->setProperty("original_width", config.input.width);
+        cfg->setProperty("original_height", config.input.height);
+        cfg->setProperty("crop_width", config.input.width);
+        cfg->setProperty("crop_height", config.input.height);
+        cfg->setProperty("scaled_width", config.input.width);
+        cfg->setProperty("scaled_height", config.input.height);
+        cfg->setProperty("has_transparency", config.input.has_alpha);
+        cfg->setProperty("format", config.input.format);
+        cfg->setProperty("has_animation", config.input.has_animation);
+
+        m_dialog->setConfiguration(cfg.data());
+    }
+
+    {
+        if (m_dialog->exec() != QDialog::Accepted) {
+            return ImportExportCodes::Cancelled;
+        }
+
+        KisPropertiesConfigurationSP cfg(m_dialog->configuration());
+
+        // Krita follows BGRA layout (checks RgbU8).
+        config.output.colorspace = MODE_BGRA;
+        config.options.bypass_filtering = cfg->getBool("bypass_filtering", false) ? 1 : 0;
+        config.options.no_fancy_upsampling = cfg->getBool("no_fancy_upsampling", false) ? 1 : 0;
+        if (cfg->getBool("use_cropping", false)) {
+            config.options.use_cropping = 1;
+            config.options.crop_left = cfg->getInt("crop_left", 0);
+            config.options.crop_top = cfg->getInt("crop_top", 0);
+            config.options.crop_width = cfg->getInt("crop_width", config.input.width);
+            config.options.crop_height = cfg->getInt("crop_height", config.input.height);
+        }
+
+        if (cfg->getBool("use_scaling", false)) {
+            config.options.scaled_width = cfg->getInt("scaled_width", config.input.height);
+            config.options.scaled_height = cfg->getInt("scaled_height", config.input.height);
+        }
+
+        config.options.use_threads = cfg->getBool("use_threads", false) ? 1 : 0;
+
+        if (cfg->getBool("use_dithering", false)) {
+            config.options.dithering_strength = cfg->getInt("dithering_strength", 0);
+            config.options.alpha_dithering_strength = cfg->getInt("alpha_dithering_strength", 0);
+        }
+
+        config.options.flip = cfg->getBool("flip", false) ? 1 : 0;
+    }
+
+    {
+        const VP8StatusCode result = WebPDecode(data, data_size, &config);
+        dbgPlugins << "WebP import status: " << result;
+        switch (result) {
+        case VP8_STATUS_OK:
+            break;
+        case VP8_STATUS_OUT_OF_MEMORY:
+            return ImportExportCodes::InsufficientMemory;
+        case VP8_STATUS_INVALID_PARAM:
+            return ImportExportCodes::InternalError;
+        case VP8_STATUS_BITSTREAM_ERROR:
+            return ImportExportCodes::FileFormatIncorrect;
+        case VP8_STATUS_UNSUPPORTED_FEATURE:
+            return ImportExportCodes::FormatFeaturesUnsupported;
+        case VP8_STATUS_SUSPENDED:
+            return ImportExportCodes::InternalError;
+        case VP8_STATUS_USER_ABORT:
+            return ImportExportCodes::InternalError;
+        case VP8_STATUS_NOT_ENOUGH_DATA:
+            return ImportExportCodes::FileFormatIncorrect;
+        }
+    }
+
+    const KoColorSpace *colorSpace = KoColorSpaceRegistry::instance()->rgb8();
+    KisImageSP image = new KisImage(document->createUndoStore(), config.output.width, config.output.height, colorSpace, i18n("WebP Image"));
+
+    KisPaintLayerSP layer(new KisPaintLayer(image, image->nextLayerName(), 255));
+    layer->paintDevice()->writeBytes(config.output.u.RGBA.rgba, 0, 0, config.output.width, config.output.height);
+    image->addNode(layer.data(), image->rootLayer().data());
+
+    document->setCurrentImage(image);
+    WebPFreeDecBuffer(&config.output);
+    return ImportExportCodes::OK;
+}
+
+#include "kis_webp_import.moc"
diff --git a/plugins/impex/webp/kis_webp_import.h b/plugins/impex/webp/kis_webp_import.h
new file mode 100644
index 0000000000..089bb0111e
--- /dev/null
+++ b/plugins/impex/webp/kis_webp_import.h
@@ -0,0 +1,32 @@
+/*
+ *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy at amyspark.me>
+ *
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _KIS_WEBP_IMPORT_H_
+#define _KIS_WEBP_IMPORT_H_
+
+#include <QScopedPointer>
+#include <QVariant>
+
+#include <KisImportExportFilter.h>
+#include <KoDialog.h>
+
+#include "dlg_webp_import.h"
+
+class KisWebPImport : public KisImportExportFilter
+{
+    Q_OBJECT
+public:
+    KisWebPImport(QObject *parent, const QVariantList &);
+    ~KisWebPImport() override;
+
+    KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration = 0) override;
+    KisPropertiesConfigurationSP defaultConfiguration(const QByteArray &from, const QByteArray &to) const override;
+
+private:
+    QScopedPointer<KisDlgWebPImport> m_dialog {nullptr};
+};
+
+#endif // _KIS_WEBP_IMPORT_H_
diff --git a/plugins/impex/webp/krita_webp.desktop b/plugins/impex/webp/krita_webp.desktop
new file mode 100644
index 0000000000..381c88a379
--- /dev/null
+++ b/plugins/impex/webp/krita_webp.desktop
@@ -0,0 +1,72 @@
+[Desktop Entry]
+Name=Krita
+Name[af]=Krita
+Name[ar]=كريتا
+Name[bg]=Krita
+Name[br]=Krita
+Name[bs]=Krita
+Name[ca]=Krita
+Name[ca at valencia]=Krita
+Name[cs]=Krita
+Name[cy]=Krita
+Name[da]=Krita
+Name[de]=Krita
+Name[el]=Krita
+Name[en_GB]=Krita
+Name[eo]=Krita
+Name[es]=Krita
+Name[et]=Krita
+Name[eu]=Krita
+Name[fi]=Krita
+Name[fr]=Krita
+Name[fy]=Krita
+Name[ga]=Krita
+Name[gl]=Krita
+Name[he]=Krita
+Name[hi]=क्रिता
+Name[hne]=केरिता
+Name[hr]=Krita
+Name[hu]=Krita
+Name[ia]=Krita
+Name[is]=Krita
+Name[it]=Krita
+Name[ja]=Krita
+Name[kk]=Krita
+Name[ko]=Krita
+Name[lt]=Krita
+Name[lv]=Krita
+Name[mr]=क्रिटा
+Name[ms]=Krita
+Name[nb]=Krita
+Name[nds]=Krita
+Name[ne]=क्रिता
+Name[nl]=Krita
+Name[nn]=Krita
+Name[pl]=Krita
+Name[pt]=Krita
+Name[pt_BR]=Krita
+Name[ro]=Krita
+Name[ru]=Krita
+Name[se]=Krita
+Name[sk]=Krita
+Name[sl]=Krita
+Name[sv]=Krita
+Name[ta]=கிரிட்டா
+Name[tg]=Krita
+Name[tr]=Krita
+Name[ug]=Krita
+Name[uk]=Krita
+Name[uz]=Krita
+Name[uz at cyrillic]=Krita
+Name[wa]=Krita
+Name[xh]=Krita
+Name[x-test]=xxKritaxx
+Name[zh_CN]=Krita
+Name[zh_TW]=Krita
+Exec=krita %F
+MimeType=image/webp;
+Type=Application
+Icon=krita
+Categories=Qt;KDE;Office;Graphics;
+StartupNotify=true
+NoDisplay=true
diff --git a/plugins/impex/webp/krita_webp_export.json b/plugins/impex/webp/krita_webp_export.json
new file mode 100644
index 0000000000..7b77f1cde6
--- /dev/null
+++ b/plugins/impex/webp/krita_webp_export.json
@@ -0,0 +1,12 @@
+{
+    "Id": "Krita WebP Export Filter",
+    "NoDisplay": "true",
+    "Type": "Service",
+    "X-KDE-Export": "image/webp",
+    "X-KDE-Library": "kritawebpexport",
+    "X-KDE-ServiceTypes": [
+        "Krita/FileFilter"
+    ],
+    "X-KDE-Weight": "1",
+    "X-KDE-Extensions" : "webp"
+}
diff --git a/plugins/impex/webp/krita_webp_import.json b/plugins/impex/webp/krita_webp_import.json
new file mode 100644
index 0000000000..ac126175af
--- /dev/null
+++ b/plugins/impex/webp/krita_webp_import.json
@@ -0,0 +1,12 @@
+{
+    "Id": "Krita WebP Import Filter",
+    "NoDisplay": "true",
+    "Type": "Service",
+    "X-KDE-Import": "image/webp",
+    "X-KDE-Library": "kritawebpimport",
+    "X-KDE-ServiceTypes": [
+        "Krita/FileFilter"
+    ],
+    "X-KDE-Weight": "1",
+    "X-KDE-Extensions" : "webp"
+}


More information about the kimageshop mailing list