From 46823e7859f5a681d6abf1c3e5349ae19dd6ac17 Mon Sep 17 00:00:00 2001 From: StephanSiemen Date: Thu, 14 Dec 2023 16:14:10 +0000 Subject: [PATCH] DRIVERS - add initial support for webp output to CairoDriver MAGP-1137 Please note, changes in CMakeList are still needed to enable feature --- src/common/OutputFactory.cc | 8 +++ src/common/OutputFactory.h | 9 +++ src/common/OutputHandler.cc | 5 +- src/drivers/CairoDriver.cc | 111 +++++++++++++++++++++++++++++++++++- src/drivers/CairoDriver.h | 8 ++- 5 files changed, 135 insertions(+), 6 deletions(-) diff --git a/src/common/OutputFactory.cc b/src/common/OutputFactory.cc index b91f8da4c..29447e64c 100644 --- a/src/common/OutputFactory.cc +++ b/src/common/OutputFactory.cc @@ -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) { diff --git a/src/common/OutputFactory.h b/src/common/OutputFactory.h index bf3cbc35a..d423835ea 100644 --- a/src/common/OutputFactory.h +++ b/src/common/OutputFactory.h @@ -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 { diff --git a/src/common/OutputHandler.cc b/src/common/OutputHandler.cc index 42115be46..c039ca2b9 100644 --- a/src/common/OutputHandler.cc +++ b/src/common/OutputHandler.cc @@ -92,7 +92,7 @@ void OutputHandler::set(const XmlNode& node, DriverManager& magics) { void OutputHandler::drivers(vector& ds) { vector 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 { @@ -142,6 +142,9 @@ static SimpleObjectMaker ceps("cairo_eps" #ifdef HAVE_GEOTIFF static SimpleObjectMaker geotiff("geotiff"); #endif +#ifdef HAVE_WEBP +static SimpleObjectMaker webp("webp"); +#endif #endif static SimpleObjectMaker kml("kml"); diff --git a/src/drivers/CairoDriver.cc b/src/drivers/CairoDriver.cc index d610f67a4..4c3c2be9f 100644 --- a/src/drivers/CairoDriver.cc +++ b/src/drivers/CairoDriver.cc @@ -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")) { @@ -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 { @@ -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_); @@ -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 } } @@ -360,6 +368,103 @@ void CairoDriver::closeLayer(Layer&) const { cairo_restore(cr_); } +#ifdef HAVE_WEBP + +#include +/*! + \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: "<