diff options
Diffstat (limited to 'vcl/source/filter/webp/writer.cxx')
-rw-r--r-- | vcl/source/filter/webp/writer.cxx | 206 |
1 files changed, 206 insertions, 0 deletions
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: */ |