LibreOffice Module vcl (master)  1
ipcx.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 
21 #include <memory>
22 #include <vcl/graph.hxx>
23 #include <vcl/BitmapTools.hxx>
24 #include <tools/stream.hxx>
25 #include <filter/PcxReader.hxx>
26 
27 class FilterConfigItem;
28 
29 //============================ PCXReader ==================================
30 
31 namespace {
32 
33 class PCXReader {
34 
35 private:
36 
37  SvStream& m_rPCX; // the PCX file to read
38 
39  std::unique_ptr<vcl::bitmap::RawBitmap> mpBitmap;
40  std::vector<Color> mvPalette;
41  sal_uInt8 nVersion; // PCX-Version
42  sal_uInt8 nEncoding; // compression type
43  sal_uInt16 nBitsPerPlanePix; // bits per plane per pixel
44  sal_uInt16 nPlanes; // no of planes
45  sal_uInt16 nBytesPerPlaneLin; // bytes per plane line
46 
47  sal_uInt32 nWidth, nHeight; // dimension in pixel
48  sal_uInt16 nResX, nResY; // resolution in pixel per inch or 0,0
49  sal_uInt16 nDestBitsPerPixel; // bits per pixel in destination bitmap 1,4,8 or 24
50  std::unique_ptr<sal_uInt8[]>
51  pPalette;
52  bool bStatus; // from now on do not read status from stream ( SJ )
53 
54 
55  void ImplReadBody();
56  void ImplReadPalette( unsigned int nCol );
57  void ImplReadHeader();
58 
59 public:
60  explicit PCXReader(SvStream &rStream);
61  bool ReadPCX(Graphic & rGraphic );
62  // Reads a PCX file from the stream and fills the GDIMetaFile
63 };
64 
65 }
66 
67 //=================== methods of PCXReader ==============================
68 
69 PCXReader::PCXReader(SvStream &rStream)
70  : m_rPCX(rStream)
71  , nVersion(0)
72  , nEncoding(0)
73  , nBitsPerPlanePix(0)
74  , nPlanes(0)
75  , nBytesPerPlaneLin(0)
76  , nWidth(0)
77  , nHeight(0)
78  , nResX(0)
79  , nResY(0)
80  , nDestBitsPerPixel(0)
81  , pPalette(new sal_uInt8[ 768 ])
82  , bStatus(false)
83 {
84 }
85 
86 bool PCXReader::ReadPCX(Graphic & rGraphic)
87 {
88  if ( m_rPCX.GetError() )
89  return false;
90 
91  m_rPCX.SetEndian(SvStreamEndian::LITTLE);
92 
93  // read header:
94 
95  bStatus = true;
96 
97  ImplReadHeader();
98 
99  // sanity check there is enough data before trying allocation
100  if (bStatus && nBytesPerPlaneLin > m_rPCX.remainingSize() / nPlanes)
101  {
102  bStatus = false;
103  }
104 
105  if (bStatus)
106  {
107  sal_uInt32 nResult;
108  bStatus = !o3tl::checked_multiply(nWidth, nHeight, nResult) && nResult <= SAL_MAX_INT32/2/3;
109  }
110 
111  // Write BMP header and conditionally (maybe invalid for now) color palette:
112  if (bStatus)
113  {
114  mpBitmap.reset( new vcl::bitmap::RawBitmap( Size( nWidth, nHeight ), 24 ) );
115 
116  if ( nDestBitsPerPixel <= 8 )
117  {
118  sal_uInt16 nColors = 1 << nDestBitsPerPixel;
119  sal_uInt8* pPal = pPalette.get();
120  mvPalette.resize( nColors );
121  for ( sal_uInt16 i = 0; i < nColors; i++, pPal += 3 )
122  {
123  mvPalette[i] = Color( pPal[ 0 ], pPal[ 1 ], pPal[ 2 ] );
124  }
125  }
126 
127  // read bitmap data
128  ImplReadBody();
129 
130  // If an extended color palette exists at the end of the file, then read it and
131  // and write again in palette:
132  if ( nDestBitsPerPixel == 8 && bStatus )
133  {
134  sal_uInt8* pPal = pPalette.get();
135  m_rPCX.SeekRel(1);
136  ImplReadPalette(256);
137  mvPalette.resize( 256 );
138  for ( sal_uInt16 i = 0; i < 256; i++, pPal += 3 )
139  {
140  mvPalette[i] = Color( pPal[ 0 ], pPal[ 1 ], pPal[ 2 ] );
141  }
142  }
143 
144  if ( bStatus )
145  {
146  rGraphic = vcl::bitmap::CreateFromData(std::move(*mpBitmap));
147  return true;
148  }
149  }
150  return false;
151 }
152 
153 void PCXReader::ImplReadHeader()
154 {
155  sal_uInt8 nbyte(0);
156  m_rPCX.ReadUChar( nbyte ).ReadUChar( nVersion ).ReadUChar( nEncoding );
157  if ( nbyte!=0x0a || (nVersion != 0 && nVersion != 2 && nVersion != 3 && nVersion != 5) || nEncoding > 1 )
158  {
159  bStatus = false;
160  return;
161  }
162 
163  nbyte = 0;
164  m_rPCX.ReadUChar( nbyte ); nBitsPerPlanePix = static_cast<sal_uInt16>(nbyte);
165  sal_uInt16 nMinX(0),nMinY(0),nMaxX(0),nMaxY(0);
166  m_rPCX.ReadUInt16( nMinX ).ReadUInt16( nMinY ).ReadUInt16( nMaxX ).ReadUInt16( nMaxY );
167 
168  if ((nMinX > nMaxX) || (nMinY > nMaxY))
169  {
170  bStatus = false;
171  return;
172  }
173 
174  nWidth = nMaxX-nMinX+1;
175  nHeight = nMaxY-nMinY+1;
176 
177  m_rPCX.ReadUInt16( nResX );
178  m_rPCX.ReadUInt16( nResY );
179  if ( nResX >= nWidth || nResY >= nHeight || ( nResX != nResY ) )
180  nResX = nResY = 0;
181 
182  ImplReadPalette( 16 );
183 
184  m_rPCX.SeekRel( 1 );
185  nbyte = 0;
186  m_rPCX.ReadUChar( nbyte ); nPlanes = static_cast<sal_uInt16>(nbyte);
187  sal_uInt16 nushort(0);
188  m_rPCX.ReadUInt16( nushort ); nBytesPerPlaneLin = nushort;
189  sal_uInt16 nPaletteInfo;
190  m_rPCX.ReadUInt16( nPaletteInfo );
191 
192  m_rPCX.SeekRel( 58 );
193 
194  nDestBitsPerPixel = nBitsPerPlanePix * nPlanes;
195  if (nDestBitsPerPixel == 2 || nDestBitsPerPixel == 3) nDestBitsPerPixel = 4;
196 
197  if ( ( nDestBitsPerPixel != 1 && nDestBitsPerPixel != 4 && nDestBitsPerPixel != 8 && nDestBitsPerPixel != 24 )
198  || nPlanes > 4 || nBytesPerPlaneLin < ( ( nWidth * nBitsPerPlanePix+7 ) >> 3 ) )
199  {
200  bStatus = false;
201  return;
202  }
203 
204  // If the bitmap has only 2 colors, the palette is most often invalid and it is always(?)
205  // a black and white image:
206  if ( nPlanes == 1 && nBitsPerPlanePix == 1 )
207  {
208  pPalette[ 0 ] = pPalette[ 1 ] = pPalette[ 2 ] = 0x00;
209  pPalette[ 3 ] = pPalette[ 4 ] = pPalette[ 5 ] = 0xff;
210  }
211 }
212 
213 void PCXReader::ImplReadBody()
214 {
215  std::unique_ptr<sal_uInt8[]> pPlane[ 4 ];
216  sal_uInt8 * pDest;
217  sal_uInt32 i, ny, nLastPercent = 0, nPercent;
218  sal_uInt16 nCount, nx;
219  sal_uInt8 nDat = 0, nCol = 0;
220 
221  for (sal_uInt16 np = 0; np < nPlanes; ++np)
222  pPlane[np].reset(new sal_uInt8[nBytesPerPlaneLin]());
223 
224  nCount = 0;
225  for ( ny = 0; ny < nHeight; ny++ )
226  {
227  if (!m_rPCX.good())
228  {
229  bStatus = false;
230  break;
231  }
232  nPercent = ny * 60 / nHeight + 10;
233  if ( ny == 0 || nLastPercent + 4 <= nPercent )
234  {
235  nLastPercent = nPercent;
236  }
237  for (sal_uInt16 np = 0; np < nPlanes; ++np)
238  {
239  if ( nEncoding == 0)
240  m_rPCX.ReadBytes( static_cast<void *>(pPlane[ np ].get()), nBytesPerPlaneLin );
241  else
242  {
243  pDest = pPlane[ np ].get();
244  nx = nBytesPerPlaneLin;
245  while ( nCount > 0 && nx > 0)
246  {
247  *(pDest++) = nDat;
248  nx--;
249  nCount--;
250  }
251  while (nx > 0 && m_rPCX.good())
252  {
253  m_rPCX.ReadUChar( nDat );
254  if ( ( nDat & 0xc0 ) == 0xc0 )
255  {
256  nCount =static_cast<sal_uInt64>(nDat) & 0x003f;
257  m_rPCX.ReadUChar( nDat );
258  if ( nCount < nx )
259  {
260  nx -= nCount;
261  while ( nCount > 0)
262  {
263  *(pDest++) = nDat;
264  nCount--;
265  }
266  }
267  else
268  {
269  nCount -= nx;
270  do
271  {
272  *(pDest++) = nDat;
273  nx--;
274  }
275  while ( nx > 0 );
276  break;
277  }
278  }
279  else
280  {
281  *(pDest++) = nDat;
282  nx--;
283  }
284  }
285  }
286  }
287  sal_uInt8 *pSource1 = pPlane[ 0 ].get();
288  sal_uInt8 *pSource2 = pPlane[ 1 ].get();
289  sal_uInt8 *pSource3 = pPlane[ 2 ].get();
290  sal_uInt8 *pSource4 = pPlane[ 3 ].get();
291  switch ( nBitsPerPlanePix + ( nPlanes << 8 ) )
292  {
293  // 2 colors
294  case 0x101 :
295  for ( i = 0; i < nWidth; i++ )
296  {
297  sal_uInt32 nShift = ( i & 7 ) ^ 7;
298  if ( nShift == 0 )
299  mpBitmap->SetPixel( ny, i, mvPalette[*(pSource1++) & 1] );
300  else
301  mpBitmap->SetPixel( ny, i, mvPalette[(*pSource1 >> nShift ) & 1] );
302  }
303  break;
304  // 4 colors
305  case 0x102 :
306  for ( i = 0; i < nWidth; i++ )
307  {
308  switch( i & 3 )
309  {
310  case 0 :
311  nCol = *pSource1 >> 6;
312  break;
313  case 1 :
314  nCol = ( *pSource1 >> 4 ) & 0x03 ;
315  break;
316  case 2 :
317  nCol = ( *pSource1 >> 2 ) & 0x03;
318  break;
319  case 3 :
320  nCol = ( *pSource1++ ) & 0x03;
321  break;
322  }
323  mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
324  }
325  break;
326  // 256 colors
327  case 0x108 :
328  for ( i = 0; i < nWidth; i++ )
329  {
330  mpBitmap->SetPixel( ny, i, mvPalette[*pSource1++] );
331  }
332  break;
333  // 8 colors
334  case 0x301 :
335  for ( i = 0; i < nWidth; i++ )
336  {
337  sal_uInt32 nShift = ( i & 7 ) ^ 7;
338  if ( nShift == 0 )
339  {
340  nCol = ( *pSource1++ & 1) + ( ( *pSource2++ << 1 ) & 2 ) + ( ( *pSource3++ << 2 ) & 4 );
341  mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
342  }
343  else
344  {
345  nCol = sal::static_int_cast< sal_uInt8 >(
346  ( ( *pSource1 >> nShift ) & 1) + ( ( ( *pSource2 >> nShift ) << 1 ) & 2 ) +
347  ( ( ( *pSource3 >> nShift ) << 2 ) & 4 ));
348  mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
349  }
350  }
351  break;
352  // 16 colors
353  case 0x401 :
354  for ( i = 0; i < nWidth; i++ )
355  {
356  sal_uInt32 nShift = ( i & 7 ) ^ 7;
357  if ( nShift == 0 )
358  {
359  nCol = ( *pSource1++ & 1) + ( ( *pSource2++ << 1 ) & 2 ) + ( ( *pSource3++ << 2 ) & 4 ) +
360  ( ( *pSource4++ << 3 ) & 8 );
361  mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
362  }
363  else
364  {
365  nCol = sal::static_int_cast< sal_uInt8 >(
366  ( ( *pSource1 >> nShift ) & 1) + ( ( ( *pSource2 >> nShift ) << 1 ) & 2 ) +
367  ( ( ( *pSource3 >> nShift ) << 2 ) & 4 ) + ( ( ( *pSource4 >> nShift ) << 3 ) & 8 ));
368  mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
369  }
370  }
371  break;
372  // 16m colors
373  case 0x308 :
374  for ( i = 0; i < nWidth; i++ )
375  {
376  mpBitmap->SetPixel( ny, i, Color( *pSource1++, *pSource2++, *pSource3++ ) );
377 
378  }
379  break;
380  default :
381  bStatus = false;
382  break;
383  }
384  }
385 }
386 
387 void PCXReader::ImplReadPalette( unsigned int nCol )
388 {
389  sal_uInt8 r, g, b;
390  sal_uInt8* pPtr = pPalette.get();
391  for ( unsigned int i = 0; i < nCol; i++ )
392  {
393  m_rPCX.ReadUChar( r ).ReadUChar( g ).ReadUChar( b );
394  *pPtr++ = r;
395  *pPtr++ = g;
396  *pPtr++ = b;
397  }
398 }
399 
400 //================== GraphicImport - the exported function ================
401 
402 bool ImportPcxGraphic(SvStream & rStream, Graphic & rGraphic)
403 {
404  PCXReader aPCXReader(rStream);
405  bool bRetValue = aPCXReader.ReadPCX(rGraphic);
406  if ( !bRetValue )
408  return bRetValue;
409 }
410 
411 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double ny
FPDF_BITMAP mpBitmap
int nCount
#define SAL_MAX_INT32
int i
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
void SetError(ErrCode nErrorCode)
sal_Int16 nVersion
Intended to be used to feed into CreateFromData to create a BitmapEx.
Definition: RawBitmap.hxx:21
double nx
unsigned char sal_uInt8
#define SVSTREAM_FILEFORMAT_ERROR
Definition: errcode.hxx:260
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 ImportPcxGraphic(SvStream &rStream, Graphic &rGraphic)
Definition: ipcx.cxx:402