LibreOffice Module vcl (master)  1
ipbm.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 <sal/config.h>
21 #include <o3tl/safeint.hxx>
22 #include <vcl/FilterConfigItem.hxx>
23 #include <vcl/graph.hxx>
24 #include <vcl/BitmapTools.hxx>
25 #include <tools/stream.hxx>
26 #include <filter/PbmReader.hxx>
27 
28 //============================ PBMReader ==================================
29 
30 namespace {
31 
32 class PBMReader {
33 
34 private:
35 
36  SvStream& mrPBM; // the PBM file to read
37 
38  bool mbStatus;
39  bool mbRemark; // sal_False if the stream is in a comment
40  bool mbRaw; // RAW/ASCII MODE
41  sal_uInt8 mnMode; // 0->PBM, 1->PGM, 2->PPM
42  std::unique_ptr<vcl::bitmap::RawBitmap> mpRawBmp;
43  std::vector<Color> mvPalette;
44  sal_Int32 mnWidth, mnHeight; // dimensions in pixel
45  sal_uInt16 mnCol;
46  sal_uInt64 mnMaxVal; // max value in the <missing comment>
47  bool ImplReadBody();
48  bool ImplReadHeader();
49 
50 public:
51  explicit PBMReader(SvStream & rPBM);
52  bool ReadPBM(Graphic & rGraphic );
53 };
54 
55 }
56 
57 //=================== Methods of PBMReader ==============================
58 
59 PBMReader::PBMReader(SvStream & rPBM)
60  : mrPBM(rPBM)
61  , mbStatus(true)
62  , mbRemark(false)
63  , mbRaw(true)
64  , mnMode(0)
65  , mnWidth(0)
66  , mnHeight(0)
67  , mnCol(0)
68  , mnMaxVal(0)
69 {
70 }
71 
72 bool PBMReader::ReadPBM(Graphic & rGraphic )
73 {
74  if ( mrPBM.GetError() )
75  return false;
76 
77  mrPBM.SetEndian( SvStreamEndian::LITTLE );
78 
79  // read header:
80 
81  mbStatus = ImplReadHeader();
82  if ( !mbStatus )
83  return false;
84 
85  if ( ( mnMaxVal == 0 ) || ( mnWidth <= 0 ) || ( mnHeight <= 0 ) )
86  return false;
87 
88  sal_uInt32 nPixelsRequired;
89  if (o3tl::checked_multiply<sal_uInt32>(mnWidth, mnHeight, nPixelsRequired))
90  return false;
91  const auto nRemainingSize = mrPBM.remainingSize();
92 
93  // 0->PBM, 1->PGM, 2->PPM
94  switch ( mnMode )
95  {
96  case 0:
97  {
98  if (nRemainingSize < nPixelsRequired / 8)
99  return false;
100 
101  mpRawBmp.reset( new vcl::bitmap::RawBitmap( Size( mnWidth, mnHeight ), 24 ) );
102  mvPalette.resize( 2 );
103  mvPalette[0] = Color( 0xff, 0xff, 0xff );
104  mvPalette[1] = Color( 0x00, 0x00, 0x00 );
105  break;
106  }
107  case 1 :
108  if (nRemainingSize < nPixelsRequired)
109  return false;
110 
111  mpRawBmp.reset( new vcl::bitmap::RawBitmap( Size( mnWidth, mnHeight ), 24 ) );
112  mnCol = static_cast<sal_uInt16>(mnMaxVal) + 1;
113  if ( mnCol > 256 )
114  mnCol = 256;
115 
116  mvPalette.resize( 256 );
117  for ( sal_uInt16 i = 0; i < mnCol; i++ )
118  {
119  sal_uInt16 nCount = 255 * i / mnCol;
120  mvPalette[i] = Color( static_cast<sal_uInt8>(nCount), static_cast<sal_uInt8>(nCount), static_cast<sal_uInt8>(nCount) );
121  }
122  break;
123  case 2 :
124  if (nRemainingSize / 3 < nPixelsRequired)
125  return false;
126 
127  mpRawBmp.reset( new vcl::bitmap::RawBitmap( Size( mnWidth, mnHeight ), 24 ) );
128  break;
129  }
130 
131  // read bitmap data
132  mbStatus = ImplReadBody();
133 
134  if ( mbStatus )
135  rGraphic = vcl::bitmap::CreateFromData(std::move(*mpRawBmp));
136 
137  return mbStatus;
138 }
139 
140 bool PBMReader::ImplReadHeader()
141 {
142  sal_uInt8 nID[ 2 ];
143  sal_uInt8 nDat;
144  sal_uInt8 nMax, nCount = 0;
145  bool bFinished = false;
146 
147  mrPBM.ReadUChar( nID[ 0 ] ).ReadUChar( nID[ 1 ] );
148  if (!mrPBM.good() || nID[0] != 'P')
149  return false;
150  mnMaxVal = mnWidth = mnHeight = 0;
151  switch ( nID[ 1 ] )
152  {
153  case '1' :
154  mbRaw = false;
155  [[fallthrough]];
156  case '4' :
157  mnMode = 0;
158  nMax = 2; // number of parameters in Header
159  mnMaxVal = 1;
160  break;
161  case '2' :
162  mbRaw = false;
163  [[fallthrough]];
164  case '5' :
165  mnMode = 1;
166  nMax = 3;
167  break;
168  case '3' :
169  mbRaw = false;
170  [[fallthrough]];
171  case '6' :
172  mnMode = 2;
173  nMax = 3;
174  break;
175  default:
176  return false;
177  }
178  while ( !bFinished )
179  {
180  mrPBM.ReadUChar( nDat );
181 
182  if (!mrPBM.good())
183  return false;
184 
185  if ( nDat == '#' )
186  {
187  mbRemark = true;
188  continue;
189  }
190  else if ( ( nDat == 0x0d ) || ( nDat == 0x0a ) )
191  {
192  mbRemark = false;
193  nDat = 0x20;
194  }
195  if ( mbRemark )
196  continue;
197 
198  if ( ( nDat == 0x20 ) || ( nDat == 0x09 ) )
199  {
200  if ( ( nCount == 0 ) && mnWidth )
201  nCount++;
202  else if ( ( nCount == 1 ) && mnHeight )
203  {
204  if ( ++nCount == nMax )
205  bFinished = true;
206  }
207  else if ( ( nCount == 2 ) && mnMaxVal )
208  {
209  bFinished = true;
210  }
211  continue;
212  }
213  if ( ( nDat >= '0' ) && ( nDat <= '9' ) )
214  {
215  nDat -= '0';
216  if ( nCount == 0 )
217  {
218  if (mnWidth > SAL_MAX_INT32 / 10)
219  {
220  return false;
221  }
222  mnWidth *= 10;
223  if (nDat > SAL_MAX_INT32 - mnWidth)
224  {
225  return false;
226  }
227  mnWidth += nDat;
228  }
229  else if ( nCount == 1 )
230  {
231  if (mnHeight > SAL_MAX_INT32 / 10)
232  {
233  return false;
234  }
235  mnHeight *= 10;
236  if (nDat > SAL_MAX_INT32 - mnHeight)
237  {
238  return false;
239  }
240  mnHeight += nDat;
241  }
242  else if ( nCount == 2 )
243  {
244  if (mnMaxVal > std::numeric_limits<sal_uInt64>::max() / 10)
245  {
246  return false;
247  }
248  mnMaxVal *= 10;
249  if (nDat > std::numeric_limits<sal_uInt64>::max() - mnMaxVal)
250  {
251  return false;
252  }
253  mnMaxVal += nDat;
254  }
255  }
256  else
257  return false;
258  }
259  return mbStatus;
260 }
261 
262 bool PBMReader::ImplReadBody()
263 {
264  sal_uInt8 nDat = 0, nCount;
265  sal_uInt64 nGrey, nRGB[3];
266  sal_Int32 nWidth = 0;
267  sal_Int32 nHeight = 0;
268 
269  if ( mbRaw )
270  {
271  signed char nShift = 0;
272  switch ( mnMode )
273  {
274 
275  // PBM
276  case 0 :
277  while ( nHeight != mnHeight )
278  {
279  if (!mrPBM.good())
280  return false;
281 
282  if ( --nShift < 0 )
283  {
284  mrPBM.ReadUChar( nDat );
285  nShift = 7;
286  }
287  mpRawBmp->SetPixel( nHeight, nWidth, mvPalette[(nDat >> nShift) & 0x01] );
288  if ( ++nWidth == mnWidth )
289  {
290  nShift = 0;
291  nWidth = 0;
292  nHeight++;
293  }
294  }
295  break;
296 
297  // PGM
298  case 1 :
299  while ( nHeight != mnHeight )
300  {
301  if (!mrPBM.good())
302  return false;
303 
304  mrPBM.ReadUChar( nDat );
305  mpRawBmp->SetPixel( nHeight, nWidth++, mvPalette[nDat]);
306 
307  if ( nWidth == mnWidth )
308  {
309  nWidth = 0;
310  nHeight++;
311  }
312  }
313  break;
314 
315  // PPM
316  case 2 :
317  while ( nHeight != mnHeight )
318  {
319  if (!mrPBM.good())
320  return false;
321 
322  sal_uInt8 nR, nG, nB;
323  sal_uInt8 nRed, nGreen, nBlue;
324  mrPBM.ReadUChar( nR ).ReadUChar( nG ).ReadUChar( nB );
325  nRed = 255 * nR / mnMaxVal;
326  nGreen = 255 * nG / mnMaxVal;
327  nBlue = 255 * nB / mnMaxVal;
328  mpRawBmp->SetPixel( nHeight, nWidth++, Color( nRed, nGreen, nBlue ) );
329  if ( nWidth == mnWidth )
330  {
331  nWidth = 0;
332  nHeight++;
333  }
334  }
335  break;
336  }
337  }
338  else
339  {
340  bool bPara = false;
341  bool bFinished = false;
342 
343  switch ( mnMode )
344  {
345  // PBM
346  case 0 :
347  while ( !bFinished )
348  {
349  if (!mrPBM.good())
350  return false;
351 
352  mrPBM.ReadUChar( nDat );
353 
354  if ( nDat == '#' )
355  {
356  mbRemark = true;
357  continue;
358  }
359  else if ( ( nDat == 0x0d ) || ( nDat == 0x0a ) )
360  {
361  mbRemark = false;
362  continue;
363  }
364  if ( mbRemark || nDat == 0x20 || nDat == 0x09 )
365  continue;
366 
367  if ( nDat == '0' || nDat == '1' )
368  {
369  mpRawBmp->SetPixel( nHeight, nWidth, mvPalette[static_cast<sal_uInt8>(nDat - '0')] );
370  nWidth++;
371  if ( nWidth == mnWidth )
372  {
373  nWidth = 0;
374  if ( ++nHeight == mnHeight )
375  bFinished = true;
376  }
377  }
378  else
379  return false;
380  }
381  break;
382 
383  // PGM
384  case 1 :
385 
386  bPara = false;
387  nCount = 0;
388  nGrey = 0;
389 
390  while ( !bFinished )
391  {
392  if ( nCount )
393  {
394  nCount--;
395  if ( nGrey <= mnMaxVal )
396  nGrey = 255 * nGrey / mnMaxVal;
397  mpRawBmp->SetPixel( nHeight, nWidth++, mvPalette[static_cast<sal_uInt8>(nGrey)] );
398  nGrey = 0;
399  if ( nWidth == mnWidth )
400  {
401  nWidth = 0;
402  if ( ++nHeight == mnHeight )
403  bFinished = true;
404  }
405  continue;
406  }
407 
408  if (!mrPBM.good())
409  return false;
410 
411  mrPBM.ReadUChar( nDat );
412 
413  if ( nDat == '#' )
414  {
415  mbRemark = true;
416  if ( bPara )
417  {
418  bPara = false;
419  nCount++;
420  }
421  continue;
422  }
423  else if ( ( nDat == 0x0d ) || ( nDat == 0x0a ) )
424  {
425  mbRemark = false;
426  if ( bPara )
427  {
428  bPara = false;
429  nCount++;
430  }
431  continue;
432  }
433 
434  if ( nDat == 0x20 || nDat == 0x09 )
435  {
436  if ( bPara )
437  {
438  bPara = false;
439  nCount++;
440  }
441  continue;
442  }
443  if ( nDat >= '0' && nDat <= '9' )
444  {
445  bPara = true;
446  nGrey *= 10;
447  nGrey += nDat-'0';
448  continue;
449  }
450  else
451  return false;
452  }
453  break;
454 
455 
456  // PPM
457  case 2 :
458 
459  bPara = false;
460  nCount = 0;
461  nRGB[ 0 ] = nRGB[ 1 ] = nRGB[ 2 ] = 0;
462 
463  while ( !bFinished )
464  {
465  if ( nCount == 3 )
466  {
467  nCount = 0;
468  mpRawBmp->SetPixel( nHeight, nWidth++, Color( static_cast< sal_uInt8 >( ( nRGB[ 0 ] * 255 ) / mnMaxVal ),
469  static_cast< sal_uInt8 >( ( nRGB[ 1 ] * 255 ) / mnMaxVal ),
470  static_cast< sal_uInt8 >( ( nRGB[ 2 ] * 255 ) / mnMaxVal ) ) );
471  nRGB[ 0 ] = nRGB[ 1 ] = nRGB[ 2 ] = 0;
472  if ( nWidth == mnWidth )
473  {
474  nWidth = 0;
475  if ( ++nHeight == mnHeight )
476  bFinished = true;
477  }
478  continue;
479  }
480 
481  if (!mrPBM.good())
482  return false;
483 
484  mrPBM.ReadUChar( nDat );
485 
486  if ( nDat == '#' )
487  {
488  mbRemark = true;
489  if ( bPara )
490  {
491  bPara = false;
492  nCount++;
493  }
494  continue;
495  }
496  else if ( ( nDat == 0x0d ) || ( nDat == 0x0a ) )
497  {
498  mbRemark = false;
499  if ( bPara )
500  {
501  bPara = false;
502  nCount++;
503  }
504  continue;
505  }
506 
507  if ( nDat == 0x20 || nDat == 0x09 )
508  {
509  if ( bPara )
510  {
511  bPara = false;
512  nCount++;
513  }
514  continue;
515  }
516  if ( nDat >= '0' && nDat <= '9' )
517  {
518  bPara = true;
519  nRGB[ nCount ] *= 10;
520  nRGB[ nCount ] += nDat-'0';
521  continue;
522  }
523  else
524  return false;
525  }
526  break;
527  }
528  }
529  return mbStatus;
530 }
531 
532 //================== GraphicImport - the exported function ================
533 
534 bool ImportPbmGraphic( SvStream & rStream, Graphic & rGraphic)
535 {
536  PBMReader aPBMReader(rStream);
537 
538  return aPBMReader.ReadPBM(rGraphic );
539 }
540 
541 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double mnHeight
sal_Int32 mnCol
int nCount
int i
Intended to be used to feed into CreateFromData to create a BitmapEx.
Definition: RawBitmap.hxx:21
unsigned char sal_uInt8
double mnWidth
BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight, sal_Int32 nStride, vcl::PixelFormat ePixelFormat, bool bReversColors, bool bReverseAlpha)
Copy block of image data into the bitmap.
bool ImportPbmGraphic(SvStream &rStream, Graphic &rGraphic)
Definition: ipbm.cxx:534
sal_Int16 mnMode