LibreOffice Module vcl (master)  1
xpmread.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 <bitmapwriteaccess.hxx>
21 #include <vcl/graph.hxx>
22 #include <tools/stream.hxx>
24 #include "rgbtable.hxx"
25 #include "xpmread.hxx"
26 #include <cstring>
27 #include <array>
28 #include <map>
29 
30 #define XPMTEMPBUFSIZE 0x00008000
31 #define XPMSTRINGBUF 0x00008000
32 
33 #define XPMIDENTIFIER 0x00000001 // mnIdentifier includes one of the six phases
34 #define XPMDEFINITION 0x00000002 // the XPM format consists of
35 #define XPMVALUES 0x00000003
36 #define XPMCOLORS 0x00000004
37 #define XPMPIXELS 0x00000005
38 #define XPMEXTENSIONS 0x00000006
39 #define XPMENDEXT 0x00000007
40 
41 #define XPMREMARK 0x00000001 // defines used by mnStatus
42 #define XPMDOUBLE 0x00000002
43 #define XPMSTRING 0x00000004
44 #define XPMFINISHED 0x00000008
45 
46 namespace {
47 
49 {
50  XPMREAD_OK,
51  XPMREAD_ERROR,
52  XPMREAD_NEED_MORE
53 };
54 
55 }
56 
57 class BitmapWriteAccess;
58 class Graphic;
59 
60 namespace {
61 
62 class XPMReader : public GraphicReader
63 {
64 private:
65 
66  SvStream& mrIStm;
67  Bitmap maBmp;
69  Bitmap maMaskBmp;
70  BitmapScopedWriteAccess mpMaskAcc;
71  long mnLastPos;
72 
75  sal_uLong mnColors;
76  sal_uInt32 mnCpp; // characters per pix
77  bool mbTransparent;
78  bool mbStatus;
79  sal_uLong mnStatus;
80  sal_uLong mnIdentifier;
81  sal_uInt8 mcThisByte;
82  sal_uInt8 mcLastByte;
83  sal_uLong mnTempAvail;
84  sal_uInt8* mpTempBuf;
85  sal_uInt8* mpTempPtr;
86  // each key is ( mnCpp )Byte(s)-> ASCII entry assigned to the colour
87  // each colordata is
88  // 1 Byte -> 0xFF if colour is transparent
89  // 3 Bytes -> RGB value of the colour
90  typedef std::array<sal_uInt8,4> colordata;
91  typedef std::map<OString, colordata> colormap;
92  colormap maColMap;
93  sal_uLong mnStringSize;
94  sal_uInt8* mpStringBuf;
95  sal_uLong mnParaSize;
96  sal_uInt8* mpPara;
97 
98  bool ImplGetString();
99  bool ImplGetColor();
100  bool ImplGetScanLine( sal_uLong );
101  bool ImplGetColSub(colordata &rDest);
102  bool ImplGetColKey( sal_uInt8 );
103  void ImplGetRGBHex(colordata &rDest, sal_uLong);
104  bool ImplGetPara( sal_uLong numb );
105  static bool ImplCompare(sal_uInt8 const *, sal_uInt8 const *, sal_uLong);
106  sal_uLong ImplGetULONG( sal_uLong nPara );
107 
108 public:
109  explicit XPMReader( SvStream& rStm );
110 
111  ReadState ReadXPM( Graphic& rGraphic );
112 };
113 
114 }
115 
116 XPMReader::XPMReader(SvStream& rStm)
117  : mrIStm(rStm)
118  , mnLastPos(rStm.Tell())
119  , mnWidth(0)
120  , mnHeight(0)
121  , mnColors(0)
122  , mnCpp(0)
123  , mbTransparent(false)
124  , mbStatus(true)
125  , mnStatus( 0 )
126  , mnIdentifier(XPMIDENTIFIER)
127  , mcThisByte(0)
128  , mcLastByte(0)
129  , mnTempAvail(0)
130  , mpTempBuf(nullptr)
131  , mpTempPtr(nullptr)
132  , mnStringSize(0)
133  , mpStringBuf(nullptr)
134  , mnParaSize(0)
135  , mpPara(nullptr)
136 {
137 }
138 
139 ReadState XPMReader::ReadXPM( Graphic& rGraphic )
140 {
141  ReadState eReadState;
142  sal_uInt8 cDummy;
143 
144  // check if we can real ALL
145  mrIStm.Seek( STREAM_SEEK_TO_END );
146  mrIStm.ReadUChar( cDummy );
147 
148  // if we could not read all
149  // return and wait for new data
150  if ( mrIStm.GetError() != ERRCODE_IO_PENDING )
151  {
152  mrIStm.Seek( mnLastPos );
153  mbStatus = true;
154 
155  if ( mbStatus )
156  {
157  mpStringBuf = new sal_uInt8 [ XPMSTRINGBUF ];
158  mpTempBuf = new sal_uInt8 [ XPMTEMPBUFSIZE ];
159 
160  mbStatus = ImplGetString();
161  if ( mbStatus )
162  {
163  mnIdentifier = XPMVALUES; // fetch Bitmap information
164  mnWidth = ImplGetULONG( 0 );
165  mnHeight = ImplGetULONG( 1 );
166  mnColors = ImplGetULONG( 2 );
167  mnCpp = ImplGetULONG( 3 );
168  }
169  if ( mnColors > ( SAL_MAX_UINT32 / ( 4 + mnCpp ) ) )
170  mbStatus = false;
171  if ( ( mnWidth * mnCpp ) >= XPMSTRINGBUF )
172  mbStatus = false;
173  //xpms are a minimum of one character (one byte) per pixel, so if the file isn't
174  //even that long, it's not all there
175  if (mrIStm.remainingSize() + mnTempAvail < static_cast<sal_uInt64>(mnWidth) * mnHeight)
176  mbStatus = false;
177  if ( mbStatus && mnWidth && mnHeight && mnColors && mnCpp )
178  {
179  mnIdentifier = XPMCOLORS;
180 
181  for (sal_uLong i = 0; i < mnColors; ++i)
182  {
183  if (!ImplGetColor())
184  {
185  mbStatus = false;
186  break;
187  }
188  }
189 
190  if ( mbStatus )
191  {
192  // create a 24bit graphic when more as 256 colours present
193  sal_uInt16 nBits = 1;
194  if ( mnColors > 256 )
195  nBits = 24;
196  else if ( mnColors > 16 )
197  nBits = 8;
198  else if ( mnColors > 2 )
199  nBits = 4;
200  else
201  nBits = 1;
202 
203  maBmp = Bitmap( Size( mnWidth, mnHeight ), nBits );
204  mpAcc = BitmapScopedWriteAccess(maBmp);
205 
206  // mbTransparent is TRUE if at least one colour is transparent
207  if ( mbTransparent )
208  {
209  maMaskBmp = Bitmap( Size( mnWidth, mnHeight ), 1 );
210  mpMaskAcc = BitmapScopedWriteAccess(maMaskBmp);
211  if ( !mpMaskAcc )
212  mbStatus = false;
213  }
214  if( mpAcc && mbStatus )
215  {
216  if (mnColors <= 256) // palette is only needed by using less than 257
217  { // colors
218  sal_uInt8 i = 0;
219  for (auto& elem : maColMap)
220  {
221  mpAcc->SetPaletteColor(i, Color(elem.second[1], elem.second[2], elem.second[3]));
222  //reuse map entry, overwrite color with palette index
223  elem.second[1] = i;
224  i++;
225  }
226  }
227 
228  // now we get the bitmap data
229  mnIdentifier = XPMPIXELS;
230  for (sal_uLong i = 0; i < mnHeight; ++i)
231  {
232  if ( !ImplGetScanLine( i ) )
233  {
234  mbStatus = false;
235  break;
236  }
237  }
238  mnIdentifier = XPMEXTENSIONS;
239  }
240  }
241  }
242 
243  delete[] mpStringBuf;
244  delete[] mpTempBuf;
245 
246  }
247  if( mbStatus )
248  {
249  mpAcc.reset();
250  if ( mpMaskAcc )
251  {
252  mpMaskAcc.reset();
253  rGraphic = Graphic( BitmapEx( maBmp, maMaskBmp ) );
254  }
255  else
256  {
257  rGraphic = maBmp;
258  }
259  eReadState = XPMREAD_OK;
260  }
261  else
262  {
263  mpMaskAcc.reset();
264  mpAcc.reset();
265 
266  eReadState = XPMREAD_ERROR;
267  }
268  }
269  else
270  {
271  mrIStm.ResetError();
272  eReadState = XPMREAD_NEED_MORE;
273  }
274  return eReadState;
275 }
276 
277 // ImplGetColor returns various colour values,
278 // returns TRUE if various colours could be assigned
279 bool XPMReader::ImplGetColor()
280 {
281  sal_uInt8* pString = mpStringBuf;
282  if (!ImplGetString())
283  return false;
284 
285  if (mnStringSize < mnCpp)
286  return false;
287 
288  OString aKey(reinterpret_cast<char*>(pString), mnCpp);
289  colordata aValue;
290  bool bStatus = ImplGetColSub(aValue);
291  if (bStatus)
292  {
293  maColMap[aKey] = aValue;
294  }
295  return bStatus;
296 }
297 
298 // ImpGetScanLine reads the string mpBufSize and writes the pixel in the
299 // Bitmap. Parameter nY is the horizontal position.
300 bool XPMReader::ImplGetScanLine( sal_uLong nY )
301 {
302  bool bStatus = ImplGetString();
303  sal_uInt8* pString = mpStringBuf;
304  BitmapColor aWhite;
305  BitmapColor aBlack;
306 
307  if ( bStatus )
308  {
309  if ( mpMaskAcc )
310  {
311  aWhite = mpMaskAcc->GetBestMatchingColor( COL_WHITE );
312  aBlack = mpMaskAcc->GetBestMatchingColor( COL_BLACK );
313  }
314  if ( mnStringSize != ( mnWidth * mnCpp ))
315  bStatus = false;
316  else
317  {
318  Scanline pScanline = mpAcc->GetScanline(nY);
319  Scanline pMaskScanline = mpMaskAcc ? mpMaskAcc->GetScanline(nY) : nullptr;
320  for (sal_uLong i = 0; i < mnWidth; ++i)
321  {
322  OString aKey(reinterpret_cast<char*>(pString), mnCpp);
323  auto it = maColMap.find(aKey);
324  if (it != maColMap.end())
325  {
326  if (mnColors > 256)
327  mpAcc->SetPixelOnData(pScanline, i, Color(it->second[1], it->second[2], it->second[3]));
328  else
329  mpAcc->SetPixelOnData(pScanline, i, BitmapColor(it->second[1]));
330  if (pMaskScanline)
331  mpMaskAcc->SetPixelOnData(pMaskScanline, i, it->second[0] ? aWhite : aBlack);
332  }
333  pString += mnCpp;
334  }
335  }
336  }
337  return bStatus;
338 }
339 
340 // tries to determine a colour value from mpStringBuf
341 // if a colour was found the RGB value is written a pDest[1]..pDest[2]
342 // pDest[0] contains 0xFF if the colour is transparent otherwise 0
343 
344 bool XPMReader::ImplGetColSub(colordata &rDest)
345 {
346  unsigned char cTransparent[] = "None";
347 
348  bool bColStatus = false;
349 
350  if ( ImplGetColKey( 'c' ) || ImplGetColKey( 'm' ) || ImplGetColKey( 'g' ) )
351  {
352  // hexentry for RGB or HSV color ?
353  if (*mpPara == '#')
354  {
355  rDest[0] = 0;
356  bColStatus = true;
357  switch ( mnParaSize )
358  {
359  case 25 :
360  ImplGetRGBHex(rDest, 6);
361  break;
362  case 13 :
363  ImplGetRGBHex(rDest, 2);
364  break;
365  case 7 :
366  ImplGetRGBHex(rDest, 0);
367  break;
368  default:
369  bColStatus = false;
370  break;
371  }
372  }
373  // maybe pixel is transparent
374  else if ( ImplCompare( &cTransparent[0], mpPara, 4 ))
375  {
376  rDest[0] = 0xff;
377  bColStatus = true;
378  mbTransparent = true;
379  }
380  // last we will try to get the colorname
381  else if ( mnParaSize > 2 ) // name must enlarge the minimum size
382  {
383  sal_uLong i = 0;
384  while ( true )
385  {
386  if ( pRGBTable[ i ].name == nullptr )
387  break;
388  if ( std::strlen(pRGBTable[i].name) > mnParaSize &&
389  pRGBTable[ i ].name[ mnParaSize ] == 0 )
390  {
391  if ( ImplCompare ( reinterpret_cast<unsigned char const *>(pRGBTable[ i ].name),
392  mpPara, mnParaSize ) )
393  {
394  bColStatus = true;
395  rDest[0] = 0;
396  rDest[1] = pRGBTable[i].red;
397  rDest[2] = pRGBTable[i].green;
398  rDest[3] = pRGBTable[i].blue;
399  break;
400  }
401  }
402  i++;
403  }
404  }
405  }
406  return bColStatus;
407 }
408 
409 // ImplGetColKey searches string mpStringBuf for a parameter 'nKey'
410 // and returns a boolean. (if TRUE mpPara and mnParaSize will be set)
411 
412 bool XPMReader::ImplGetColKey( sal_uInt8 nKey )
413 {
414  sal_uInt8 nTemp, nPrev = ' ';
415 
416  if (mnStringSize < mnCpp + 1)
417  return false;
418 
419  mpPara = mpStringBuf + mnCpp + 1;
420  mnParaSize = 0;
421 
422  while ( *mpPara != 0 )
423  {
424  if ( *mpPara == nKey )
425  {
426  nTemp = *( mpPara + 1 );
427  if ( nTemp == ' ' || nTemp == 0x09 )
428  {
429  if ( nPrev == ' ' || nPrev == 0x09 )
430  break;
431  }
432  }
433  nPrev = *mpPara;
434  mpPara++;
435  }
436  if ( *mpPara )
437  {
438  mpPara++;
439  while ( (*mpPara == ' ') || (*mpPara == 0x09) )
440  {
441  mpPara++;
442  }
443  if ( *mpPara != 0 )
444  {
445  while ( *(mpPara+mnParaSize) != ' ' && *(mpPara+mnParaSize) != 0x09 &&
446  *(mpPara+mnParaSize) != 0 )
447  {
448  mnParaSize++;
449  }
450  }
451  }
452  return mnParaSize != 0;
453 }
454 
455 // ImplGetRGBHex translates the ASCII-Hexadecimalvalue belonging to mpPara
456 // in a RGB value and writes this to rDest
457 // below formats should be contained in mpPara:
458 // if nAdd = 0 : '#12ab12' -> RGB = 0x12, 0xab, 0x12
459 // 2 : '#1234abcd1234' " " " "
460 // 6 : '#12345678abcdefab12345678' " " " "
461 
462 void XPMReader::ImplGetRGBHex(colordata &rDest, sal_uLong nAdd)
463 {
464  sal_uInt8* pPtr = mpPara+1;
465 
466  for (sal_uLong i = 1; i < 4; ++i)
467  {
468  sal_uInt8 nHex = (*pPtr++) - '0';
469  if ( nHex > 9 )
470  nHex = ((nHex - 'A' + '0') & 7) + 10;
471 
472  sal_uInt8 nTemp = (*pPtr++) - '0';
473  if ( nTemp > 9 )
474  nTemp = ((nTemp - 'A' + '0') & 7) + 10;
475  nHex = ( nHex << 4 ) + nTemp;
476 
477  pPtr += nAdd;
478  rDest[i] = nHex;
479  }
480 }
481 
482 // ImplGetUlong returns the value of a up to 6-digit long ASCII-decimal number.
483 
484 sal_uLong XPMReader::ImplGetULONG( sal_uLong nPara )
485 {
486  if ( ImplGetPara ( nPara ) )
487  {
488  sal_uLong nRetValue = 0;
489  sal_uInt8* pPtr = mpPara;
490 
491  if ( ( mnParaSize > 6 ) || ( mnParaSize == 0 ) ) return 0;
492  for ( sal_uLong i = 0; i < mnParaSize; i++ )
493  {
494  sal_uInt8 j = (*pPtr++) - 48;
495  if ( j > 9 ) return 0; // ascii is invalid
496  nRetValue*=10;
497  nRetValue+=j;
498  }
499  return nRetValue;
500  }
501  else return 0;
502 }
503 
504 bool XPMReader::ImplCompare(sal_uInt8 const * pSource, sal_uInt8 const * pDest, sal_uLong nSize)
505 {
506  for (sal_uLong i = 0; i < nSize; ++i)
507  {
508  if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
509  {
510  return false;
511  }
512  }
513  return true;
514 }
515 
516 // ImplGetPara tries to retrieve nNumb (0...x) parameters from mpStringBuf.
517 // Parameters are separated by spaces or tabs.
518 // If a parameter was found then the return value is TRUE and mpPara + mnParaSize
519 // are set.
520 
521 bool XPMReader::ImplGetPara ( sal_uLong nNumb )
522 {
523  sal_uInt8 nByte;
524  sal_uLong nSize = 0;
525  sal_uInt8* pPtr = mpStringBuf;
526  sal_uLong nCount = 0;
527 
528  if ( ( *pPtr != ' ' ) && ( *pPtr != 0x09 ) )
529  {
530  mpPara = pPtr;
531  mnParaSize = 0;
532  nCount = 0;
533  }
534  else
535  {
536  mpPara = nullptr;
537  nCount = 0xffffffff;
538  }
539 
540  while ( nSize < mnStringSize )
541  {
542  nByte = *pPtr;
543 
544  if ( mpPara )
545  {
546  if ( ( nByte == ' ' ) || ( nByte == 0x09 ) )
547  {
548  if ( nCount == nNumb )
549  break;
550  else
551  mpPara = nullptr;
552  }
553  else
554  mnParaSize++;
555  }
556  else
557  {
558  if ( ( nByte != ' ' ) && ( nByte != 0x09 ) )
559  {
560  mpPara = pPtr;
561  mnParaSize = 1;
562  nCount++;
563  }
564  }
565  nSize++;
566  pPtr++;
567  }
568  return ( ( nCount == nNumb ) && mpPara );
569 }
570 
571 // The next string is read and stored in mpStringBuf (terminated with 0);
572 // mnStringSize contains the size of the string read.
573 // Comments like '//' and '/*...*/' are skipped.
574 
575 bool XPMReader::ImplGetString()
576 {
577  sal_uInt8 const sID[] = "/* XPM */";
578  sal_uInt8* pString = mpStringBuf;
579 
580  mnStringSize = 0;
581  mpStringBuf[0] = 0;
582 
583  while( mbStatus && ( mnStatus != XPMFINISHED ) )
584  {
585  if ( mnTempAvail == 0 )
586  {
587  mnTempAvail = mrIStm.ReadBytes( mpTempBuf, XPMTEMPBUFSIZE );
588  if ( mnTempAvail == 0 )
589  break;
590 
591  mpTempPtr = mpTempBuf;
592 
593  if ( mnIdentifier == XPMIDENTIFIER )
594  {
595  if ( mnTempAvail <= 50 )
596  {
597  mbStatus = false; // file is too short to be a correct XPM format
598  break;
599  }
600  for ( int i = 0; i < 9; i++ ) // searching for "/* XPM */"
601  if ( *mpTempPtr++ != sID[i] )
602  {
603  mbStatus = false;
604  break;
605  }
606  mnTempAvail-=9;
607  mnIdentifier++;
608  }
609  }
610  mcLastByte = mcThisByte;
611  mcThisByte = *mpTempPtr++;
612  mnTempAvail--;
613 
614  if ( mnStatus & XPMDOUBLE )
615  {
616  if ( mcThisByte == 0x0a )
617  mnStatus &=~XPMDOUBLE;
618  continue;
619  }
620  if ( mnStatus & XPMREMARK )
621  {
622  if ( ( mcThisByte == '/' ) && ( mcLastByte == '*' ) )
623  mnStatus &=~XPMREMARK;
624  continue;
625  }
626  if ( mnStatus & XPMSTRING ) // characters in string
627  {
628  if ( mcThisByte == '"' )
629  {
630  mnStatus &=~XPMSTRING; // end of parameter by eol
631  break;
632  }
633  if ( mnStringSize >= ( XPMSTRINGBUF - 1 ) )
634  {
635  mbStatus = false;
636  break;
637  }
638  *pString++ = mcThisByte;
639  pString[0] = 0;
640  mnStringSize++;
641  continue;
642  }
643  else
644  { // characters beside string
645  switch ( mcThisByte )
646  {
647  case '*' :
648  if ( mcLastByte == '/' ) mnStatus |= XPMREMARK;
649  break;
650  case '/' :
651  if ( mcLastByte == '/' ) mnStatus |= XPMDOUBLE;
652  break;
653  case '"' : mnStatus |= XPMSTRING;
654  break;
655  case '{' :
656  if ( mnIdentifier == XPMDEFINITION )
657  mnIdentifier++;
658  break;
659  case '}' :
660  if ( mnIdentifier == XPMENDEXT )
661  mnStatus = XPMFINISHED;
662  break;
663  }
664  }
665  }
666  return mbStatus;
667 }
668 
669 
670 VCL_DLLPUBLIC bool ImportXPM( SvStream& rStm, Graphic& rGraphic )
671 {
672  std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
673  rGraphic.SetReaderContext(nullptr);
674  XPMReader* pXPMReader = dynamic_cast<XPMReader*>( pContext.get() );
675  if (!pXPMReader)
676  {
677  pContext = std::make_shared<XPMReader>( rStm );
678  pXPMReader = static_cast<XPMReader*>( pContext.get() );
679  }
680 
681  bool bRet = true;
682 
683  ReadState eReadState = pXPMReader->ReadXPM( rGraphic );
684 
685  if( eReadState == XPMREAD_ERROR )
686  {
687  bRet = false;
688  }
689  else if( eReadState == XPMREAD_NEED_MORE )
690  rGraphic.SetReaderContext( pContext );
691 
692  return bRet;
693 }
694 
695 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
double mnHeight
#define XPMIDENTIFIER
Definition: xpmread.cxx:33
#define XPMENDEXT
Definition: xpmread.cxx:39
#define VCL_DLLPUBLIC
Definition: dllapi.h:29
sal_uIntPtr sal_uLong
#define XPMSTRINGBUF
Definition: xpmread.cxx:31
vcl::ScopedBitmapAccess< BitmapWriteAccess, Bitmap,&Bitmap::AcquireWriteAccess > BitmapScopedWriteAccess
#define XPMDEFINITION
Definition: xpmread.cxx:34
ReadState
Definition: gifread.cxx:44
This template handles BitmapAccess the RAII way.
void SetReaderContext(const std::shared_ptr< GraphicReader > &pReader)
Definition: graph.cxx:501
static const XPMRGBTab pRGBTable[]
Definition: rgbtable.hxx:33
int nCount
VCL_DLLPUBLIC bool ImportXPM(SvStream &rStm, Graphic &rGraphic)
Definition: xpmread.cxx:670
#define XPMEXTENSIONS
Definition: xpmread.cxx:38
sal_uInt8 * Scanline
Definition: Scanline.hxx:25
#define XPMDOUBLE
Definition: xpmread.cxx:42
int i
#define XPMCOLORS
Definition: xpmread.cxx:36
#define XPMFINISHED
Definition: xpmread.cxx:44
#define XPMSTRING
Definition: xpmread.cxx:43
#define XPMTEMPBUFSIZE
Definition: xpmread.cxx:30
#define XPMREMARK
Definition: xpmread.cxx:41
sal_uInt8 blue
Definition: rgbtable.hxx:30
sal_uInt8 red
Definition: rgbtable.hxx:28
unsigned char sal_uInt8
std::shared_ptr< GraphicReader > & GetReaderContext()
Definition: graph.cxx:496
double mnWidth
#define XPMVALUES
Definition: xpmread.cxx:35
#define ERRCODE_IO_PENDING
Definition: errcode.hxx:227
#define XPMPIXELS
Definition: xpmread.cxx:37
sal_uInt8 green
Definition: rgbtable.hxx:29