Skip to content

Commit

Permalink
Pull request #220: Feature/MAGP-1137 add webp
Browse files Browse the repository at this point in the history
Merge in MAG/magics from feature/MAGP-1137-add-webp to develop

* commit 'e140097e74e0d8b64d741b6ac131a1f42c9c2c8c':
  CMAKE - add option to find and enable webp output MAGP-1137
  DRIVERS - add initial support for webp output to CairoDriver MAGP-1137
  VERSION - set version to 4.16 for new webp feature branch
  • Loading branch information
sylvielamythepaut committed Dec 20, 2023
2 parents 3fa5658 + e140097 commit 9695b29
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 7 deletions.
14 changes: 13 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ cmake_minimum_required( VERSION 3.12 FATAL_ERROR )
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild/cmake")
find_package( ecbuild 3.4 REQUIRED )

project( magics VERSION 4.15.1 LANGUAGES CXX )
project( magics VERSION 4.16.0 LANGUAGES CXX )

# make sure that the header files are installed into include/magics
# note that this needs to be done before ecbuild_declare_project()
Expand Down Expand Up @@ -56,6 +56,12 @@ ecbuild_add_option( FEATURE GEOTIFF
CONDITION HAVE_CAIRO
REQUIRED_PACKAGES GeoTIFF )

ecbuild_add_option( FEATURE WEBP
DEFAULT OFF
DESCRIPTION "webp support [implies cairo]"
CONDITION HAVE_CAIRO
REQUIRED_PACKAGES WEBP )

ecbuild_add_option( FEATURE NETCDF
DEFAULT ON
DESCRIPTION "enable netcdf support"
Expand Down Expand Up @@ -274,6 +280,12 @@ if( HAVE_GEOTIFF )
list( APPEND MAGICS_EXTRA_DEFINITIONS HAVE_GEOTIFF )
endif()

if( HAVE_WEBP )
list( APPEND MAGICS_EXTRA_INCLUDE_DIRS ${WEBP_INCLUDE_DIR} )
list( APPEND MAGICS_EXTRA_LIBRARIES ${WEBP_LIBRARY} )
list( APPEND MAGICS_EXTRA_DEFINITIONS HAVE_WEBP )
endif()

list( APPEND MAGICS_EXTRA_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} )

if( EC_OS_NAME MATCHES "windows" )
Expand Down
69 changes: 69 additions & 0 deletions cmake/FindWebP.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# (C) Copyright 2023- ECMWF.
#
# This software is licensed under the terms of the Apache Licence Version 2.0
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
# In applying this licence, ECMWF does not waive the privileges and immunities
# granted to it by virtue of its status as an intergovernmental organisation
# nor does it submit to any jurisdiction.

# - Try to find the WebP includes and library
# This module defines
#
# WEBP_FOUND - System has WebP
# WEBP_INCLUDE_DIRS - the WebP include directories
# WEBP_LIBRARIES - the libraries needed to use WebP
#
# The following paths will be searched with priority if set in CMake or env
#
# WEBP_DIR - root folder of the WebP installation
# WEBP_PATH - root folder of the WebP installation
#
# Define WEBP_MIN_VERSION for which version desired.

if( NOT WEBP_PATH )
if ( NOT "$ENV{WEBP_PATH}" STREQUAL "" )
set( WEBP_PATH "$ENV{WEBP_PATH}" )
elseif ( NOT "$ENV{WEBP_DIR}" STREQUAL "" )
set( WEBP_PATH "$ENV{WEBP_DIR}" )
endif()
endif()

if( NOT WEBP_PATH )

include(FindPkgConfig)

if(WEBP_MIN_VERSION)
pkg_check_modules(PKWEBP ${_pkgconfig_REQUIRED} QUIET WEBP>=${WEBP_MIN_VERSION})
else()
pkg_check_modules(PKWEBP ${_pkgconfig_REQUIRED} QUIET WEBP)
endif()

if( PKG_CONFIG_FOUND AND PKWEBP_FOUND )

find_path(WEBP_INCLUDE_DIR encode.h HINTS ${PKWEBP_INCLUDEDIR} ${PKWEBP_INCLUDE_DIRS} PATH_SUFFIXES WEBP NO_DEFAULT_PATH )
find_library(WEBP_LIBRARY webp HINTS ${PKWEBP_LIBDIR} ${PKWEBP_LIBRARY_DIRS} PATH_SUFFIXES WEBP NO_DEFAULT_PATH )

endif()

endif()

if( WEBP_PATH )

find_path(WEBP_INCLUDE_DIR NAMES encode.h PATHS ${WEBP_PATH} ${WEBP_PATH}/include PATH_SUFFIXES WEBP NO_DEFAULT_PATH )
find_library(WEBP_LIBRARY NAMES webp PATHS ${WEBP_PATH} ${WEBP_PATH}/lib PATH_SUFFIXES WEBP NO_DEFAULT_PATH )

endif()

find_path(WEBP_INCLUDE_DIR NAMES encode.h PATHS PATH_SUFFIXES WEBP )
find_library( WEBP_LIBRARY NAMES webp PATHS PATH_SUFFIXES WEBP )


# handle the QUIETLY and REQUIRED arguments and set GRIBAPI_FOUND
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WEBP DEFAULT_MSG
WEBP_LIBRARY WEBP_INCLUDE_DIR)

set( WEBP_LIBRARIES ${WEBP_LIBRARY} )
set( WEBP_INCLUDE_DIRS ${WEBP_INCLUDE_DIR} )

mark_as_advanced( WEBP_INCLUDE_DIR WEBP_LIBRARY )
8 changes: 8 additions & 0 deletions src/common/OutputFactory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ void CAIRO_GeoTiffOutputFactory::set(DriverManager& magics, const XmlNode& node)

magics.push_back(driver);
}

void CAIRO_WebpOutputFactory::set(DriverManager& magics, const XmlNode& node) {
CairoDriver* driver = new CairoDriver();
driver->set(node);
driver->setWEBP();

magics.push_back(driver);
}
#endif

void KML_KmlOutputFactory::set(DriverManager& magics, const XmlNode& node) {
Expand Down
9 changes: 9 additions & 0 deletions src/common/OutputFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,15 @@ class CAIRO_GeoTiffOutputFactory : public OutputFactory {
virtual OutputFactory* clone() const override { return new CAIRO_GeoTiffOutputFactory(); }
virtual void set(DriverManager&, const XmlNode&) override;
};

class CAIRO_WebpOutputFactory : public OutputFactory {
public:
CAIRO_WebpOutputFactory() {}
virtual ~CAIRO_WebpOutputFactory() override {}

virtual OutputFactory* clone() const override { return new CAIRO_WebpOutputFactory(); }
virtual void set(DriverManager&, const XmlNode&) override;
};
#endif

class KML_KmlOutputFactory : public OutputFactory {
Expand Down
5 changes: 4 additions & 1 deletion src/common/OutputHandler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ void OutputHandler::set(const XmlNode& node, DriverManager& magics) {
void OutputHandler::drivers(vector<string>& ds) {
vector<string> all = {"ps", "eps", "ps_pdf", "gd_png", "jpeg", "gif", "gif_animation",
"svg", "mgb", "png", "pdf", "cairo", "cairo_svg", "cairo_ps",
"cairo_eps", "geotiff", "kml", "geojson"};
"cairo_eps", "geotiff", "webp", "kml", "geojson"};

for (const auto& d : all) {
try {
Expand Down Expand Up @@ -142,6 +142,9 @@ static SimpleObjectMaker<CAIRO_EpsOutputFactory, OutputFactory> ceps("cairo_eps"
#ifdef HAVE_GEOTIFF
static SimpleObjectMaker<CAIRO_GeoTiffOutputFactory, OutputFactory> geotiff("geotiff");
#endif
#ifdef HAVE_WEBP
static SimpleObjectMaker<CAIRO_WebpOutputFactory, OutputFactory> webp("webp");
#endif
#endif

static SimpleObjectMaker<KML_KmlOutputFactory, OutputFactory> kml("kml");
Expand Down
111 changes: 108 additions & 3 deletions src/drivers/CairoDriver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ void CairoDriver::open() {


void CairoDriver::setupNewSurface() const {
if (magCompare(backend_, "png") || magCompare(backend_, "geotiff")) {
if (magCompare(backend_, "png") || magCompare(backend_, "geotiff") || magCompare(backend_, "webp")) {
surface_ = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dimensionXglobal_, dimensionYglobal_);
}
else if (magCompare(backend_, "pdf")) {
Expand Down Expand Up @@ -206,7 +206,7 @@ void CairoDriver::setupNewSurface() const {
#endif
}

if (magCompare(transparent_, "off") || !(magCompare(backend_, "png") || magCompare(backend_, "geotiff"))) {
if (magCompare(transparent_, "off") || !(magCompare(backend_, "png") || magCompare(backend_, "geotiff")|| magCompare(backend_, "webp"))) {
cairo_set_source_rgb(cr_, 1.0, 1.0, 1.0); /* white */
}
else {
Expand Down Expand Up @@ -243,7 +243,7 @@ void CairoDriver::close() {
*/
MAGICS_NO_EXPORT void CairoDriver::startPage() const {
if (currentPage_ > 0) {
if (magCompare(backend_, "png") || magCompare(backend_, "geotiff")) {
if (magCompare(backend_, "png") || magCompare(backend_, "geotiff") || magCompare(backend_, "webp")) {
cairo_destroy(cr_);
cairo_surface_destroy(surface_);

Expand Down Expand Up @@ -342,6 +342,14 @@ MAGICS_NO_EXPORT void CairoDriver::endPage() const {
write_tiff();
#else
MagLog::error() << "CairoDriver: GEOTIFF not enabled!" << std::endl;
#endif
}
else if (magCompare(backend_, "webp")) {
#ifdef HAVE_WEBP
fileName_ = getFileName("webp", currentPage_);
write_webp();
#else
MagLog::error() << "CairoDriver: WebP not enabled!" << std::endl;
#endif
}
}
Expand All @@ -360,6 +368,103 @@ void CairoDriver::closeLayer(Layer&) const {
cairo_restore(cr_);
}

#ifdef HAVE_WEBP

#include <webp/encode.h>
/*!
\brief write raster into WebP
Only the raw raster (normally written to a PNG) is here written into a Webp.
*/
MAGICS_NO_EXPORT void CairoDriver::write_webp() const {

WebPPicture picture;
uint32_t* argb_output;

int x, y;

const int width = cairo_image_surface_get_width(surface_);
const int height = cairo_image_surface_get_height(surface_);
const int stride = cairo_image_surface_get_stride(surface_);
const cairo_format_t format = cairo_image_surface_get_format(surface_);
unsigned char* data = cairo_image_surface_get_data(surface_);

if (format != CAIRO_FORMAT_RGB24 && format != CAIRO_FORMAT_ARGB32) {
MagLog::error() << "CairoDriver: invalid Cairo image format. Not able to create WebP. "<< fileName_ << std::endl;
return;
}

// Configure WebP - especially compression
WebPConfig config;
if (!WebPConfigPreset(&config, WEBP_PRESET_DRAWING, quality_))
return;
config.lossless = 1;
config.quality = 90;
config.thread_level = 1;
config.method = 2; // Compression method (0=fast/larger, 6=slow/smaller)

if (!WebPValidateConfig(&config)) {return;}
if (!WebPPictureInit(&picture)) {return;}
picture.use_argb = 1;
picture.width = width;
picture.height = height;

if (!WebPPictureAlloc(&picture)) {return;}

// Set up write-to-memory
WebPMemoryWriter writer;
WebPMemoryWriterInit(&writer);
picture.writer = WebPMemoryWrite;
picture.custom_ptr = &writer;

// Copy image data into WebP picture
argb_output = picture.argb;
for (y = 0; y < height; y++) {

// Get pixels at start of each row
uint32_t* src = (uint32_t*) data;
uint32_t* dst = argb_output;

// For each pixel in row
for (x = 0; x < width; x++) {

// Pull pixel data, removing alpha channel if necessary
uint32_t src_pixel = *src;
if (format != CAIRO_FORMAT_ARGB32)
src_pixel |= 0xFF000000;
*dst = src_pixel;
src++;
dst++;
}
// Next row
data += stride;
argb_output += picture.argb_stride;
}

////// Encode image
const int result = WebPEncode(&config, &picture);
if (result < 1) {
MagLog::error() << "CairoDriver: Encoding error: "<<picture.error_code<<". Not able to create WebP. "<< fileName_ << std::endl;
return;
}

FILE *f = ::fopen(fileName_.c_str(), "wb");
if( f == NULL ) {
MagLog::error() << "CairoDriver: Cannot WebP file "<< fileName_ << std::endl;
return;
}

uint8_t* webp = writer.mem;
size_t webp_size = writer.size;
fwrite(webp, webp_size, 1, f);
fclose(f);

WebPPictureFree(&picture);
return;
}
#endif // HAVE_WEBP


#ifdef HAVE_GEOTIFF

Expand Down
8 changes: 6 additions & 2 deletions src/drivers/CairoDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class CairoDriver : public BaseDriver, public CairoDriverAttributes {
if (magCompare(node.name(), "png") || magCompare(node.name(), "pdf") || magCompare(node.name(), "cairo_ps") ||
magCompare(node.name(), "cairo_svg") ||
// magCompare(node.name(), "x") ||
magCompare(node.name(), "cairo_eps") || magCompare(node.name(), "geotiff")) {
magCompare(node.name(), "cairo_eps") || magCompare(node.name(), "geotiff") || magCompare(node.name(), "webp")) {
XmlNode basic = node;
basic.name("driver");
BaseDriver::set(basic);
Expand All @@ -72,7 +72,8 @@ class CairoDriver : public BaseDriver, public CairoDriverAttributes {
void setSVG() const { backend_ = "svg"; }
// void setX() const {backend_ = "x";}
void setGEOTIFF() const { backend_ = "geotiff"; }
void setCairo() const { backend_ = "cairo"; }
void setWEBP() const { backend_ = "webp"; }
void setCairo() const { backend_ = "cairo"; }

private:
MAGICS_NO_EXPORT void startPage() const override;
Expand Down Expand Up @@ -118,6 +119,9 @@ class CairoDriver : public BaseDriver, public CairoDriverAttributes {
MAGICS_NO_EXPORT MFloat setY(const MFloat y) const override { return y; }
#ifdef HAVE_GEOTIFF
MAGICS_NO_EXPORT void write_tiff() const;
#endif
#ifdef HAVE_WEBP
MAGICS_NO_EXPORT void write_webp() const;
#endif
MAGICS_NO_EXPORT bool write_8bit_png() const;
mutable MFloat offsetX_;
Expand Down

0 comments on commit 9695b29

Please sign in to comment.