From 71772963359553c2da8d0891d6e179e03f0cd4e5 Mon Sep 17 00:00:00 2001 From: Alex Merry Date: Sun, 19 Jan 2014 11:23:06 +0000 Subject: [PATCH] Import the WebP image I/O code from kde-runtime The plugin export mechanism has been patched up (including the addition of the JSON file), and the FindWebP.cmake file is new. Writing is currently disabled, as it produces broken images. Autotests are generated using the cwebp and dwebp utilities distributed with the libwebp reference library. REVIEW: 115355 --- autotests/CMakeLists.txt | 1 + autotests/read/webp/bw-cwebp-lossless.png | Bin 0 -> 743 bytes autotests/read/webp/bw-cwebp-lossless.webp | Bin 0 -> 418 bytes autotests/read/webp/bw-cwebp.png | Bin 0 -> 845 bytes autotests/read/webp/bw-cwebp.webp | Bin 0 -> 162 bytes autotests/read/webp/rgb-cwebp-lossless.png | Bin 0 -> 1053 bytes autotests/read/webp/rgb-cwebp-lossless.webp | Bin 0 -> 916 bytes autotests/read/webp/rgb-cwebp.png | Bin 0 -> 1499 bytes autotests/read/webp/rgb-cwebp.webp | Bin 0 -> 290 bytes cmake/FindWebP.cmake | 203 +++++++++++++++++ src/imageformats/CMakeLists.txt | 26 +++ src/imageformats/webp.cpp | 232 ++++++++++++++++++++ src/imageformats/webp.desktop | 7 + src/imageformats/webp.h | 59 +++++ src/imageformats/webp.json | 4 + src/imageformats/webp.xml | 7 + 16 files changed, 539 insertions(+) create mode 100644 autotests/read/webp/bw-cwebp-lossless.png create mode 100644 autotests/read/webp/bw-cwebp-lossless.webp create mode 100644 autotests/read/webp/bw-cwebp.png create mode 100644 autotests/read/webp/bw-cwebp.webp create mode 100644 autotests/read/webp/rgb-cwebp-lossless.png create mode 100644 autotests/read/webp/rgb-cwebp-lossless.webp create mode 100644 autotests/read/webp/rgb-cwebp.png create mode 100644 autotests/read/webp/rgb-cwebp.webp create mode 100644 cmake/FindWebP.cmake create mode 100644 src/imageformats/webp.cpp create mode 100644 src/imageformats/webp.desktop create mode 100644 src/imageformats/webp.h create mode 100644 src/imageformats/webp.json create mode 100644 src/imageformats/webp.xml diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index bedc3ad..14db235 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -53,6 +53,7 @@ kimageformats_read_tests( rgb tga xcf + webp ) # Basic write tests diff --git a/autotests/read/webp/bw-cwebp-lossless.png b/autotests/read/webp/bw-cwebp-lossless.png new file mode 100644 index 0000000000000000000000000000000000000000..e2d506bb3da276c09b8f9388dc8ad6b96f987f5e GIT binary patch literal 743 zcmV?P)E1)jxbCtrE}4UMN{WWBtG@56F7V!ybw)(i9BT()zyEYy z*ECJnb>X{hTkpM;5&(=b&N=6twKj?(N+|#kLcI6RIRJ3Z5kf*95z{n9#4i9{*Ue_L ztE;P`D6F;f`Fu8;5kkh}G3Wg0>8WX&s;b`J-Ufp~nx>2~LWnV@u4_cB>ly&!IF?dw zZEc;MolU1xMEnG3n&$HI^8EaKKA$`1wAPEof)Fwo3@D|G#lm}Ut?jxF02pIY^wC~p z45idsyUa@|hr{8)!NC`R-y<9qLJ&e2W1RCW%LpNlkB?-YwM_g-rqjE0Co zBz*z^0Ove#Ym8ZO&VP@CIF32zKbeE3X~MYXocGUxbFM5)tu+7$A^NYt81wM(@c#a; zl*;q`?>W&!1$A9pYuCj=pkXQ5pSe{mFTVOV^*y77_t{#z7Na5J-rnA5G^*?R`T5xx z)Bk&r=lRagju2ur8m;)?!0O%f-oL)SUSD6|-`}5{oP@QfPk?~YwyoA$DYc@};Ky&* ZzW_2gTD;a-w0-~p002ovPDHLkV1n%MZ8iV^ literal 0 HcmV?d00001 diff --git a/autotests/read/webp/bw-cwebp-lossless.webp b/autotests/read/webp/bw-cwebp-lossless.webp new file mode 100644 index 0000000000000000000000000000000000000000..3567af16a140d9af685229cb754014476d4bbc35 GIT binary patch literal 418 zcmV;T0bTx5Nk&GR0RRA3MM6+kP&iDE0RR9mAHW9ypW`5sBt?pbbBwGvcmD$O}3 zs%tIRTJl^HlxW@%Tx)Ub|Nq=7)4jKxrvgxdszA><0I06Dj1MW1^JR(oN`NE)U>Rv_ z+qP}no>}kK9rORURTlS%{;O@Qc3`zl+xwhrIgk+rIeoIoBONps}V59IOm*m zj4^c%8e^WHpGTw7WHM1oO(v6 zRaI3rO+zUSf`BpB@At2+uFlTRj4^aM38T^I=H^C9SrkRnG}Gx60FopDfGo@6I2J;< z@Hyu;0a8k(lraV&MCRyryZwHjb52)dv|x;NIvtzGD2mcFrIe1xkWs) zi;D|{5a&Dyf-npjV^T_+D}>O=$q8c&06;0Fl#)_PDa*1fib4pH=XsvzLWr^~>$)z> zQfvM6^mKiFO$Y&i%gf8h$4B!M^jCZ-GRD|-y6p$oW&jXH(ZRt%yWO76W=bi`^eYww zwnWSSY~9>@9LIyfAWc&tgq>9%g2gng4$<-#tu^Po+wHd7?N7_EllV&n-~3&L)|wDv z9bccHZ<~0933Kgp?}QLr(*N3bLEtGbe+{oZ!is}-bbSmLU9^6k%?V>HiXvM8D;U3; z`$k~Jy{f9RED=JUJqiHWN9ApKwbpr_XIW+y!u?%u?i&FBIB~X$?>Pq>5R_&4@bK{d z{+=Ys@$qr1)dG*=c@g|eD{m0N-x2>4fe(DU%^o9pp0`>pE9}dSvM?(XjF>;yqzZzx`do{j*3)_OXf-rwJk z$K&B}czSy3Giw_V!2DvHWmy=8oO7RJ*_XhbcTLmS`$%2aK8>`Q76HKLZD;NEbQymD XJv8mkouukm00000NkvXXu0mjfnzEDt literal 0 HcmV?d00001 diff --git a/autotests/read/webp/bw-cwebp.webp b/autotests/read/webp/bw-cwebp.webp new file mode 100644 index 0000000000000000000000000000000000000000..9c2e0c0a96eb64c035a8689a93c8a651075152a6 GIT binary patch literal 162 zcmV;T0A2r5Nk&GR00012MM6+kP&got0002+1OS}@Dj)zL06v8@l}Du`A|W^Wr~sf4 ziD>`;TeYY9Lj>@I34K#5#m-=uXn+6!{``8Te9Zq*PsjMbnA@!7H8?=4bc5rmg(GVC zWB|Ae5dJiyR=%qqJU8?1tHnkyVqJ1O=U%!5{LL#)Wo)4ReMF`~cfX|+%y6X-c0Yr5 QjYZ0`ZvI`0lSO4Jlp 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 B>Bay6 literal 0 HcmV?d00001 diff --git a/autotests/read/webp/rgb-cwebp.webp b/autotests/read/webp/rgb-cwebp.webp new file mode 100644 index 0000000000000000000000000000000000000000..d889a9b5e874e319449e7bc2fb4367fbdcdd830e GIT binary patch literal 290 zcmV+-0p0#mNk&E*0RRA3MM6+kP&gnC0RR9{1^}G_Dj)zL06vjCm`Ej~A|WUZP_PmS zYyec{_zkcdfH#0QhktWDD9iv2*af`mPc8)%JHRExBme;Z{*^(GBLjqR?tcouu^3Y? z$)6FWHR1K6ev|HR>=L)ZGu%Y*-$qtaAh_pTpd)j9OZ z^2S*wk&?bOad~c9fWEPkkG)HFyL52mJMr>G@NP~s-!U`an`@a)SLX_M`G;%+` +# Copyright (c) 2013 Martin Gräßlin +# Copyright (c) 2013-2014, Alex Merry, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +if(${CMAKE_VERSION} VERSION_LESS 2.8.12) + message(FATAL_ERROR "CMake 2.8.12 is required by FindWebP.cmake") +endif() + +set(knownComponents + WebP + Decoder + DeMux + Mux) + +unset(unknownComponents) + +set(pkgConfigModules) +set(requiredComponents) + +if (WebP_FIND_COMPONENTS) + set(comps ${WebP_FIND_COMPONENTS}) +else() + set(comps ${knownComponents}) +endif() + +# iterate through the list of requested components, and check that we know them all. +# If not, fail. +foreach(comp ${comps}) + list(FIND knownComponents ${comp} index ) + if("${index}" STREQUAL "-1") + list(APPEND unknownComponents "${comp}") + else() + if("${comp}" STREQUAL "WebP") + list(APPEND pkgConfigModules "libwebp") + elseif("${comp}" STREQUAL "Decoder") + list(APPEND pkgConfigModules "libwebpdecoder") + elseif("${comp}" STREQUAL "DeMux") + list(APPEND pkgConfigModules "libwebpdemux") + elseif("${comp}" STREQUAL "Mux") + list(APPEND pkgConfigModules "libwebpmux") + endif() + endif() +endforeach() + +if(DEFINED unknownComponents) + set(msgType STATUS) + if(WebP_FIND_REQUIRED) + set(msgType FATAL_ERROR) + endif() + if(NOT WebP_FIND_QUIETLY) + message(${msgType} "WebP: requested unknown components ${unknownComponents}") + endif() + return() +endif() + +macro(_WEBP_HANDLE_COMPONENT _comp) + set(_header) + set(_lib) + set(_pkgconfig_module) + if("${_comp}" STREQUAL "WebP") + set(_header "webp/encode.h") + set(_lib "webp") + set(_pkgconfig_module "libwebp") + elseif("${_comp}" STREQUAL "Decoder") + set(_header "webp/decode.h") + set(_lib "webpdecoder") + set(_pkgconfig_module "libwebpdecoder") + elseif("${_comp}" STREQUAL "DeMux") + set(_header "webp/demux.h") + set(_lib "webpdemux") + set(_pkgconfig_module "libwebpdemux") + elseif("${_comp}" STREQUAL "Mux") + set(_header "webp/mux.h") + set(_lib "webpmux") + set(_pkgconfig_module "libwebpmux") + endif() + + find_path(WebP_${_comp}_INCLUDE_DIR NAMES ${_header} HINTS ${PKG_WebP_INCLUDE_DIRS}) + find_library(WebP_${_comp}_LIBRARY NAMES ${_lib} HINTS ${PKG_WebP_LIBRARY_DIRS}) + + if(WebP_${_comp}_INCLUDE_DIR AND WebP_${_comp}_LIBRARY) + if(NOT "${_comp}" STREQUAL "Decoder") + list(APPEND WebP_INCLUDE_DIRS ${WebP_${_comp}_INCLUDE_DIR}) + list(APPEND WebP_LIBRARIES ${WebP_${_comp}_LIBRARY}) + endif() + endif() + + if(PKG_WebP_VERSION AND NOT PKG_WebP_${_pkgconfig_module}_VERSION) + # this is what gets set if we only search for one module + set(WebP_${_comp}_VERSION_STRING "${PKG_WebP_VERSION}") + else() + set(WebP_${_comp}_VERSION_STRING "${PKG_WebP_${_pkgconfig_module}_VERSION}") + endif() + if(NOT WebP_VERSION_STRING) + set(WebP_VERSION_STRING ${WebP_${_comp}_VERSION_STRING}) + endif() + + find_package_handle_standard_args(WebP_${_comp} + FOUND_VAR + WebP_${_comp}_FOUND + REQUIRED_VARS + WebP_${_comp}_LIBRARY + WebP_${_comp}_INCLUDE_DIR + VERSION_VAR + WebP_${_comp}_VERSION_STRING + ) + + mark_as_advanced(WebP_${_comp}_LIBRARY WebP_${_comp}_INCLUDE_DIR) + + # compatibility for old variable naming + set(WebP_${_comp}_INCLUDE_DIRS ${WebP_${_comp}_INCLUDE_DIR}) + set(WebP_${_comp}_LIBRARIES ${WebP_${_comp}_LIBRARY}) + + if(WebP_${_comp}_FOUND AND NOT TARGET WebP::${_comp}) + add_library(WebP::${_comp} UNKNOWN IMPORTED) + set_target_properties(WebP::${_comp} PROPERTIES + IMPORTED_LOCATION "${WebP_${_comp}_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${WebP_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${WebP_${_comp}_INCLUDE_DIR}" + ) + endif() +endmacro() + +include(FindPackageHandleStandardArgs) +# Use pkg-config to get the directories and then use these values +# in the FIND_PATH() and FIND_LIBRARY() calls +find_package(PkgConfig) +pkg_check_modules(PKG_WebP QUIET ${pkgConfigModules}) + +set(WebP_DEFINITIONS ${PKG_WebP_CFLAGS}) + +foreach(comp ${comps}) + _webp_handle_component(${comp}) +endforeach() + +if (WebP_Decoder_FOUND AND NOT WebP_WebP_FOUND) + list(APPEND WebP_INCLUDE_DIRS ${WebP_Decoder_INCLUDE_DIR}) + list(APPEND WebP_LIBRARIES ${WebP_Decoder_LIBRARY}) +endif() + +if (WebP_INCLUDE_DIRS) + list(REMOVE_DUPLICATES WebP_INCLUDE_DIRS) +endif() + +if(WebP_WebP_FOUND) + if(WebP_DeMux_FOUND) + add_dependencies(WebP::DeMux WebP::WebP) + endif() + if(WebP_Mux_FOUND) + add_dependencies(WebP::Mux WebP::WebP) + endif() +endif() + +find_package_handle_standard_args(WebP + FOUND_VAR + WebP_FOUND + REQUIRED_VARS + WebP_LIBRARIES + WebP_INCLUDE_DIRS + VERSION_VAR + WebP_VERSION_STRING + HANDLE_COMPONENTS + ) + +# compatibility for old variable naming +set(WebP_INCLUDE_DIR ${WebP_INCLUDE_DIRS}) + +include(FeatureSummary) +set_package_properties(WebP PROPERTIES + URL http://www.openexr.com/ + DESCRIPTION "A library for handling WebP/VP8 image files") + diff --git a/src/imageformats/CMakeLists.txt b/src/imageformats/CMakeLists.txt index 7cafd18..422515d 100644 --- a/src/imageformats/CMakeLists.txt +++ b/src/imageformats/CMakeLists.txt @@ -119,6 +119,32 @@ install(FILES tga.desktop DESTINATION ${SERVICES_INSTALL_DIR}/qimageioplugins/) ################################## +find_package(WebP COMPONENTS WebP) +set_package_properties(WebP PROPERTIES + TYPE OPTIONAL + PURPOSE "Required for the QImage plugin for WebP images" +) + +if(WebP_FOUND) + add_library(kimg_webp MODULE webp.cpp) + target_link_libraries(kimg_webp Qt5::Gui WebP::WebP) + + install(TARGETS kimg_webp DESTINATION ${QT_PLUGIN_INSTALL_DIR}/imageformats/) + install(FILES webp.desktop DESTINATION ${SERVICES_INSTALL_DIR}/qimageioplugins/) + + find_package(SharedMimeInfo) + set_package_properties(SharedMimeInfo PROPERTIES + TYPE RECOMMENDED + PURPOSE "Required to install the WebP MIME Type information" + ) + if (SharedMimeInfo_FOUND) + install(FILES webp.xml DESTINATION ${XDG_MIME_INSTALL_DIR}) + update_xdg_mimetypes(${XDG_MIME_INSTALL_DIR}) + endif() +endif() + +################################## + add_library(kimg_xcf MODULE xcf.cpp) target_link_libraries(kimg_xcf Qt5::Gui) diff --git a/src/imageformats/webp.cpp b/src/imageformats/webp.cpp new file mode 100644 index 0000000..f22dcd8 --- /dev/null +++ b/src/imageformats/webp.cpp @@ -0,0 +1,232 @@ +/* +QImageIO Routines to read/write WebP images. + +Copyright (c) 2012,2013 Martin Koller + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*/ + +#include + +#include "webp.h" +#include +#include + +#include +#include + +//--------------------------------------------------------------------- + +WebPHandler::WebPHandler() + : quality(75) +{ +} + +//--------------------------------------------------------------------- + +bool WebPHandler::canRead() const +{ + if (canRead(device())) { + setFormat("webp"); + return true; + } + return false; +} + +//--------------------------------------------------------------------- + +bool WebPHandler::read(QImage *retImage) +{ + QByteArray data = device()->readAll(); + + WebPBitstreamFeatures features; + VP8StatusCode ret = WebPGetFeatures(reinterpret_cast(data.constData()), data.size(), &features); + if ( ret != VP8_STATUS_OK ) { + return false; + } + + if ( features.has_alpha ) { + *retImage = QImage(features.width, features.height, QImage::Format_ARGB32); + } else { + *retImage = QImage(features.width, features.height, QImage::Format_RGB32); + } + + if ( retImage->isNull() ) { // out of memory + return false; + } + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if ( WebPDecodeARGBInto(reinterpret_cast(data.constData()), + data.size(), reinterpret_cast(retImage->bits()), + retImage->byteCount(), retImage->bytesPerLine()) == 0 ) { + return false; + } +#else + if ( WebPDecodeBGRAInto(reinterpret_cast(data.constData()), + data.size(), reinterpret_cast(retImage->bits()), + retImage->byteCount(), retImage->bytesPerLine()) == 0 ) { + return false; + } +#endif + + return true; +} + +//--------------------------------------------------------------------- + +bool WebPHandler::write(const QImage &image) +{ + // limitation in WebP + if ( (image.height() > 16383) || (image.height() == 0) || + (image.width() > 16383) || (image.width() == 0) ) + return false; + + uint8_t *imageData = new uint8_t[image.width() * image.height() * (3 + image.hasAlphaChannel())]; + + size_t idx = 0; + for (int y = 0; y < image.height(); y++) { + const QRgb *scanline = reinterpret_cast(image.constScanLine(y)); + for (int x = 0; x < image.width(); x++) { + imageData[idx++] = qRed(scanline[x]); + imageData[idx++] = qGreen(scanline[x]); + imageData[idx++] = qBlue(scanline[x]); + + if ( image.hasAlphaChannel() ) { + imageData[idx++] = qAlpha(scanline[x]); + } + } + } + + uint8_t *output = 0; + size_t size; + if ( image.hasAlphaChannel() ) { + size = WebPEncodeRGBA(imageData, image.width(), image.height(), image.width() * 4, quality, &output); + } else { + size = WebPEncodeRGB(imageData, image.width(), image.height(), image.width() * 4, quality, &output); + } + delete [] imageData; + + if ( size == 0 ) { + free(output); + return false; + } + + device()->write(reinterpret_cast(output), size); + free(output); + + return true; +} + +//--------------------------------------------------------------------- + +QByteArray WebPHandler::format() const +{ + return "webp"; +} + +//--------------------------------------------------------------------- + +bool WebPHandler::supportsOption(ImageOption option) const +{ + return (option == Quality) || (option == Size); +} + +//--------------------------------------------------------------------- + +QVariant WebPHandler::option(ImageOption option) const +{ + switch ( option ) + { + case Quality: + return quality; + + case Size: { + QByteArray data = device()->peek(26); + + int width = 0, height = 0; + + if ( WebPGetInfo(reinterpret_cast(data.constData()), + data.size(), &width, &height) == 0 ) + return QSize(); // header error + + return QSize(width, height); + } + + default: return QVariant(); + } +} + +//--------------------------------------------------------------------- + +void WebPHandler::setOption(ImageOption option, const QVariant &value) +{ + if (option == Quality) + quality = qBound(0, value.toInt(), 100); +} + +//--------------------------------------------------------------------- + +bool WebPHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning("WebPHandler::canRead() called with no device"); + return false; + } + + // WebP file header: 4 bytes "RIFF", 4 bytes length, 4 bytes "WEBP" + QByteArray header = device->peek(12); + + return (header.size() == 12) && header.startsWith("RIFF") && header.endsWith("WEBP"); +} + +//--------------------------------------------------------------------- +//--------------------------------------------------------------------- + +QImageIOPlugin::Capabilities WebPPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + // this plugin produces webp images it cannot read at the moment, + // so do not claim to be able to write + if (format == "webp") + return Capabilities(CanRead); + //return Capabilities(CanRead | CanWrite); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && WebPHandler::canRead(device)) + cap |= CanRead; + /* + if (device->isWritable()) + cap |= CanWrite; + */ + return cap; +} + +//--------------------------------------------------------------------- + +QImageIOHandler *WebPPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new WebPHandler; + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +//--------------------------------------------------------------------- + diff --git a/src/imageformats/webp.desktop b/src/imageformats/webp.desktop new file mode 100644 index 0000000..3526be5 --- /dev/null +++ b/src/imageformats/webp.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=QImageIOPlugins +X-KDE-ImageFormat=webp +X-KDE-MimeType=image/x-webp +X-KDE-Read=true +X-KDE-Write=true diff --git a/src/imageformats/webp.h b/src/imageformats/webp.h new file mode 100644 index 0000000..02b90ee --- /dev/null +++ b/src/imageformats/webp.h @@ -0,0 +1,59 @@ +/* +QImageIO Routines to read/write WebP images. + +Copyright (c) 2012,2013 Martin Koller + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*/ + +#ifndef WEBP_H +#define WEBP_H + +#include + +class WebPHandler : public QImageIOHandler +{ +public: + WebPHandler(); + + virtual bool canRead() const; + virtual bool read(QImage *image); + virtual bool write(const QImage &image); + + virtual QByteArray format() const; + + virtual bool supportsOption(ImageOption option) const; + virtual QVariant option(ImageOption option) const; + virtual void setOption(ImageOption option, const QVariant &value); + + static bool canRead(QIODevice *device); + +private: + int quality; +}; + +class WebPPlugin : public QImageIOPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "webp.json") + +public: + virtual Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + virtual QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +#endif diff --git a/src/imageformats/webp.json b/src/imageformats/webp.json new file mode 100644 index 0000000..9e81ff9 --- /dev/null +++ b/src/imageformats/webp.json @@ -0,0 +1,4 @@ +{ + "Keys": [ "webp" ], + "MimeTypes": [ "image/x-webp" ] +} diff --git a/src/imageformats/webp.xml b/src/imageformats/webp.xml new file mode 100644 index 0000000..4246f76 --- /dev/null +++ b/src/imageformats/webp.xml @@ -0,0 +1,7 @@ + + + + WebP image + + +