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  if( ( ( nPos2 = aRet.indexOf( pTok2 ) ) != -1 ) &&
145  ( 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  if( (nPos = ( aLine = aLastLine ).indexOf('{') ) != -1 )
212  aLine = aLine.copy(nPos + 1);
213 
214  bFirstLine = false;
215  }
216  else if( !pInStm->ReadLine( aLine ) )
217  break;
218 
219  if (!aLine.isEmpty())
220  {
221  sal_Int32 nIndex = 0;
222  const sal_Int32 nLen {aLine.getLength()};
223  while (nRow<nHeight && nIndex<nLen)
224  {
225  bool bProcessed = false;
226 
227  nBit = nDigits = nValue = 0;
228 
229  while (nIndex<nLen)
230  {
231  const unsigned char cChar = aLine[nIndex];
232 
233  ++nIndex;
234  if (cChar==',') // sequence completed, ',' already skipped for next loop
235  break;
236 
237  const short nTable = pHexTable[ cChar ];
238 
239  if( rtl::isAsciiHexDigit( cChar ) || !nTable )
240  {
241  nValue = ( nValue << 4 ) + nTable;
242  nDigits++;
243  bProcessed = true;
244  }
245  else if( ( nTable < 0 ) && nDigits )
246  {
247  bProcessed = true;
248  break;
249  }
250  }
251 
252  if( bProcessed )
253  {
254  Scanline pScanline = pAcc1->GetScanline(nRow);
255  while( ( nCol < nWidth ) && ( nBit < nBits ) )
256  pAcc1->SetPixelOnData(pScanline, nCol++, ( nValue & ( 1 << nBit++ ) ) ? aBlack : aWhite);
257 
258  if( nCol == nWidth )
259  {
260  nCol = 0;
261  nRow++;
262  }
263  }
264  }
265  }
266  }
267 }
268 
269 ReadState XBMReader::ReadXBM( Graphic& rGraphic )
270 {
271  ReadState eReadState;
272  sal_uInt8 cDummy;
273 
274  // check if we can read ALL
275  rIStm.Seek( STREAM_SEEK_TO_END );
276  rIStm.ReadUChar( cDummy );
277 
278  // if we cannot read all
279  // we return and wait for new data
280  if ( rIStm.GetError() != ERRCODE_IO_PENDING )
281  {
282  rIStm.Seek( nLastPos );
283  bStatus = false;
284  OString aLine = FindTokenLine( &rIStm, "#define", "_width" );
285 
286  if ( bStatus )
287  {
288  int nValue;
289  if ( ( nValue = ParseDefine( aLine.getStr() ) ) > 0 )
290  {
291  nWidth = nValue;
292  aLine = FindTokenLine( &rIStm, "#define", "_height" );
293 
294  // if height was not received, we search again
295  // from start of the file
296  if ( !bStatus )
297  {
298  rIStm.Seek( nLastPos );
299  aLine = FindTokenLine( &rIStm, "#define", "_height" );
300  }
301  }
302  else
303  bStatus = false;
304 
305  if ( bStatus )
306  {
307  if ( ( nValue = ParseDefine( aLine.getStr() ) ) > 0 )
308  {
309  nHeight = nValue;
310  aLine = FindTokenLine( &rIStm, "static", "_bits" );
311 
312  if ( bStatus )
313  {
314  XBMFormat eFormat = XBM10;
315 
316  if (aLine.indexOf("short") != -1)
317  eFormat = XBM10;
318  else if (aLine.indexOf("char") != -1)
319  eFormat = XBM11;
320  else
321  bStatus = false;
322 
323  //xbms are a minimum of one character per 8 pixels, so if the file isn't
324  //even that long, it's not all there
325  if (rIStm.remainingSize() < (static_cast<sal_uInt64>(nWidth) * nHeight) / 8)
326  bStatus = false;
327 
328  if ( bStatus && nWidth && nHeight )
329  {
330  aBmp1 = Bitmap( Size( nWidth, nHeight ), 1 );
331  pAcc1 = BitmapScopedWriteAccess(aBmp1);
332 
333  if( pAcc1 )
334  {
335  aWhite = pAcc1->GetBestMatchingColor( COL_WHITE );
336  aBlack = pAcc1->GetBestMatchingColor( COL_BLACK );
337  ParseData( &rIStm, aLine, eFormat );
338  }
339  else
340  bStatus = false;
341  }
342  }
343  }
344  }
345  }
346 
347  if (bStatus && pAcc1)
348  {
349  Bitmap aBlackBmp( Size( pAcc1->Width(), pAcc1->Height() ), 1 );
350 
351  pAcc1.reset();
352  aBlackBmp.Erase( COL_BLACK );
353  rGraphic = BitmapEx( aBlackBmp, aBmp1 );
354  eReadState = XBMREAD_OK;
355  }
356  else
357  eReadState = XBMREAD_ERROR;
358  }
359  else
360  {
361  rIStm.ResetError();
362  eReadState = XBMREAD_NEED_MORE;
363  }
364 
365  return eReadState;
366 }
367 
368 VCL_DLLPUBLIC bool ImportXBM( SvStream& rStm, Graphic& rGraphic )
369 {
370  std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
371  rGraphic.SetReaderContext(nullptr);
372  XBMReader* pXBMReader = dynamic_cast<XBMReader*>( pContext.get() );
373  if (!pXBMReader)
374  {
375  pContext = std::make_shared<XBMReader>( rStm );
376  pXBMReader = static_cast<XBMReader*>( pContext.get() );
377  }
378 
379  bool bRet = true;
380 
381  ReadState eReadState = pXBMReader->ReadXBM( rGraphic );
382 
383  if( eReadState == XBMREAD_ERROR )
384  {
385  bRet = false;
386  }
387  else if( eReadState == XBMREAD_NEED_MORE )
388  rGraphic.SetReaderContext( pContext );
389 
390  return bRet;
391 }
392 
393 /* 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:501
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:368
unsigned char sal_uInt8
std::shared_ptr< GraphicReader > & GetReaderContext()
Definition: graph.cxx:496
#define ERRCODE_IO_PENDING
Definition: errcode.hxx:227
sal_uInt16 nPos
sal_Int16 nValue