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
28
29//============================ PCXReader ==================================
30
31namespace {
32
33class PCXReader {
34
35private:
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
59public:
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
69PCXReader::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
86bool 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
153void 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
213void 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
387void 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
402bool 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: */
FPDF_BITMAP mpBitmap
double ny
double nx
void SetError(ErrCode nErrorCode)
Intended to be used to feed into CreateFromData to create a BitmapEx.
Definition: RawBitmap.hxx:22
int nCount
#define SVSTREAM_FILEFORMAT_ERROR
Definition: errcode.hxx:260
sal_Int16 nVersion
bool ImportPcxGraphic(SvStream &rStream, Graphic &rGraphic)
Definition: ipcx.cxx:402
int i
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
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.
unsigned char sal_uInt8
#define SAL_MAX_INT32