1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
29 #include <comphelper/scopeguard.hxx>
31 #include <webp/decode.h>
33 static bool readWebpInfo(SvStream
& stream
, std::vector
<uint8_t>& data
,
34 WebPBitstreamFeatures
& features
)
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);
44 data
.resize(lastSize
+ nBytesRead
);
45 int status
= WebPGetFeatures(data
.data(), data
.size(), &features
);
46 if (status
== VP8_STATUS_OK
)
48 if (status
== VP8_STATUS_NOT_ENOUGH_DATA
)
49 continue; // Try again with 4096 more bytes read.
55 static bool readWebp(SvStream
& stream
, Graphic
& graphic
)
57 WebPDecoderConfig config
;
58 if (!WebPInitDecoderConfig(&config
))
60 SAL_WARN("vcl.filter.webp", "WebPInitDecoderConfig() failed");
63 comphelper::ScopeGuard
freeBuffer([&config
]() { WebPFreeDecBuffer(&config
.output
); });
64 std::vector
<uint8_t> data
;
65 if (!readWebpInfo(stream
, data
, config
.input
))
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();
79 AlphaMask bitmapAlpha
;
80 if (bSupportsBitmap32
&& has_alpha
)
82 bitmap
= Bitmap(Size(width
, height
), vcl::PixelFormat::N32_BPP
);
86 bitmap
= Bitmap(Size(width
, height
), vcl::PixelFormat::N24_BPP
);
88 bitmapAlpha
= AlphaMask(Size(width
, height
));
91 BitmapScopedWriteAccess
access(bitmap
);
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
;
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
;
116 case ScanlineFormat::N32BitTcBgra
:
117 config
.output
.colorspace
= MODE_bgrA
;
118 pixelMode
= PixelMode::DirectRead
;
120 case ScanlineFormat::N32BitTcArgb
:
121 config
.output
.colorspace
= MODE_Argb
;
122 pixelMode
= PixelMode::DirectRead
;
125 config
.output
.colorspace
= MODE_RGBA
;
126 pixelMode
= PixelMode::SetPixel
;
134 switch (access
->GetScanlineFormat())
136 case ScanlineFormat::N24BitTcRgb
:
137 config
.output
.colorspace
= MODE_RGBA
;
138 pixelMode
= PixelMode::Split
;
140 case ScanlineFormat::N24BitTcBgr
:
141 config
.output
.colorspace
= MODE_BGRA
;
142 pixelMode
= PixelMode::Split
;
145 config
.output
.colorspace
= MODE_RGBA
;
146 pixelMode
= PixelMode::SetPixel
;
152 switch (access
->GetScanlineFormat())
154 case ScanlineFormat::N24BitTcRgb
:
155 config
.output
.colorspace
= MODE_RGB
;
156 pixelMode
= PixelMode::DirectRead
;
158 case ScanlineFormat::N24BitTcBgr
:
159 config
.output
.colorspace
= MODE_BGR
;
160 pixelMode
= PixelMode::DirectRead
;
163 config
.output
.colorspace
= MODE_RGB
;
164 pixelMode
= PixelMode::SetPixel
;
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();
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
),
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
)
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.
200 // If more data is needed, reading 4096 bytes more and repeat.
202 sal_Size nBytesRead
= stream
.ReadBytes(data
.data(), 4096);
205 // Truncated file, again try to return at least something.
209 data
.resize(nBytesRead
);
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
);
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);
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
));
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
);
285 access
.reset(); // Flush BitmapScopedWriteAccess.
286 if (bSupportsBitmap32
&& has_alpha
)
287 graphic
= BitmapEx(bitmap
);
291 graphic
= BitmapEx(bitmap
, bitmapAlpha
);
293 graphic
= BitmapEx(bitmap
);
298 bool ImportWebpGraphic(SvStream
& rStream
, Graphic
& rGraphic
)
300 bool bRetValue
= readWebp(rStream
, rGraphic
);
302 rStream
.SetError(SVSTREAM_FILEFORMAT_ERROR
);
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
))
312 pixelSize
= Size(features
.width
, features
.height
);
313 bitsPerPixel
= features
.has_alpha
? 32 : 24;
314 hasAlpha
= features
.has_alpha
;
318 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */