LibreOffice Module vcl (master)  1
PngImageReader.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  */
10 
12 #include <png.h>
13 #include <rtl/crc.h>
14 #include <tools/stream.hxx>
15 #include <vcl/bitmap.hxx>
16 #include <vcl/alpha.hxx>
17 #include <vcl/BitmapTools.hxx>
18 #include <unotools/configmgr.hxx>
19 
21 #include <svdata.hxx>
22 #include <salinst.hxx>
23 
24 namespace
25 {
26 void lclReadStream(png_structp pPng, png_bytep pOutBytes, png_size_t nBytesToRead)
27 {
28  png_voidp pIO = png_get_io_ptr(pPng);
29 
30  if (pIO == nullptr)
31  return;
32 
33  SvStream* pStream = static_cast<SvStream*>(pIO);
34 
35  sal_Size nBytesRead = pStream->ReadBytes(pOutBytes, nBytesToRead);
36 
37  if (nBytesRead != nBytesToRead)
38  {
39  if (!nBytesRead)
40  png_error(pPng, "Error reading");
41  else
42  {
43  // Make sure to not reuse old data (could cause infinite loop).
44  memset(pOutBytes + nBytesRead, 0, nBytesToRead - nBytesRead);
45  png_warning(pPng, "Short read");
46  }
47  }
48 }
49 
50 constexpr int PNG_SIGNATURE_SIZE = 8;
51 
52 bool isPng(SvStream& rStream)
53 {
54  // Check signature bytes
55  sal_uInt8 aHeader[PNG_SIGNATURE_SIZE];
56  rStream.ReadBytes(aHeader, PNG_SIGNATURE_SIZE);
57 
58  return png_sig_cmp(aHeader, 0, PNG_SIGNATURE_SIZE) == 0;
59 }
60 
61 bool reader(SvStream& rStream, BitmapEx& rBitmapEx, bool bUseBitmap32)
62 {
63  if (!isPng(rStream))
64  return false;
65 
66  png_structp pPng = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
67  if (!pPng)
68  return false;
69 
70  png_infop pInfo = png_create_info_struct(pPng);
71  if (!pInfo)
72  {
73  png_destroy_read_struct(&pPng, nullptr, nullptr);
74  return false;
75  }
76 
77  // All variables holding resources need to be declared here in order to be
78  // properly cleaned up in case of an error, otherwise libpng's longjmp()
79  // jumps over the destructor calls.
80  Bitmap aBitmap;
81  AlphaMask aBitmapAlpha;
82  Size prefSize;
83  BitmapScopedWriteAccess pWriteAccess;
84  AlphaScopedWriteAccess pWriteAccessAlpha;
85  std::vector<std::vector<png_byte>> aRows;
86 
87  if (setjmp(png_jmpbuf(pPng)))
88  {
89  png_destroy_read_struct(&pPng, &pInfo, nullptr);
90  // Set the bitmap if it contains something, even on failure. This allows
91  // reading images that are only partially broken.
92  pWriteAccess.reset();
93  pWriteAccessAlpha.reset();
94  if (!aBitmap.IsEmpty() && !aBitmapAlpha.IsEmpty())
95  rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
96  else if (!aBitmap.IsEmpty())
97  rBitmapEx = BitmapEx(aBitmap);
98  if (!rBitmapEx.IsEmpty() && !prefSize.IsEmpty())
99  {
100  rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
101  rBitmapEx.SetPrefSize(prefSize);
102  }
103  return false;
104  }
105 
106  png_set_option(pPng, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
107 
108  png_set_read_fn(pPng, &rStream, lclReadStream);
109 
111  png_set_crc_action(pPng, PNG_CRC_ERROR_QUIT, PNG_CRC_WARN_DISCARD);
112  else
113  png_set_crc_action(pPng, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
114 
115  png_set_sig_bytes(pPng, PNG_SIGNATURE_SIZE);
116 
117  png_read_info(pPng, pInfo);
118 
119  png_uint_32 width = 0;
120  png_uint_32 height = 0;
121  int bitDepth = 0;
122  int colorType = -1;
123  int interlace = -1;
124 
125  png_uint_32 returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, &colorType,
126  &interlace, nullptr, nullptr);
127 
128  if (returnValue != 1)
129  {
130  png_destroy_read_struct(&pPng, &pInfo, nullptr);
131  return false;
132  }
133 
134  if (colorType == PNG_COLOR_TYPE_PALETTE)
135  png_set_palette_to_rgb(pPng);
136 
137  if (colorType == PNG_COLOR_TYPE_GRAY)
138  png_set_expand_gray_1_2_4_to_8(pPng);
139 
140  if (png_get_valid(pPng, pInfo, PNG_INFO_tRNS))
141  png_set_tRNS_to_alpha(pPng);
142 
143  if (bitDepth == 16)
144  png_set_scale_16(pPng);
145 
146  if (bitDepth < 8)
147  png_set_packing(pPng);
148 
149  // Convert gray+alpha to RGBA, keep gray as gray.
150  if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA
151  || (colorType == PNG_COLOR_TYPE_GRAY && png_get_valid(pPng, pInfo, PNG_INFO_tRNS)))
152  {
153  png_set_gray_to_rgb(pPng);
154  }
155 
156  // Sets the filler byte - if RGB it converts to RGBA
157  // png_set_filler(pPng, 0xFF, PNG_FILLER_AFTER);
158 
159  int nNumberOfPasses = png_set_interlace_handling(pPng);
160 
161  png_read_update_info(pPng, pInfo);
162  returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, &colorType, nullptr,
163  nullptr, nullptr);
164 
165  if (returnValue != 1)
166  {
167  png_destroy_read_struct(&pPng, &pInfo, nullptr);
168  return false;
169  }
170 
171  if (bitDepth != 8
172  || (colorType != PNG_COLOR_TYPE_RGB && colorType != PNG_COLOR_TYPE_RGB_ALPHA
173  && colorType != PNG_COLOR_TYPE_GRAY))
174  {
175  png_destroy_read_struct(&pPng, &pInfo, nullptr);
176  return false;
177  }
178 
179  png_uint_32 res_x = 0;
180  png_uint_32 res_y = 0;
181  int unit_type = 0;
182  if (png_get_pHYs(pPng, pInfo, &res_x, &res_y, &unit_type) != 0
183  && unit_type == PNG_RESOLUTION_METER && res_x && res_y)
184  {
185  // convert into MapUnit::Map100thMM
186  prefSize = Size(static_cast<sal_Int32>((100000.0 * width) / res_x),
187  static_cast<sal_Int32>((100000.0 * height) / res_y));
188  }
189 
190  {
191  if (colorType == PNG_COLOR_TYPE_RGB)
192  {
193  aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
194  {
195  pWriteAccess = BitmapScopedWriteAccess(aBitmap);
196  if (!pWriteAccess)
197  {
198  png_destroy_read_struct(&pPng, &pInfo, nullptr);
199  return false;
200  }
201  ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
202  if (eFormat == ScanlineFormat::N24BitTcBgr)
203  png_set_bgr(pPng);
204 
205  for (int pass = 0; pass < nNumberOfPasses; pass++)
206  {
207  for (png_uint_32 y = 0; y < height; y++)
208  {
209  Scanline pScanline = pWriteAccess->GetScanline(y);
210  png_read_row(pPng, pScanline, nullptr);
211  }
212  }
213  pWriteAccess.reset();
214  }
215  rBitmapEx = BitmapEx(aBitmap);
216  }
217  else if (colorType == PNG_COLOR_TYPE_RGB_ALPHA)
218  {
219  size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo);
220 
221  if (bUseBitmap32)
222  {
223  aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N32_BPP);
224  {
225  pWriteAccess = BitmapScopedWriteAccess(aBitmap);
226  if (!pWriteAccess)
227  {
228  png_destroy_read_struct(&pPng, &pInfo, nullptr);
229  return false;
230  }
231  ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
232  if (eFormat == ScanlineFormat::N32BitTcAbgr
233  || eFormat == ScanlineFormat::N32BitTcBgra)
234  {
235  png_set_bgr(pPng);
236  }
237 
238  aRows = std::vector<std::vector<png_byte>>(height);
239  for (auto& rRow : aRows)
240  rRow.resize(aRowSizeBytes, 0);
241 
242  auto const alphaFirst = (eFormat == ScanlineFormat::N32BitTcAbgr
243  || eFormat == ScanlineFormat::N32BitTcArgb);
244  for (int pass = 0; pass < nNumberOfPasses; pass++)
245  {
246  for (png_uint_32 y = 0; y < height; y++)
247  {
248  Scanline pScanline = pWriteAccess->GetScanline(y);
249  png_bytep pRow = aRows[y].data();
250  png_read_row(pPng, pRow, nullptr);
251  size_t iColor = 0;
252  for (size_t i = 0; i < aRowSizeBytes; i += 4)
253  {
254  sal_Int8 alpha = pRow[i + 3];
255  if (alphaFirst)
256  {
257  pScanline[iColor++] = alpha;
258  }
259  pScanline[iColor++] = vcl::bitmap::premultiply(pRow[i + 0], alpha);
260  pScanline[iColor++] = vcl::bitmap::premultiply(pRow[i + 1], alpha);
261  pScanline[iColor++] = vcl::bitmap::premultiply(pRow[i + 2], alpha);
262  if (!alphaFirst)
263  {
264  pScanline[iColor++] = alpha;
265  }
266  }
267  }
268  }
269  pWriteAccess.reset();
270  }
271  rBitmapEx = BitmapEx(aBitmap);
272  }
273  else
274  {
275  aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
276  aBitmapAlpha = AlphaMask(Size(width, height), nullptr);
277  {
278  pWriteAccess = BitmapScopedWriteAccess(aBitmap);
279  if (!pWriteAccess)
280  {
281  png_destroy_read_struct(&pPng, &pInfo, nullptr);
282  return false;
283  }
284  ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
285  if (eFormat == ScanlineFormat::N24BitTcBgr)
286  png_set_bgr(pPng);
287 
288  pWriteAccessAlpha = AlphaScopedWriteAccess(aBitmapAlpha);
289 
290  aRows = std::vector<std::vector<png_byte>>(height);
291  for (auto& rRow : aRows)
292  rRow.resize(aRowSizeBytes, 0);
293 
294  for (int pass = 0; pass < nNumberOfPasses; pass++)
295  {
296  for (png_uint_32 y = 0; y < height; y++)
297  {
298  Scanline pScanline = pWriteAccess->GetScanline(y);
299  Scanline pScanAlpha = pWriteAccessAlpha->GetScanline(y);
300  png_bytep pRow = aRows[y].data();
301  png_read_row(pPng, pRow, nullptr);
302  size_t iAlpha = 0;
303  size_t iColor = 0;
304  for (size_t i = 0; i < aRowSizeBytes; i += 4)
305  {
306  pScanline[iColor++] = pRow[i + 0];
307  pScanline[iColor++] = pRow[i + 1];
308  pScanline[iColor++] = pRow[i + 2];
309  pScanAlpha[iAlpha++] = 0xFF - pRow[i + 3];
310  }
311  }
312  }
313  pWriteAccess.reset();
314  pWriteAccessAlpha.reset();
315  }
316  rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
317  }
318  }
319  else if (colorType == PNG_COLOR_TYPE_GRAY)
320  {
321  aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N8_BPP,
322  &Bitmap::GetGreyPalette(256));
323  aBitmap.Erase(COL_WHITE);
324  {
325  pWriteAccess = BitmapScopedWriteAccess(aBitmap);
326  if (!pWriteAccess)
327  {
328  png_destroy_read_struct(&pPng, &pInfo, nullptr);
329  return false;
330  }
331 
332  for (int pass = 0; pass < nNumberOfPasses; pass++)
333  {
334  for (png_uint_32 y = 0; y < height; y++)
335  {
336  Scanline pScanline = pWriteAccess->GetScanline(y);
337  png_read_row(pPng, pScanline, nullptr);
338  }
339  }
340  pWriteAccess.reset();
341  }
342  rBitmapEx = BitmapEx(aBitmap);
343  }
344  }
345 
346  png_read_end(pPng, pInfo);
347 
348  png_destroy_read_struct(&pPng, &pInfo, nullptr);
349 
350  if (!prefSize.IsEmpty())
351  {
352  rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
353  rBitmapEx.SetPrefSize(prefSize);
354  }
355 
356  return true;
357 }
358 
359 std::unique_ptr<sal_uInt8[]> getMsGifChunk(SvStream& rStream, sal_Int32* chunkSize)
360 {
361  if (chunkSize)
362  *chunkSize = 0;
363  if (!isPng(rStream))
364  return nullptr;
365  // It's easier to read manually the contents and find the chunk than
366  // try to get it using libpng.
367  // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_format
368  // Each chunk is: 4 bytes length, 4 bytes type, <length> bytes, 4 bytes crc
369  bool ignoreCrc = utl::ConfigManager::IsFuzzing();
370  for (;;)
371  {
372  sal_uInt32 length, type, crc;
373  rStream.ReadUInt32(length);
374  rStream.ReadUInt32(type);
375  if (!rStream.good())
376  return nullptr;
377  constexpr sal_uInt32 PNGCHUNK_msOG = 0x6d734f47; // Microsoft Office Animated GIF
378  constexpr sal_uInt64 MSGifHeaderSize = 11; // "MSOFFICE9.0"
379  if (type == PNGCHUNK_msOG && length > MSGifHeaderSize)
380  {
381  // calculate chunktype CRC (swap it back to original byte order)
382  sal_uInt32 typeForCrc = type;
383 #if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
384  typeForCrc = OSL_SWAPDWORD(typeForCrc);
385 #endif
386  sal_uInt32 computedCrc = rtl_crc32(0, &typeForCrc, 4);
387  const sal_uInt64 pos = rStream.Tell();
388  if (pos + length >= rStream.TellEnd())
389  return nullptr; // broken PNG
390 
391  char msHeader[MSGifHeaderSize];
392  if (rStream.ReadBytes(msHeader, MSGifHeaderSize) != MSGifHeaderSize)
393  return nullptr;
394  computedCrc = rtl_crc32(computedCrc, msHeader, MSGifHeaderSize);
395  length -= MSGifHeaderSize;
396 
397  std::unique_ptr<sal_uInt8[]> chunk(new sal_uInt8[length]);
398  if (rStream.ReadBytes(chunk.get(), length) != length)
399  return nullptr;
400  computedCrc = rtl_crc32(computedCrc, chunk.get(), length);
401  rStream.ReadUInt32(crc);
402  if (!ignoreCrc && crc != computedCrc)
403  continue; // invalid chunk, ignore
404  if (chunkSize)
405  *chunkSize = length;
406  return chunk;
407  }
408  if (rStream.remainingSize() < length)
409  return nullptr;
410  rStream.SeekRel(length);
411  rStream.ReadUInt32(crc);
412  constexpr sal_uInt32 PNGCHUNK_IEND = 0x49454e44;
413  if (type == PNGCHUNK_IEND)
414  return nullptr;
415  }
416 }
417 
418 } // anonymous namespace
419 
420 namespace vcl
421 {
423  : mrStream(rStream)
424 {
425 }
426 
428 {
429  auto pBackendCapabilities = ImplGetSVData()->mpDefInst->GetBackendCapabilities();
430  bool bSupportsBitmap32 = pBackendCapabilities->mbSupportsBitmap32;
431 
432  return reader(mrStream, rBitmapEx, bSupportsBitmap32);
433 }
434 
436 {
437  BitmapEx bitmap;
438  read(bitmap);
439  return bitmap;
440 }
441 
442 std::unique_ptr<sal_uInt8[]> PngImageReader::getMicrosoftGifChunk(SvStream& rStream,
443  sal_Int32* chunkSize)
444 {
445  sal_uInt64 originalPosition = rStream.Tell();
446  SvStreamEndian originalEndian = rStream.GetEndian();
447  rStream.SetEndian(SvStreamEndian::BIG);
448  std::unique_ptr<sal_uInt8[]> chunk = getMsGifChunk(rStream, chunkSize);
449  rStream.SetEndian(originalEndian);
450  rStream.Seek(originalPosition);
451  return chunk;
452 }
453 
454 } // namespace vcl
455 
456 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt8 premultiply(sal_uInt8 c, sal_uInt8 a)
signed char sal_Int8
virtual sal_uInt64 TellEnd()
void SetPrefMapMode(const MapMode &rPrefMapMode)
Definition: bitmapex.hxx:90
sal_uInt64 Seek(sal_uInt64 nPos)
This template handles BitmapAccess the RAII way.
vcl::ScopedBitmapAccess< BitmapWriteAccess, Bitmap,&Bitmap::AcquireWriteAccess > BitmapScopedWriteAccess
sal_uInt64 SeekRel(sal_Int64 nPos)
ScanlineFormat
Definition: Scanline.hxx:29
size_t pos
length
static bool IsFuzzing()
sal_uInt64 remainingSize()
float y
SvStream & ReadUInt32(sal_uInt32 &rUInt32)
bool IsEmpty() const
Definition: BitmapEx.cxx:196
ImplSVData * ImplGetSVData()
Definition: svdata.cxx:74
#define PNGCHUNK_IEND
Definition: pngwrite.cxx:41
sal_uInt8 * Scanline
Definition: Scanline.hxx:26
bool IsEmpty() const
int i
static std::unique_ptr< sal_uInt8[]> getMicrosoftGifChunk(SvStream &rStream, sal_Int32 *chunkSize=nullptr)
virtual std::shared_ptr< vcl::BackendCapabilities > GetBackendCapabilities()
Definition: salinst.hxx:132
pass
std::size_t ReadBytes(void *pData, std::size_t nSize)
SvStreamEndian GetEndian() const
vcl::ScopedBitmapAccess< BitmapWriteAccess, AlphaMask,&AlphaMask::AcquireAlphaWriteAccess > AlphaScopedWriteAccess
unsigned char sal_uInt8
void SetEndian(SvStreamEndian SvStreamEndian)
void SetPrefSize(const Size &rPrefSize)
Definition: bitmapex.hxx:87
bool IsEmpty() const
Definition: bitmap.hxx:547
sal_uInt64 Tell() const
bool good() const
bool Erase(const Color &rFillColor)
Fill the entire bitmap with the given color.
Definition: bitmappaint.cxx:34
ResultType type
SvStreamEndian
PngImageReader(SvStream &rStream)
SalInstance * mpDefInst
Definition: svdata.hxx:383
static const BitmapPalette & GetGreyPalette(int nEntries)