tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / vcl / source / filter / webp / reader.cxx
blob11dabefbb7de9c88aa21879078c6d4bd15e28e6b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <memory>
21 #include <vcl/graph.hxx>
22 #include <tools/stream.hxx>
23 #include <filter/WebpReader.hxx>
24 #include <vcl/BitmapWriteAccess.hxx>
25 #include <salinst.hxx>
26 #include <sal/log.hxx>
27 #include <comphelper/configuration.hxx>
28 #include <svdata.hxx>
29 #include <comphelper/scopeguard.hxx>
31 #include <webp/decode.h>
33 static bool readWebpInfo(SvStream& stream, std::vector<uint8_t>& data,
34 WebPBitstreamFeatures& features)
36 for (;;)
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;
52 return true;
55 static bool readWebp(SvStream& stream, Graphic& graphic)
57 WebPDecoderConfig config;
58 if (!WebPInitDecoderConfig(&config))
60 SAL_WARN("vcl.filter.webp", "WebPInitDecoderConfig() failed");
61 return false;
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;
72 if (width > SAL_MAX_INT32 / 8 || height > SAL_MAX_INT32 / 8)
73 return false; // avoid overflows later
75 const bool bFuzzing = comphelper::IsFuzzing();
76 const bool bSupportsBitmap32 = bFuzzing || ImplGetSVData()->mpDefInst->supportsBitmap32();
78 Bitmap bitmap;
79 AlphaMask bitmapAlpha;
80 if (bSupportsBitmap32 && has_alpha)
82 bitmap = Bitmap(Size(width, height), vcl::PixelFormat::N32_BPP);
84 else
86 bitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
87 if (has_alpha)
88 bitmapAlpha = AlphaMask(Size(width, height));
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
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()
102 PixelMode pixelMode = PixelMode::SetPixel;
104 config.output.width = width;
105 config.output.height = height;
106 config.output.is_external_memory = 1;
107 if (bSupportsBitmap32 && has_alpha)
109 switch (access->GetScanlineFormat())
111 // Our bitmap32 code expects premultiplied.
112 case ScanlineFormat::N32BitTcRgba:
113 config.output.colorspace = MODE_rgbA;
114 pixelMode = PixelMode::DirectRead;
115 break;
116 case ScanlineFormat::N32BitTcBgra:
117 config.output.colorspace = MODE_bgrA;
118 pixelMode = PixelMode::DirectRead;
119 break;
120 case ScanlineFormat::N32BitTcArgb:
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;
130 else
132 if (has_alpha)
134 switch (access->GetScanlineFormat())
136 case ScanlineFormat::N24BitTcRgb:
137 config.output.colorspace = MODE_RGBA;
138 pixelMode = PixelMode::Split;
139 break;
140 case ScanlineFormat::N24BitTcBgr:
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;
150 else
152 switch (access->GetScanlineFormat())
154 case ScanlineFormat::N24BitTcRgb:
155 config.output.colorspace = MODE_RGB;
156 pixelMode = PixelMode::DirectRead;
157 break;
158 case ScanlineFormat::N24BitTcBgr:
159 config.output.colorspace = MODE_BGR;
160 pixelMode = PixelMode::DirectRead;
161 break;
162 default:
163 config.output.colorspace = MODE_RGB;
164 pixelMode = PixelMode::SetPixel;
165 break;
169 if (pixelMode == PixelMode::DirectRead)
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();
175 else
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();
183 std::unique_ptr<WebPIDecoder, decltype(&WebPIDelete)> decoder(WebPIDecode(nullptr, 0, &config),
184 WebPIDelete);
186 bool success = true;
187 for (;;)
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)
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;
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)
205 // Truncated file, again try to return at least something.
206 success = false;
207 break;
209 data.resize(nBytesRead);
212 switch (pixelMode)
214 case PixelMode::DirectRead:
216 // Adjust for IsBottomUp() if necessary.
217 if (access->IsBottomUp())
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)
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);
230 break;
232 case PixelMode::Split:
234 // Split to normal and alpha bitmaps.
235 BitmapScopedWriteAccess accessAlpha(bitmapAlpha);
236 for (tools::Long y = 0; y < access->Height(); ++y)
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)
243 memcpy(dstB, src, 3);
244 *dstA = *(src + 3);
245 src += 4;
246 dstB += 3;
247 dstA += 1;
250 break;
252 case PixelMode::SetPixel:
254 for (tools::Long y = 0; y < access->Height(); ++y)
256 const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
257 for (tools::Long x = 0; x < access->Width(); ++x)
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;
267 if (!bitmapAlpha.IsEmpty())
269 BitmapScopedWriteAccess accessAlpha(bitmapAlpha);
270 for (tools::Long y = 0; y < accessAlpha->Height(); ++y)
272 const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
273 for (tools::Long x = 0; x < accessAlpha->Width(); ++x)
275 sal_uInt8 a = src[3];
276 accessAlpha->SetPixelIndex(y, x, a);
277 src += 4;
281 break;
285 access.reset(); // Flush BitmapScopedWriteAccess.
286 if (bSupportsBitmap32 && has_alpha)
287 graphic = BitmapEx(bitmap);
288 else
290 if (has_alpha)
291 graphic = BitmapEx(bitmap, bitmapAlpha);
292 else
293 graphic = BitmapEx(bitmap);
295 return success;
298 bool ImportWebpGraphic(SvStream& rStream, Graphic& rGraphic)
300 bool bRetValue = readWebp(rStream, rGraphic);
301 if (!bRetValue)
302 rStream.SetError(SVSTREAM_FILEFORMAT_ERROR);
303 return bRetValue;
306 bool ReadWebpInfo(SvStream& stream, Size& pixelSize, sal_uInt16& bitsPerPixel, bool& hasAlpha)
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;
318 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */