summaryrefslogtreecommitdiff
path: root/vcl/source
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source')
-rw-r--r--vcl/source/filter/FilterConfigCache.cxx6
-rw-r--r--vcl/source/filter/GraphicFormatDetector.cxx13
-rw-r--r--vcl/source/filter/graphicfilter.cxx74
-rw-r--r--vcl/source/filter/graphicfilter2.cxx33
-rw-r--r--vcl/source/filter/webp/reader.cxx319
-rw-r--r--vcl/source/filter/webp/writer.cxx206
-rw-r--r--vcl/source/gdi/gfxlink.cxx1
-rw-r--r--vcl/source/graphic/UnoGraphicDescriptor.cxx2
8 files changed, 652 insertions, 2 deletions
diff --git a/vcl/source/filter/FilterConfigCache.cxx b/vcl/source/filter/FilterConfigCache.cxx
index 478756ff17f0..acd3086e5a5d 100644
--- a/vcl/source/filter/FilterConfigCache.cxx
+++ b/vcl/source/filter/FilterConfigCache.cxx
@@ -41,8 +41,8 @@ using namespace ::com::sun::star::configuration ;
const char* FilterConfigCache::FilterConfigCacheEntry::InternalPixelFilterNameList[] =
{
- IMP_BMP, IMP_GIF, IMP_PNG,IMP_JPEG, IMP_XBM, IMP_XPM,
- EXP_BMP, EXP_JPEG, EXP_PNG, IMP_MOV, nullptr
+ IMP_BMP, IMP_GIF, IMP_PNG,IMP_JPEG, IMP_XBM, IMP_XPM, IMP_WEBP,
+ EXP_BMP, EXP_JPEG, EXP_PNG, IMP_MOV, EXP_WEBP, nullptr
};
const char* FilterConfigCache::FilterConfigCacheEntry::InternalVectorFilterNameList[] =
@@ -255,6 +255,8 @@ const char* FilterConfigCache::InternalFilterListForSvxLight[] =
"xpm","2","exp",
"svg","1","SVISVG",
"svg","2","SVESVG",
+ "webp","1","SVIWEBP",
+ "webp","2","SVEWEBP",
nullptr
};
diff --git a/vcl/source/filter/GraphicFormatDetector.cxx b/vcl/source/filter/GraphicFormatDetector.cxx
index 56f7a9f2d006..162125f3fe97 100644
--- a/vcl/source/filter/GraphicFormatDetector.cxx
+++ b/vcl/source/filter/GraphicFormatDetector.cxx
@@ -28,6 +28,7 @@
namespace vcl
{
+
namespace
{
bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen)
@@ -545,6 +546,18 @@ bool GraphicFormatDetector::checkPDF()
return false;
}
+bool GraphicFormatDetector::checkWEBP()
+{
+ if (maFirstBytes[0] == 'R' && maFirstBytes[1] == 'I' && maFirstBytes[2] == 'F'
+ && maFirstBytes[3] == 'F' && maFirstBytes[8] == 'W' && maFirstBytes[9] == 'E'
+ && maFirstBytes[10] == 'B' && maFirstBytes[11] == 'P')
+ {
+ msDetectedFormat = "WEBP";
+ return true;
+ }
+ return false;
+}
+
} // vcl namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx
index 4a606d7d1ebb..f57475eda91c 100644
--- a/vcl/source/filter/graphicfilter.cxx
+++ b/vcl/source/filter/graphicfilter.cxx
@@ -47,6 +47,8 @@
#include "jpeg/jpeg.hxx"
#include "ixbm/xbmread.hxx"
#include "ixpm/xpmread.hxx"
+#include <filter/WebpReader.hxx>
+#include <filter/WebpWriter.hxx>
#include <osl/module.hxx>
#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/awt/Size.hpp>
@@ -65,6 +67,7 @@
#include <vector>
#include <memory>
#include <string_view>
+#include <o3tl/string_view.hxx>
#include "FilterConfigCache.hxx"
#include "graphicfilter_internal.hxx"
@@ -74,6 +77,41 @@
#define PMGCHUNG_msOG 0x6d734f47 // Microsoft Office Animated GIF
+// Support for GfxLinkType::NativeWebp is so far disabled,
+// as enabling it would write .webp images e.g. to .odt documents,
+// making those images unreadable for older readers. So for now
+// disable the support so that .webp images will be written out as .png,
+// and somewhen later enable the support unconditionally.
+
+namespace
+{
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool starts_with(std::basic_string_view<charT, traits> sv,
+ std::basic_string_view<charT, traits> x) noexcept
+{
+ return sv.substr(0, x.size()) == x;
+}
+template <typename charT, typename traits = std::char_traits<charT>>
+constexpr bool starts_with(std::basic_string_view<charT, traits> sv, charT const* x)
+{
+ return starts_with(sv, std::basic_string_view<charT, traits>(x));
+}
+
+}
+
+static bool supportNativeWebp()
+{
+ const char* const testname = getenv("LO_TESTNAME");
+ if(testname == nullptr)
+ return false;
+ // Enable support only for those unittests that test it.
+ if( std::string_view("_anonymous_namespace___GraphicTest__testUnloadedGraphicLoading_") == testname
+ || std::string_view("VclFiltersTest__testExportImport_") == testname
+ || starts_with(std::string_view(testname), "WebpFilterTest__"))
+ return true;
+ return false;
+}
+
typedef ::std::vector< GraphicFilter* > FilterList_impl;
static FilterList_impl* pFilterHdlList = nullptr;
@@ -444,6 +482,16 @@ bool ImpPeekGraphicFormat( SvStream& rStream, OUString& rFormatExtension, bool b
}
}
+ if (!bTest || rFormatExtension.startsWith("WEBP"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkWEBP())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
return bTest && !bSomethingTested;
}
@@ -1325,6 +1373,13 @@ Graphic GraphicFilter::ImportUnloadedGraphic(SvStream& rIStream, sal_uInt64 size
{
eLinkType = GfxLinkType::NativePdf;
}
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WEBP))
+ {
+ if(supportNativeWebp())
+ eLinkType = GfxLinkType::NativeWebp;
+ else
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
else
{
nStatus = ERRCODE_GRFILTER_FILTERERROR;
@@ -1726,6 +1781,16 @@ ErrCode GraphicFilter::ImportGraphic( Graphic& rGraphic, const OUString& rPath,
else
nStatus = ERRCODE_GRFILTER_FILTERERROR;
}
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WEBP))
+ {
+ if (ImportWebpGraphic(rIStream, rGraphic))
+ {
+ if(supportNativeWebp())
+ eLinkType = GfxLinkType::NativeWebp;
+ }
+ else
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
else
nStatus = ERRCODE_GRFILTER_FILTERERROR;
}
@@ -2150,6 +2215,14 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& r
}
}
}
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_WEBP))
+ {
+ if (!ExportWebpGraphic(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
else
nStatus = ERRCODE_GRFILTER_FILTERERROR;
}
@@ -2230,6 +2303,7 @@ IMPL_LINK( GraphicFilter, FilterCallback, ConvertData&, rData, bool )
case ConvertDataFormat::WMF: aShortName = WMF_SHORTNAME; break;
case ConvertDataFormat::EMF: aShortName = EMF_SHORTNAME; break;
case ConvertDataFormat::SVG: aShortName = SVG_SHORTNAME; break;
+ case ConvertDataFormat::WEBP: aShortName = WEBP_SHORTNAME; break;
default:
break;
diff --git a/vcl/source/filter/graphicfilter2.cxx b/vcl/source/filter/graphicfilter2.cxx
index ebebf829a33f..dd13e8123340 100644
--- a/vcl/source/filter/graphicfilter2.cxx
+++ b/vcl/source/filter/graphicfilter2.cxx
@@ -25,6 +25,7 @@
#include <vcl/outdev.hxx>
#include <vcl/graphicfilter.hxx>
#include <unotools/ucbstreamhelper.hxx>
+#include <filter/WebpReader.hxx>
#include "graphicfilter_internal.hxx"
#define DATA_SIZE 640
@@ -87,6 +88,7 @@ bool GraphicDescriptor::Detect( bool bExtendedInfo )
else if ( ImpDetectPSD( rStm, bExtendedInfo ) ) bRet = true;
else if ( ImpDetectEPS( rStm, bExtendedInfo ) ) bRet = true;
else if ( ImpDetectPCD( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectWEBP( rStm, bExtendedInfo ) ) bRet = true;
rStm.SetEndian( nOldFormat );
}
@@ -1143,6 +1145,36 @@ bool GraphicDescriptor::ImpDetectSVG( SvStream& /*rStm*/, bool /*bExtendedInfo*/
return bRet;
}
+bool GraphicDescriptor::ImpDetectWEBP( SvStream& rStm, bool bExtendedInfo )
+{
+ sal_uInt32 nTemp32 = 0;
+ bool bRet = false;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::BIG );
+ rStm.ReadUInt32( nTemp32 );
+
+ if ( nTemp32 == 0x52494646 )
+ {
+ rStm.ReadUInt32( nTemp32 ); // skip
+ rStm.ReadUInt32( nTemp32 );
+ if ( nTemp32 == 0x57454250 )
+ {
+ nFormat = GraphicFileFormat::WEBP;
+ bRet = true;
+
+ if ( bExtendedInfo )
+ {
+ rStm.Seek(nStmPos);
+ ReadWebpInfo(rStm, aPixSize, nBitsPerPixel, bIsAlpha );
+ bIsTransparent = bIsAlpha;
+ }
+ }
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
OUString GraphicDescriptor::GetImportFormatShortName( GraphicFileFormat nFormat )
{
const char *pKeyName = nullptr;
@@ -1172,6 +1204,7 @@ OUString GraphicDescriptor::GetImportFormatShortName( GraphicFileFormat nFormat
case GraphicFileFormat::WMF : pKeyName = "wmf"; break;
case GraphicFileFormat::EMF : pKeyName = "emf"; break;
case GraphicFileFormat::SVG : pKeyName = "svg"; break;
+ case GraphicFileFormat::WEBP : pKeyName = "webp"; break;
default: assert(false);
}
diff --git a/vcl/source/filter/webp/reader.cxx b/vcl/source/filter/webp/reader.cxx
new file mode 100644
index 000000000000..90a9278e16dc
--- /dev/null
+++ b/vcl/source/filter/webp/reader.cxx
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <vcl/graph.hxx>
+#include <tools/stream.hxx>
+#include <filter/WebpReader.hxx>
+#include <bitmapwriteaccess.hxx>
+#include <salinst.hxx>
+#include <sal/log.hxx>
+#include <unotools/configmgr.hxx>
+#include <svdata.hxx>
+
+#include <webp/decode.h>
+
+static bool readWebpInfo(SvStream& stream, std::vector<uint8_t>& data,
+ WebPBitstreamFeatures& features)
+{
+ for (;;)
+ {
+ // Read 4096 (more) bytes.
+ size_t lastSize = data.size();
+ data.resize(data.size() + 4096);
+ sal_Size nBytesRead = stream.ReadBytes(data.data() + lastSize, 4096);
+ if (nBytesRead <= 0)
+ return false;
+ data.resize(lastSize + nBytesRead);
+ int status = WebPGetFeatures(data.data(), data.size(), &features);
+ if (status == VP8_STATUS_OK)
+ break;
+ if (status == VP8_STATUS_NOT_ENOUGH_DATA)
+ continue; // Try again with 4096 more bytes read.
+ return false;
+ }
+ return true;
+}
+
+static bool readWebp(SvStream& stream, Graphic& graphic)
+{
+ WebPDecoderConfig config;
+ if (!WebPInitDecoderConfig(&config))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPInitDecoderConfig() failed");
+ return false;
+ }
+ // This unique_ptr is here just to ensure WebPFreeDecBuffer() is called at the end,
+ // it doesn't actually own the data as such.
+ std::unique_ptr<WebPDecBuffer, decltype(&WebPFreeDecBuffer)> freeBuffer(&config.output,
+ WebPFreeDecBuffer);
+ std::vector<uint8_t> data;
+ if (!readWebpInfo(stream, data, config.input))
+ return false;
+ // Here various parts of 'config' can be altered if wanted.
+ const int& width = config.input.width;
+ const int& height = config.input.height;
+ const int& has_alpha = config.input.has_alpha;
+
+ if (width > SAL_MAX_INT32 / 8 || height > SAL_MAX_INT32 / 8)
+ return false; // avoid overflows later
+
+ const bool bFuzzing = utl::ConfigManager::IsFuzzing();
+ auto pBackendCapabilities = ImplGetSVData()->mpDefInst->GetBackendCapabilities();
+ bool bSupportsBitmap32 = bFuzzing || pBackendCapabilities->mbSupportsBitmap32;
+
+ Bitmap bitmap;
+ AlphaMask bitmapAlpha;
+ if (bSupportsBitmap32 && has_alpha)
+ {
+ bitmap = Bitmap(Size(width, height), 32);
+ }
+ else
+ {
+ bitmap = Bitmap(Size(width, height), 24);
+ if (has_alpha)
+ bitmapAlpha = AlphaMask(Size(width, height));
+ }
+
+ BitmapScopedWriteAccess access(bitmap);
+ // If data cannot be read directly into the bitmap, read data first to this buffer and then convert.
+ std::vector<uint8_t> tmpRgbaData;
+ enum class PixelMode
+ {
+ DirectRead, // read data directly to the bitmap
+ Split, // read to tmp buffer and split to rgb and alpha
+ SetPixel // read to tmp buffer and use setPixel()
+ };
+ PixelMode pixelMode = PixelMode::SetPixel;
+
+ config.output.width = width;
+ config.output.height = height;
+ config.output.is_external_memory = 1;
+ if (bSupportsBitmap32 && has_alpha)
+ {
+ switch (RemoveScanline(access->GetScanlineFormat()))
+ {
+ // Our bitmap32 code expects premultiplied.
+ case ScanlineFormat::N32BitTcRgba:
+ config.output.colorspace = MODE_rgbA;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ case ScanlineFormat::N32BitTcBgra:
+ config.output.colorspace = MODE_bgrA;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ case ScanlineFormat::N32BitTcArgb:
+ config.output.colorspace = MODE_Argb;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ default:
+ config.output.colorspace = MODE_RGBA;
+ pixelMode = PixelMode::SetPixel;
+ break;
+ }
+ }
+ else
+ {
+ if (has_alpha)
+ {
+ switch (RemoveScanline(access->GetScanlineFormat()))
+ {
+ case ScanlineFormat::N24BitTcRgb:
+ config.output.colorspace = MODE_RGBA;
+ pixelMode = PixelMode::Split;
+ break;
+ case ScanlineFormat::N24BitTcBgr:
+ config.output.colorspace = MODE_BGRA;
+ pixelMode = PixelMode::Split;
+ break;
+ default:
+ config.output.colorspace = MODE_RGBA;
+ pixelMode = PixelMode::SetPixel;
+ break;
+ }
+ }
+ else
+ {
+ switch (RemoveScanline(access->GetScanlineFormat()))
+ {
+ case ScanlineFormat::N24BitTcRgb:
+ config.output.colorspace = MODE_RGB;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ case ScanlineFormat::N24BitTcBgr:
+ config.output.colorspace = MODE_BGR;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ default:
+ config.output.colorspace = MODE_RGBA;
+ pixelMode = PixelMode::SetPixel;
+ break;
+ }
+ }
+ }
+ if (pixelMode == PixelMode::DirectRead)
+ {
+ config.output.u.RGBA.rgba = access->GetBuffer();
+ config.output.u.RGBA.stride = access->GetScanlineSize();
+ config.output.u.RGBA.size = access->GetScanlineSize() * access->Height();
+ }
+ else
+ {
+ tmpRgbaData.resize(width * height * 4);
+ config.output.u.RGBA.rgba = tmpRgbaData.data();
+ config.output.u.RGBA.stride = width * 4;
+ config.output.u.RGBA.size = tmpRgbaData.size();
+ }
+
+ std::unique_ptr<WebPIDecoder, decltype(&WebPIDelete)> decoder(WebPIDecode(nullptr, 0, &config),
+ WebPIDelete);
+
+ bool success = true;
+ for (;;)
+ {
+ // During first iteration, use data read while reading the header.
+ int status = WebPIAppend(decoder.get(), data.data(), data.size());
+ if (status == VP8_STATUS_OK)
+ break;
+ if (status != VP8_STATUS_SUSPENDED)
+ {
+ // An error, still try to return at least a partially read bitmap,
+ // even if returning an error flag.
+ success = false;
+ break;
+ }
+ // If more data is needed, reading 4096 bytes more and repeat.
+ data.resize(4096);
+ sal_Size nBytesRead = stream.ReadBytes(data.data(), 4096);
+ if (nBytesRead <= 0)
+ {
+ // Truncated file, again try to return at least something.
+ success = false;
+ break;
+ }
+ data.resize(nBytesRead);
+ }
+
+ switch (pixelMode)
+ {
+ case PixelMode::DirectRead:
+ {
+ // Adjust for IsBottomUp() if necessary.
+ if (access->IsBottomUp())
+ {
+ std::vector<char> tmp;
+ const sal_uInt32 lineSize = access->GetScanlineSize();
+ tmp.resize(lineSize);
+ for (tools::Long y = 0; y < access->Height() / 2; ++y)
+ {
+ tools::Long otherY = access->Height() - 1 - y;
+ memcpy(tmp.data(), access->GetScanline(y), lineSize);
+ memcpy(access->GetScanline(y), access->GetScanline(otherY), lineSize);
+ memcpy(access->GetScanline(otherY), tmp.data(), lineSize);
+ }
+ }
+ break;
+ }
+ case PixelMode::Split:
+ {
+ // Split to normal and alpha bitmaps.
+ AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
+ for (tools::Long y = 0; y < access->Height(); ++y)
+ {
+ const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
+ unsigned char* dstB = access->GetScanline(y);
+ unsigned char* dstA = accessAlpha->GetScanline(y);
+ for (tools::Long x = 0; x < access->Width(); ++x)
+ {
+ memcpy(dstB, src, 3);
+ *dstA = 255 - *(src + 3);
+ src += 4;
+ dstB += 3;
+ dstA += 1;
+ }
+ }
+ break;
+ }
+ case PixelMode::SetPixel:
+ {
+ for (tools::Long y = 0; y < access->Height(); ++y)
+ {
+ const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
+ for (tools::Long x = 0; x < access->Width(); ++x)
+ {
+ sal_uInt8 r = src[0];
+ sal_uInt8 g = src[1];
+ sal_uInt8 b = src[2];
+ sal_uInt8 a = src[3];
+ access->SetPixel(y, x, Color(255 - a, r, g, b));
+ src += 4;
+ }
+ }
+ if (!bitmapAlpha.IsEmpty())
+ {
+ AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
+ for (tools::Long y = 0; y < accessAlpha->Height(); ++y)
+ {
+ const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
+ for (tools::Long x = 0; x < accessAlpha->Width(); ++x)
+ {
+ sal_uInt8 a = src[3];
+ accessAlpha->SetPixelIndex(y, x, 255 - a);
+ src += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ access.reset(); // Flush BitmapScopedWriteAccess.
+ if (bSupportsBitmap32 && has_alpha)
+ graphic = BitmapEx(bitmap);
+ else
+ {
+ if (has_alpha)
+ graphic = BitmapEx(bitmap, bitmapAlpha);
+ else
+ graphic = BitmapEx(bitmap);
+ }
+ return success;
+}
+
+bool ImportWebpGraphic(SvStream& rStream, Graphic& rGraphic)
+{
+ bool bRetValue = readWebp(rStream, rGraphic);
+ if (!bRetValue)
+ rStream.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ return bRetValue;
+}
+
+bool ReadWebpInfo(SvStream& stream, Size& pixelSize, sal_uInt16& bitsPerPixel, bool& hasAlpha)
+{
+ std::vector<uint8_t> data;
+ WebPBitstreamFeatures features;
+ if (!readWebpInfo(stream, data, features))
+ return false;
+ pixelSize = Size(features.width, features.height);
+ bitsPerPixel = features.has_alpha ? 32 : 24;
+ hasAlpha = features.has_alpha;
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/webp/writer.cxx b/vcl/source/filter/webp/writer.cxx
new file mode 100644
index 000000000000..de6998caa591
--- /dev/null
+++ b/vcl/source/filter/webp/writer.cxx
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <vcl/graph.hxx>
+#include <tools/stream.hxx>
+#include <filter/WebpWriter.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <sal/log.hxx>
+
+#include <webp/encode.h>
+
+static int writerFunction(const uint8_t* data, size_t size, const WebPPicture* picture)
+{
+ SvStream* stream = static_cast<SvStream*>(picture->custom_ptr);
+ return stream->WriteBytes(data, size) == size ? 1 : 0;
+}
+
+static WebPPreset presetToValue(const OUString& preset)
+{
+ if (preset.equalsIgnoreAsciiCase("picture"))
+ return WEBP_PRESET_PICTURE;
+ if (preset.equalsIgnoreAsciiCase("photo"))
+ return WEBP_PRESET_PHOTO;
+ if (preset.equalsIgnoreAsciiCase("drawing"))
+ return WEBP_PRESET_DRAWING;
+ if (preset.equalsIgnoreAsciiCase("icon"))
+ return WEBP_PRESET_ICON;
+ if (preset.equalsIgnoreAsciiCase("text"))
+ return WEBP_PRESET_TEXT;
+ return WEBP_PRESET_DEFAULT;
+}
+
+static bool writeWebp(SvStream& rStream, const BitmapEx& bitmapEx, bool lossless,
+ const OUString& preset, int quality)
+{
+ WebPConfig config;
+ if (!WebPConfigInit(&config))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPConfigInit() failed");
+ return false;
+ }
+ if (lossless)
+ {
+ if (!WebPConfigLosslessPreset(&config, 6))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPConfigLosslessPreset() failed");
+ return false;
+ }
+ }
+ else
+ {
+ if (!WebPConfigPreset(&config, presetToValue(preset), quality))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPConfigPreset() failed");
+ return false;
+ }
+ }
+ // Here various parts of 'config' can be altered if wanted.
+ assert(WebPValidateConfig(&config));
+
+ const int width = bitmapEx.GetSizePixel().Width();
+ const int height = bitmapEx.GetSizePixel().Height();
+
+ WebPPicture picture;
+ if (!WebPPictureInit(&picture))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureInit() failed");
+ return false;
+ }
+ picture.width = width;
+ picture.height = height;
+ picture.use_argb = lossless ? 1 : 0; // libwebp recommends argb only for lossless
+ // This unique_ptr is here just to ensure WebPPictureFree() is called at the end,
+ // it doesn't actually own the data as such.
+ std::unique_ptr<WebPPicture, decltype(&WebPPictureFree)> freePicture(&picture, WebPPictureFree);
+
+ // Apparently libwebp needs the entire image data at once in WebPPicture,
+ // so allocate it and copy there.
+ Bitmap bitmap(bitmapEx.GetBitmap());
+ AlphaMask bitmapAlpha;
+ if (bitmapEx.IsAlpha())
+ bitmapAlpha = bitmapEx.GetAlpha();
+ Bitmap::ScopedReadAccess access(bitmap);
+ AlphaMask::ScopedReadAccess accessAlpha(bitmapAlpha);
+ bool dataDone = false;
+ if (!access->IsBottomUp() && bitmapAlpha.IsEmpty())
+ {
+ // Try to directly copy the bitmap data.
+ switch (access->GetScanlineFormat())
+ {
+ case ScanlineFormat::N24BitTcRgb:
+ if (!WebPPictureImportRGB(&picture, access->GetBuffer(), access->GetScanlineSize()))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureImportRGB() failed");
+ return false;
+ }
+ dataDone = true;
+ break;
+ case ScanlineFormat::N24BitTcBgr:
+ if (!WebPPictureImportBGR(&picture, access->GetBuffer(), access->GetScanlineSize()))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureImportBGR() failed");
+ return false;
+ }
+ dataDone = true;
+ break;
+ // Our argb formats are premultiplied, so can't read directly using libwebp functions.
+ default:
+ break;
+ }
+ }
+ if (!dataDone)
+ {
+ // It would be simpler to convert the bitmap to 32bpp, but our 32bpp support is broken
+ // (it's unspecified whether it's premultiplied or not, for example). So handle this manually.
+ // Special handling for some common cases, generic otherwise.
+ if (!WebPPictureAlloc(&picture))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureAlloc() failed");
+ return false;
+ }
+ std::vector<uint8_t> data;
+ const int bpp = 4;
+ data.resize(width * height * bpp);
+ if (!bitmapAlpha.IsEmpty())
+ {
+ for (tools::Long y = 0; y < access->Height(); ++y)
+ {
+ unsigned char* dst = data.data() + width * bpp * y;
+ const sal_uInt8* srcB = access->GetScanline(y);
+ const sal_uInt8* srcA = accessAlpha->GetScanline(y);
+ for (tools::Long x = 0; x < access->Width(); ++x)
+ {
+ BitmapColor color = access->GetPixelFromData(srcB, x);
+ BitmapColor transparency = accessAlpha->GetPixelFromData(srcA, x);
+ color.SetTransparency(transparency.GetIndex());
+ dst[0] = color.GetRed();
+ dst[1] = color.GetGreen();
+ dst[2] = color.GetBlue();
+ dst[3] = 255 - color.GetTransparency();
+ dst += bpp;
+ }
+ }
+ }
+ else
+ {
+ for (tools::Long y = 0; y < access->Height(); ++y)
+ {
+ unsigned char* dst = data.data() + width * bpp * y;
+ const sal_uInt8* src = access->GetScanline(y);
+ for (tools::Long x = 0; x < access->Width(); ++x)
+ {
+ Color color = access->GetPixelFromData(src, x);
+ dst[0] = color.GetRed();
+ dst[1] = color.GetGreen();
+ dst[2] = color.GetBlue();
+ dst[3] = 255 - color.GetTransparency();
+ dst += bpp;
+ }
+ }
+ }
+ // And now import from the temporary data. Use the import function rather
+ // than writing the data directly to avoid the need to write the data
+ // in the exact format WebPPicture wants (YUV, etc.).
+ if (!WebPPictureImportRGBA(&picture, data.data(), width * bpp))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureImportRGBA() failed");
+ return false;
+ }
+ }
+
+ picture.writer = writerFunction;
+ picture.custom_ptr = &rStream;
+ return WebPEncode(&config, &picture);
+}
+
+bool ExportWebpGraphic(SvStream& rStream, const Graphic& rGraphic,
+ FilterConfigItem* pFilterConfigItem)
+{
+ BitmapEx bitmap = rGraphic.GetBitmapEx();
+ // If lossless, neither presets nor quality matter.
+ bool lossless = pFilterConfigItem->ReadBool("Lossless", true);
+ // Preset is WebPPreset values.
+ const OUString preset = pFilterConfigItem->ReadString("Preset", "");
+ int quality = pFilterConfigItem->ReadInt32("Quality", 75);
+ return writeWebp(rStream, bitmap, lossless, preset, quality);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/gdi/gfxlink.cxx b/vcl/source/gdi/gfxlink.cxx
index 83936c277ead..00da8ff5f1c4 100644
--- a/vcl/source/gdi/gfxlink.cxx
+++ b/vcl/source/gdi/gfxlink.cxx
@@ -137,6 +137,7 @@ bool GfxLink::LoadNative( Graphic& rGraphic )
case GfxLinkType::NativeSvg: aShortName = SVG_SHORTNAME; break;
case GfxLinkType::NativeBmp: aShortName = BMP_SHORTNAME; break;
case GfxLinkType::NativePdf: aShortName = PDF_SHORTNAME; break;
+ case GfxLinkType::NativeWebp: aShortName = WEBP_SHORTNAME; break;
default: break;
}
if (!aShortName.isEmpty())
diff --git a/vcl/source/graphic/UnoGraphicDescriptor.cxx b/vcl/source/graphic/UnoGraphicDescriptor.cxx
index 6f65828fe992..e966df7e64bd 100644
--- a/vcl/source/graphic/UnoGraphicDescriptor.cxx
+++ b/vcl/source/graphic/UnoGraphicDescriptor.cxx
@@ -127,6 +127,7 @@ void GraphicDescriptor::implCreate( SvStream& rIStm, const OUString* pURL )
case GraphicFileFormat::RAS: pMimeType = MIMETYPE_RAS; cType = graphic::GraphicType::PIXEL; break;
case GraphicFileFormat::TGA: pMimeType = MIMETYPE_TGA; cType = graphic::GraphicType::PIXEL; break;
case GraphicFileFormat::PSD: pMimeType = MIMETYPE_PSD; cType = graphic::GraphicType::PIXEL; break;
+ case GraphicFileFormat::WEBP: pMimeType = MIMETYPE_WEBP; cType = graphic::GraphicType::PIXEL; break;
case GraphicFileFormat::EPS: pMimeType = MIMETYPE_EPS; cType = graphic::GraphicType::VECTOR; break;
case GraphicFileFormat::DXF: pMimeType = MIMETYPE_DXF; cType = graphic::GraphicType::VECTOR; break;
@@ -298,6 +299,7 @@ void GraphicDescriptor::_getPropertyValues( const comphelper::PropertyMapEntry**
case GfxLinkType::NativeWmf: pMimeType = MIMETYPE_WMF; break;
case GfxLinkType::NativeMet: pMimeType = MIMETYPE_MET; break;
case GfxLinkType::NativePct: pMimeType = MIMETYPE_PCT; break;
+ case GfxLinkType::NativeWebp: pMimeType = MIMETYPE_WEBP; break;
// added Svg mimetype support
case GfxLinkType::NativeSvg: pMimeType = MIMETYPE_SVG; break;