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>
23#include <vcl/graph.hxx>
24#include <vcl/BitmapTools.hxx>
25#include <tools/stream.hxx>
26#include <filter/PbmReader.hxx>
27
28//============================ PBMReader ==================================
29
30namespace {
31
32class PBMReader {
33
34private:
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
50public:
51 explicit PBMReader(SvStream & rPBM);
52 bool ReadPBM(Graphic & rGraphic );
53};
54
55}
56
57//=================== Methods of PBMReader ==============================
58
59PBMReader::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
72bool 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
140bool 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
262bool 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
534bool 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: */
Intended to be used to feed into CreateFromData to create a BitmapEx.
Definition: RawBitmap.hxx:22
sal_Int16 mnMode
int nCount
sal_Int32 mnCol
bool ImportPbmGraphic(SvStream &rStream, Graphic &rGraphic)
Definition: ipbm.cxx:534
int i
BitmapEx CreateFromData(sal_uInt8 const *pData, sal_Int32 nWidth, sal_Int32 nHeight, sal_Int32 nStride, sal_Int8 nBitCount, bool bReversColors, bool bReverseAlpha)
Copy block of image data into the bitmap.
double mnWidth
double mnHeight
unsigned char sal_uInt8