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
This commit is contained in:
Alex Merry 2014-01-19 11:23:06 +00:00
parent f490ab0761
commit 7177296335
16 changed files with 539 additions and 0 deletions

View File

@ -53,6 +53,7 @@ kimageformats_read_tests(
rgb
tga
xcf
webp
)
# Basic write tests

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

203
cmake/FindWebP.cmake Normal file
View File

@ -0,0 +1,203 @@
# Try to find the WebP library
#
# This will define:
#
# WebP_FOUND - True if WebP is available
# WebP_LIBRARIES - Link to these to use WebP
# WebP_INCLUDE_DIRS - Include directory for WebP
# WebP_DEFINITIONS - Compiler flags required to link against WebP
#
# In addition the following more fine grained variables will be defined:
#
# WebP_WebP_FOUND WebP_WebP_INCLUDE_DIR WebP_WebP_LIBRARY
# WebP_Decoder_FOUND WebP_Decoder_INCLUDE_DIR WebP_Decoder_LIBRARY
# WebP_DeMux_FOUND WebP_DeMux_INCLUDE_DIR WebP_DeMux_LIBRARY
# WebP_Mux_FOUND WebP_Mux_INCLUDE_DIR WebP_Mux_LIBRARY
#
# Additionally, the following imported targets will be defined:
#
# WebP::WebP
# WebP::Decoder
# WebP::DeMux
# WebP::Mux
#
# Note that the Decoder library provides a strict subset of the functionality
# of the WebP library; it is therefore not included in WebP_LIBRARIES (unless
# the WebP library is not found), and you should not link to both WebP::WebP
# and WebP::Decoder.
#
# Copyright (c) 2011 Fredrik Höglund <fredrik@kde.org>
# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
# Copyright (c) 2013-2014, Alex Merry, <alex.merry@kdemail.net>
#
# 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")

View File

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

232
src/imageformats/webp.cpp Normal file
View File

@ -0,0 +1,232 @@
/*
QImageIO Routines to read/write WebP images.
Copyright (c) 2012,2013 Martin Koller <kollix@aon.at>
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 <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "webp.h"
#include <webp/decode.h>
#include <webp/encode.h>
#include <QImage>
#include <QVariant>
//---------------------------------------------------------------------
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<const uint8_t*>(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<const uint8_t*>(data.constData()),
data.size(), reinterpret_cast<uint8_t*>(retImage->bits()),
retImage->byteCount(), retImage->bytesPerLine()) == 0 ) {
return false;
}
#else
if ( WebPDecodeBGRAInto(reinterpret_cast<const uint8_t*>(data.constData()),
data.size(), reinterpret_cast<uint8_t*>(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<const QRgb*>(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<const char*>(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<const uint8_t*>(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;
}
//---------------------------------------------------------------------

View File

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

59
src/imageformats/webp.h Normal file
View File

@ -0,0 +1,59 @@
/*
QImageIO Routines to read/write WebP images.
Copyright (c) 2012,2013 Martin Koller <kollix@aon.at>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef WEBP_H
#define WEBP_H
#include <QImageIOHandler>
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

View File

@ -0,0 +1,4 @@
{
"Keys": [ "webp" ],
"MimeTypes": [ "image/x-webp" ]
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="image/x-webp">
<comment>WebP image</comment>
<glob pattern="*.webp"/>
</mime-type>
</mime-info>