LibreOffice Module vcl (master) 1
reader.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <memory>
21#include <vcl/graph.hxx>
22#include <tools/stream.hxx>
23#include <filter/WebpReader.hxx>
25#include <salinst.hxx>
26#include <sal/log.hxx>
28#include <svdata.hxx>
30
31#include <webp/decode.h>
32
33static bool readWebpInfo(SvStream& stream, std::vector<uint8_t>& data,
34 WebPBitstreamFeatures& features)
35{
36 for (;;)
37 {
38 // Read 4096 (more) bytes.
39 size_t lastSize = data.size();
40 data.resize(data.size() + 4096);
41 sal_Size nBytesRead = stream.ReadBytes(data.data() + lastSize, 4096);
42 if (nBytesRead <= 0)
43 return false;
44 data.resize(lastSize + nBytesRead);
45 int status = WebPGetFeatures(data.data(), data.size(), &features);
46 if (status == VP8_STATUS_OK)
47 break;
48 if (status == VP8_STATUS_NOT_ENOUGH_DATA)
49 continue; // Try again with 4096 more bytes read.
50 return false;
51 }
52 return true;
53}
54
55static bool readWebp(SvStream& stream, Graphic& graphic)
56{
57 WebPDecoderConfig config;
58 if (!WebPInitDecoderConfig(&config))
59 {
60 SAL_WARN("vcl.filter.webp", "WebPInitDecoderConfig() failed");
61 return false;
62 }
63 comphelper::ScopeGuard freeBuffer([&config]() { WebPFreeDecBuffer(&config.output); });
64 std::vector<uint8_t> data;
65 if (!readWebpInfo(stream, data, config.input))
66 return false;
67 // Here various parts of 'config' can be altered if wanted.
68 const int& width = config.input.width;
69 const int& height = config.input.height;
70 const int& has_alpha = config.input.has_alpha;
71
72 if (width > SAL_MAX_INT32 / 8 || height > SAL_MAX_INT32 / 8)
73 return false; // avoid overflows later
74
75 const bool bFuzzing = utl::ConfigManager::IsFuzzing();
76 const bool bSupportsBitmap32 = bFuzzing || ImplGetSVData()->mpDefInst->supportsBitmap32();
77
78 Bitmap bitmap;
79 AlphaMask bitmapAlpha;
80 if (bSupportsBitmap32 && has_alpha)
81 {
82 bitmap = Bitmap(Size(width, height), vcl::PixelFormat::N32_BPP);
83 }
84 else
85 {
86 bitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
87 if (has_alpha)
88 bitmapAlpha = AlphaMask(Size(width, height));
89 }
90
91 BitmapScopedWriteAccess access(bitmap);
92 if (!access)
93 return false;
94 // If data cannot be read directly into the bitmap, read data first to this buffer and then convert.
95 std::vector<uint8_t> tmpRgbaData;
96 enum class PixelMode
97 {
98 DirectRead, // read data directly to the bitmap
99 Split, // read to tmp buffer and split to rgb and alpha
100 SetPixel // read to tmp buffer and use setPixel()
101 };
102 PixelMode pixelMode = PixelMode::SetPixel;
103
104 config.output.width = width;
105 config.output.height = height;
106 config.output.is_external_memory = 1;
107 if (bSupportsBitmap32 && has_alpha)
108 {
109 switch (RemoveScanline(access->GetScanlineFormat()))
110 {
111 // Our bitmap32 code expects premultiplied.
113 config.output.colorspace = MODE_rgbA;
114 pixelMode = PixelMode::DirectRead;
115 break;
117 config.output.colorspace = MODE_bgrA;
118 pixelMode = PixelMode::DirectRead;
119 break;
121 config.output.colorspace = MODE_Argb;
122 pixelMode = PixelMode::DirectRead;
123 break;
124 default:
125 config.output.colorspace = MODE_RGBA;
126 pixelMode = PixelMode::SetPixel;
127 break;
128 }
129 }
130 else
131 {
132 if (has_alpha)
133 {
134 switch (RemoveScanline(access->GetScanlineFormat()))
135 {
137 config.output.colorspace = MODE_RGBA;
138 pixelMode = PixelMode::Split;
139 break;
141 config.output.colorspace = MODE_BGRA;
142 pixelMode = PixelMode::Split;
143 break;
144 default:
145 config.output.colorspace = MODE_RGBA;
146 pixelMode = PixelMode::SetPixel;
147 break;
148 }
149 }
150 else
151 {
152 switch (RemoveScanline(access->GetScanlineFormat()))
153 {
155 config.output.colorspace = MODE_RGB;
156 pixelMode = PixelMode::DirectRead;
157 break;
159 config.output.colorspace = MODE_BGR;
160 pixelMode = PixelMode::DirectRead;
161 break;
162 default:
163 config.output.colorspace = MODE_RGBA;
164 pixelMode = PixelMode::SetPixel;
165 break;
166 }
167 }
168 }
169 if (pixelMode == PixelMode::DirectRead)
170 {
171 config.output.u.RGBA.rgba = access->GetBuffer();
172 config.output.u.RGBA.stride = access->GetScanlineSize();
173 config.output.u.RGBA.size = access->GetScanlineSize() * access->Height();
174 }
175 else
176 {
177 tmpRgbaData.resize(width * height * 4);
178 config.output.u.RGBA.rgba = tmpRgbaData.data();
179 config.output.u.RGBA.stride = width * 4;
180 config.output.u.RGBA.size = tmpRgbaData.size();
181 }
182
183 std::unique_ptr<WebPIDecoder, decltype(&WebPIDelete)> decoder(WebPIDecode(nullptr, 0, &config),
184 WebPIDelete);
185
186 bool success = true;
187 for (;;)
188 {
189 // During first iteration, use data read while reading the header.
190 int status = WebPIAppend(decoder.get(), data.data(), data.size());
191 if (status == VP8_STATUS_OK)
192 break;
193 if (status != VP8_STATUS_SUSPENDED)
194 {
195 // An error, still try to return at least a partially read bitmap,
196 // even if returning an error flag.
197 success = false;
198 break;
199 }
200 // If more data is needed, reading 4096 bytes more and repeat.
201 data.resize(4096);
202 sal_Size nBytesRead = stream.ReadBytes(data.data(), 4096);
203 if (nBytesRead <= 0)
204 {
205 // Truncated file, again try to return at least something.
206 success = false;
207 break;
208 }
209 data.resize(nBytesRead);
210 }
211
212 switch (pixelMode)
213 {
214 case PixelMode::DirectRead:
215 {
216 // Adjust for IsBottomUp() if necessary.
217 if (access->IsBottomUp())
218 {
219 std::vector<char> tmp;
220 const sal_uInt32 lineSize = access->GetScanlineSize();
221 tmp.resize(lineSize);
222 for (tools::Long y = 0; y < access->Height() / 2; ++y)
223 {
224 tools::Long otherY = access->Height() - 1 - y;
225 memcpy(tmp.data(), access->GetScanline(y), lineSize);
226 memcpy(access->GetScanline(y), access->GetScanline(otherY), lineSize);
227 memcpy(access->GetScanline(otherY), tmp.data(), lineSize);
228 }
229 }
230 break;
231 }
232 case PixelMode::Split:
233 {
234 // Split to normal and alpha bitmaps.
235 AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
236 for (tools::Long y = 0; y < access->Height(); ++y)
237 {
238 const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
239 unsigned char* dstB = access->GetScanline(y);
240 unsigned char* dstA = accessAlpha->GetScanline(y);
241 for (tools::Long x = 0; x < access->Width(); ++x)
242 {
243 memcpy(dstB, src, 3);
244 *dstA = 255 - *(src + 3);
245 src += 4;
246 dstB += 3;
247 dstA += 1;
248 }
249 }
250 break;
251 }
252 case PixelMode::SetPixel:
253 {
254 for (tools::Long y = 0; y < access->Height(); ++y)
255 {
256 const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
257 for (tools::Long x = 0; x < access->Width(); ++x)
258 {
259 sal_uInt8 r = src[0];
260 sal_uInt8 g = src[1];
261 sal_uInt8 b = src[2];
262 sal_uInt8 a = src[3];
263 access->SetPixel(y, x, Color(ColorAlpha, a, r, g, b));
264 src += 4;
265 }
266 }
267 if (!bitmapAlpha.IsEmpty())
268 {
269 AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
270 for (tools::Long y = 0; y < accessAlpha->Height(); ++y)
271 {
272 const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
273 for (tools::Long x = 0; x < accessAlpha->Width(); ++x)
274 {
275 sal_uInt8 a = src[3];
276 accessAlpha->SetPixelIndex(y, x, 255 - a);
277 src += 4;
278 }
279 }
280 }
281 break;
282 }
283 }
284
285 access.reset(); // Flush BitmapScopedWriteAccess.
286 if (bSupportsBitmap32 && has_alpha)
287 graphic = BitmapEx(bitmap);
288 else
289 {
290 if (has_alpha)
291 graphic = BitmapEx(bitmap, bitmapAlpha);
292 else
293 graphic = BitmapEx(bitmap);
294 }
295 return success;
296}
297
298bool ImportWebpGraphic(SvStream& rStream, Graphic& rGraphic)
299{
300 bool bRetValue = readWebp(rStream, rGraphic);
301 if (!bRetValue)
303 return bRetValue;
304}
305
306bool ReadWebpInfo(SvStream& stream, Size& pixelSize, sal_uInt16& bitsPerPixel, bool& hasAlpha)
307{
308 std::vector<uint8_t> data;
309 WebPBitstreamFeatures features;
310 if (!readWebpInfo(stream, data, features))
311 return false;
312 pixelSize = Size(features.width, features.height);
313 bitsPerPixel = features.has_alpha ? 32 : 24;
314 hasAlpha = features.has_alpha;
315 return true;
316}
317
318/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
ScanlineFormat RemoveScanline(ScanlineFormat nFormat)
Definition: Scanline.hxx:53
bool IsEmpty() const
bool supportsBitmap32() const
Definition: salinst.hxx:90
void SetError(ErrCode nErrorCode)
static bool IsFuzzing()
This template handles BitmapAccess the RAII way.
ColorAlpha
Reference< XOutputStream > stream
float y
float x
#define SVSTREAM_FILEFORMAT_ERROR
uno_Any a
#define SAL_WARN(area, stream)
config
long Long
static bool readWebpInfo(SvStream &stream, std::vector< uint8_t > &data, WebPBitstreamFeatures &features)
Definition: reader.cxx:33
static bool readWebp(SvStream &stream, Graphic &graphic)
Definition: reader.cxx:55
bool ReadWebpInfo(SvStream &stream, Size &pixelSize, sal_uInt16 &bitsPerPixel, bool &hasAlpha)
Definition: reader.cxx:306
bool ImportWebpGraphic(SvStream &rStream, Graphic &rGraphic)
Definition: reader.cxx:298
SalInstance * mpDefInst
Definition: svdata.hxx:389
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:77
unsigned char sal_uInt8
#define SAL_MAX_INT32