LibreOffice Module vcl (master)  1
xbmread.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 <memory>
21 #include <sal/config.h>
22 #include <tools/stream.hxx>
23 
24 #include <rtl/character.hxx>
25 #include <bitmapwriteaccess.hxx>
27 
28 #include "xbmread.hxx"
29 
30 namespace {
31 
33 {
34  XBM10,
35  XBM11
36 };
37 
39 {
40  XBMREAD_OK,
41  XBMREAD_ERROR,
42  XBMREAD_NEED_MORE
43 };
44 
45 class XBMReader : public GraphicReader
46 {
47  SvStream& rIStm;
48  Bitmap aBmp1;
50  std::unique_ptr<short[]>
51  pHexTable;
52  BitmapColor aWhite;
53  BitmapColor aBlack;
54  long nLastPos;
55  long nWidth;
56  long nHeight;
57  bool bStatus;
58 
59  void InitTable();
60  OString FindTokenLine( SvStream* pInStm, const char* pTok1, const char* pTok2 );
61  int ParseDefine( const char* pDefine );
62  void ParseData( SvStream* pInStm, const OString& aLastLine, XBMFormat eFormat );
63 
64 public:
65 
66  explicit XBMReader( SvStream& rStm );
67 
68  ReadState ReadXBM( Graphic& rGraphic );
69 };
70 
71 }
72 
73 XBMReader::XBMReader( SvStream& rStm ) :
74  rIStm ( rStm ),
75  nLastPos ( rStm.Tell() ),
76  nWidth ( 0 ),
77  nHeight ( 0 ),
78  bStatus ( true )
79 {
80  pHexTable.reset( new short[ 256 ] );
81  maUpperName = "SVIXBM";
82  InitTable();
83 }
84 
85 void XBMReader::InitTable()
86 {
87  memset( pHexTable.get(), 0, sizeof( short ) * 256 );
88 
89  pHexTable[int('0')] = 0;
90  pHexTable[int('1')] = 1;
91  pHexTable[int('2')] = 2;
92  pHexTable[int('3')] = 3;
93  pHexTable[int('4')] = 4;
94  pHexTable[int('5')] = 5;
95  pHexTable[int('6')] = 6;
96  pHexTable[int('7')] = 7;
97  pHexTable[int('8')] = 8;
98  pHexTable[int('9')] = 9;
99  pHexTable[int('A')] = 10;
100  pHexTable[int('B')] = 11;
101  pHexTable[int('C')] = 12;
102  pHexTable[int('D')] = 13;
103  pHexTable[int('E')] = 14;
104  pHexTable[int('F')] = 15;
105  pHexTable[int('X')] = 0;
106  pHexTable[int('a')] = 10;
107  pHexTable[int('b')] = 11;
108  pHexTable[int('c')] = 12;
109  pHexTable[int('d')] = 13;
110  pHexTable[int('e')] = 14;
111  pHexTable[int('f')] = 15;
112  pHexTable[int('x')] = 0;
113  pHexTable[int(' ')] = -1;
114  pHexTable[int(',')] = -1;
115  pHexTable[int('}')] = -1;
116  pHexTable[int('\n')] = -1;
117  pHexTable[int('\t')] = -1;
118  pHexTable[int('\0')] = -1;
119 }
120 
121 OString XBMReader::FindTokenLine( SvStream* pInStm, const char* pTok1,
122  const char* pTok2 )
123 {
124  OString aRet;
125  sal_Int32 nPos1, nPos2;
126 
127  bStatus = false;
128 
129  do
130  {
131  if( !pInStm->ReadLine( aRet ) )
132  break;
133 
134  if( pTok1 )
135  {
136  if( ( nPos1 = aRet.indexOf( pTok1 ) ) != -1 )
137  {
138  bStatus = true;
139 
140  if( pTok2 )
141  {
142  bStatus = false;
143 
144  nPos2 = aRet.indexOf( pTok2 );
145  if( ( nPos2 != -1 ) && ( nPos2 > nPos1 ) )
146  {
147  bStatus = true;
148  }
149  }
150  }
151  }
152  }
153  while( !bStatus );
154 
155  return aRet;
156 }
157 
158 int XBMReader::ParseDefine( const char* pDefine )
159 {
160  sal_Int32 nRet = 0;
161  const char* pTmp = pDefine;
162  unsigned char cTmp;
163 
164  // move to end
165  pTmp += ( strlen( pDefine ) - 1 );
166  cTmp = *pTmp--;
167 
168  // search last digit
169  while (pHexTable[ cTmp ] == -1 && pTmp >= pDefine)
170  cTmp = *pTmp--;
171 
172  // move before number
173  while (pHexTable[ cTmp ] != -1 && pTmp >= pDefine)
174  cTmp = *pTmp--;
175 
176  // move to start of number
177  pTmp += 2;
178 
179  // read Hex
180  if( ( pTmp[0] == '0' ) && ( ( pTmp[1] == 'X' ) || ( pTmp[1] == 'x' ) ) )
181  {
182  pTmp += 2;
183  nRet = OString(pTmp, strlen(pTmp)).toInt32(16);
184  }
185  else // read decimal
186  {
187  nRet = OString(pTmp, strlen(pTmp)).toInt32();
188  }
189 
190  return nRet;
191 }
192 
193 void XBMReader::ParseData( SvStream* pInStm, const OString& aLastLine, XBMFormat eFormat )
194 {
195  OString aLine;
196  long nRow = 0;
197  long nCol = 0;
198  long nBits = ( eFormat == XBM10 ) ? 16 : 8;
199  long nBit;
200  sal_uInt16 nValue;
201  sal_uInt16 nDigits;
202  bool bFirstLine = true;
203 
204  while( nRow < nHeight )
205  {
206  if( bFirstLine )
207  {
208  sal_Int32 nPos;
209 
210  // delete opening curly bracket
211  aLine = aLastLine;
212  nPos = aLine.indexOf('{');
213  if( nPos != -1 )
214  aLine = aLine.copy(nPos + 1);
215 
216  bFirstLine = false;
217  }
218  else if( !pInStm->ReadLine( aLine ) )
219  break;
220 
221  if (!aLine.isEmpty())
222  {
223  sal_Int32 nIndex = 0;
224  const sal_Int32 nLen {aLine.getLength()};
225  while (nRow<nHeight && nIndex<nLen)
226  {
227  bool bProcessed = false;
228 
229  nBit = nDigits = nValue = 0;
230 
231  while (nIndex<nLen)
232  {
233  const unsigned char cChar = aLine[nIndex];
234 
235  ++nIndex;
236  if (cChar==',') // sequence completed, ',' already skipped for next loop
237  break;
238 
239  const short nTable = pHexTable[ cChar ];
240 
241  if( rtl::isAsciiHexDigit( cChar ) || !nTable )
242  {
243  nValue = ( nValue << 4 ) + nTable;
244  nDigits++;
245  bProcessed = true;
246  }
247  else if( ( nTable < 0 ) && nDigits )
248  {
249  bProcessed = true;
250  break;
251  }
252  }
253 
254  if( bProcessed )
255  {
256  Scanline pScanline = pAcc1->GetScanline(nRow);
257  while( ( nCol < nWidth ) && ( nBit < nBits ) )
258  pAcc1->SetPixelOnData(pScanline, nCol++, ( nValue & ( 1 << nBit++ ) ) ? aBlack : aWhite);
259 
260  if( nCol == nWidth )
261  {
262  nCol = 0;
263  nRow++;
264  }
265  }
266  }
267  }
268  }
269 }
270 
271 ReadState XBMReader::ReadXBM( Graphic& rGraphic )
272 {
273  ReadState eReadState;
274  sal_uInt8 cDummy;
275 
276  // check if we can read ALL
277  rIStm.Seek( STREAM_SEEK_TO_END );
278  rIStm.ReadUChar( cDummy );
279 
280  // if we cannot read all
281  // we return and wait for new data
282  if ( rIStm.GetError() != ERRCODE_IO_PENDING )
283  {
284  rIStm.Seek( nLastPos );
285  bStatus = false;
286  OString aLine = FindTokenLine( &rIStm, "#define", "_width" );
287 
288  if ( bStatus )
289  {
290  int nValue;
291  if ( ( nValue = ParseDefine( aLine.getStr() ) ) > 0 )
292  {
293  nWidth = nValue;
294  aLine = FindTokenLine( &rIStm, "#define", "_height" );
295 
296  // if height was not received, we search again
297  // from start of the file
298  if ( !bStatus )
299  {
300  rIStm.Seek( nLastPos );
301  aLine = FindTokenLine( &rIStm, "#define", "_height" );
302  }
303  }
304  else
305  bStatus = false;
306 
307  if ( bStatus )
308  {
309  if ( ( nValue = ParseDefine( aLine.getStr() ) ) > 0 )
310  {
311  nHeight = nValue;
312  aLine = FindTokenLine( &rIStm, "static", "_bits" );
313 
314  if ( bStatus )
315  {
316  XBMFormat eFormat = XBM10;
317 
318  if (aLine.indexOf("short") != -1)
319  eFormat = XBM10;
320  else if (aLine.indexOf("char") != -1)
321  eFormat = XBM11;
322  else
323  bStatus = false;
324 
325  //xbms are a minimum of one character per 8 pixels, so if the file isn't
326  //even that long, it's not all there
327  if (rIStm.remainingSize() < (static_cast<sal_uInt64>(nWidth) * nHeight) / 8)
328  bStatus = false;
329 
330  if ( bStatus && nWidth && nHeight )
331  {
332  aBmp1 = Bitmap( Size( nWidth, nHeight ), 1 );
333  pAcc1 = BitmapScopedWriteAccess(aBmp1);
334 
335  if( pAcc1 )
336  {
337  aWhite = pAcc1->GetBestMatchingColor( COL_WHITE );
338  aBlack = pAcc1->GetBestMatchingColor( COL_BLACK );
339  ParseData( &rIStm, aLine, eFormat );
340  }
341  else
342  bStatus = false;
343  }
344  }
345  }
346  }
347  }
348 
349  if (bStatus && pAcc1)
350  {
351  Bitmap aBlackBmp( Size( pAcc1->Width(), pAcc1->Height() ), 1 );
352 
353  pAcc1.reset();
354  aBlackBmp.Erase( COL_BLACK );
355  rGraphic = BitmapEx( aBlackBmp, aBmp1 );
356  eReadState = XBMREAD_OK;
357  }
358  else
359  eReadState = XBMREAD_ERROR;
360  }
361  else
362  {
363  rIStm.ResetError();
364  eReadState = XBMREAD_NEED_MORE;
365  }
366 
367  return eReadState;
368 }
369 
370 VCL_DLLPUBLIC bool ImportXBM( SvStream& rStm, Graphic& rGraphic )
371 {
372  std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
373  rGraphic.SetReaderContext(nullptr);
374  XBMReader* pXBMReader = dynamic_cast<XBMReader*>( pContext.get() );
375  if (!pXBMReader)
376  {
377  pContext = std::make_shared<XBMReader>( rStm );
378  pXBMReader = static_cast<XBMReader*>( pContext.get() );
379  }
380 
381  bool bRet = true;
382 
383  ReadState eReadState = pXBMReader->ReadXBM( rGraphic );
384 
385  if( eReadState == XBMREAD_ERROR )
386  {
387  bRet = false;
388  }
389  else if( eReadState == XBMREAD_NEED_MORE )
390  rGraphic.SetReaderContext( pContext );
391 
392  return bRet;
393 }
394 
395 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 nIndex
#define VCL_DLLPUBLIC
Definition: dllapi.h:29
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
vcl::ScopedBitmapAccess< BitmapWriteAccess, Bitmap,&Bitmap::AcquireWriteAccess > BitmapScopedWriteAccess
ReadState
Definition: gifread.cxx:44
This template handles BitmapAccess the RAII way.
void SetReaderContext(const std::shared_ptr< GraphicReader > &pReader)
Definition: graph.cxx:477
bool ReadLine(OString &rStr, sal_Int32 nMaxBytesToRead=0xFFFE)
XBMFormat
Definition: xbmread.cxx:32
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
VCL_DLLPUBLIC bool ImportXBM(SvStream &rStm, Graphic &rGraphic)
Definition: xbmread.cxx:370
unsigned char sal_uInt8
std::shared_ptr< GraphicReader > & GetReaderContext()
Definition: graph.cxx:472
#define ERRCODE_IO_PENDING
Definition: errcode.hxx:227
sal_uInt16 nPos
sal_Int16 nValue