1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <vcl/bmpacc.hxx>
21 #include <vcl/graph.hxx>
22 #include "rgbtable.hxx"
24 #include "xpmread.hxx"
27 XPMReader::XPMReader(SvStream
& rStm
)
31 , mnLastPos(rStm
.Tell())
36 , mbTransparent(false)
39 , mnIdentifier(XPMIDENTIFIER
)
45 , mpFastColorTable(NULL
)
54 XPMReader::~XPMReader()
57 Bitmap::ReleaseAccess( mpAcc
);
60 ReadState
XPMReader::ReadXPM( Graphic
& rGraphic
)
65 // check if we can real ALL
66 mrIStm
.Seek( STREAM_SEEK_TO_END
);
67 mrIStm
.ReadUChar( cDummy
);
69 // if we could not read all
70 // return and wait for new data
71 if ( mrIStm
.GetError() != ERRCODE_IO_PENDING
)
73 mrIStm
.Seek( mnLastPos
);
78 mpStringBuf
= new sal_uInt8
[ XPMSTRINGBUF
];
79 mpTempBuf
= new sal_uInt8
[ XPMTEMPBUFSIZE
];
81 if ( ( mbStatus
= ImplGetString() ) )
83 mnIdentifier
= XPMVALUES
; // fetch Bitmap information
84 mnWidth
= ImplGetULONG( 0 );
85 mnHeight
= ImplGetULONG( 1 );
86 mnColors
= ImplGetULONG( 2 );
87 mnCpp
= ImplGetULONG( 3 );
89 if ( mnColors
> ( SAL_MAX_UINT32
/ ( 4 + mnCpp
) ) )
91 if ( ( mnWidth
* mnCpp
) >= XPMSTRINGBUF
)
93 if ( mbStatus
&& mnWidth
&& mnHeight
&& mnColors
&& mnCpp
)
95 mnIdentifier
= XPMCOLORS
;
97 // mpColMap constitutes for all available
98 // colour: ( mnCpp )Byte(s)-> ASCII entry assigned to the colour
99 // 1 Byte -> 0xFF if colour is transparent
100 // 3 Bytes -> RGB value of the colour
101 mpColMap
= new sal_uInt8
[ mnColors
* ( 4 + mnCpp
) ];
102 for ( sal_uLong i
= 0; i
< mnColors
; i
++ )
104 if ( !ImplGetColor( i
) )
113 // create a 24bit graphic when more as 256 colours present
114 sal_uInt16 nBits
= 1;
115 if ( mnColors
> 256 )
117 else if ( mnColors
> 16 )
119 else if ( mnColors
> 2 )
124 maBmp
= Bitmap( Size( mnWidth
, mnHeight
), nBits
);
125 mpAcc
= maBmp
.AcquireWriteAccess();
127 // mbTransparent is TRUE if at least one colour is transparent
130 maMaskBmp
= Bitmap( Size( mnWidth
, mnHeight
), 1 );
131 if ( ( mpMaskAcc
= maMaskBmp
.AcquireWriteAccess() ) == NULL
)
134 if( mpAcc
&& mbStatus
)
137 if ( mnColors
<=256 ) // palette is only needed by using less than 257
140 sal_uInt8
* pPtr
= &mpColMap
[mnCpp
];
142 for ( i
= 0; i
< mnColors
; i
++ )
144 mpAcc
->SetPaletteColor( (sal_uInt8
)i
, Color( pPtr
[1], pPtr
[2], pPtr
[3] ) );
145 pPtr
+= ( mnCpp
+ 4 );
147 // using 2 charakters per pixel and less than 257 Colors we speed up
148 if ( mnCpp
== 2 ) // by using a 64kb indexing table
150 mpFastColorTable
= new sal_uInt8
[ 256 * 256 ];
151 for ( pPtr
= mpColMap
, i
= 0; i
< mnColors
; i
++, pPtr
+= mnCpp
+ 4 )
153 sal_uLong j
= pPtr
[ 0 ] << 8;
155 mpFastColorTable
[ j
] = (sal_uInt8
)i
;
159 // now we get the bitmap data
160 mnIdentifier
= XPMPIXELS
;
161 for ( i
= 0; i
< mnHeight
; i
++ )
163 if ( !ImplGetScanLine( i
) )
169 mnIdentifier
= XPMEXTENSIONS
;
174 delete[] mpFastColorTable
;
176 delete[] mpStringBuf
;
184 Bitmap::ReleaseAccess ( mpMaskAcc
), mpMaskAcc
= NULL
;
185 Bitmap::ReleaseAccess( mpAcc
), mpAcc
= NULL
;
186 rGraphic
= Graphic( BitmapEx( maBmp
, maMaskBmp
) );
190 Bitmap::ReleaseAccess( mpAcc
), mpAcc
= NULL
;
193 eReadState
= XPMREAD_OK
;
197 if ( mpMaskAcc
) Bitmap::ReleaseAccess ( mpMaskAcc
), mpMaskAcc
= NULL
;
198 if ( mpAcc
) Bitmap::ReleaseAccess( mpAcc
), mpAcc
= NULL
;
199 eReadState
= XPMREAD_ERROR
;
205 eReadState
= XPMREAD_NEED_MORE
;
210 // ImplGetColor returns variouls colour values,
211 // returns TRUE if various colours could be assigned
213 bool XPMReader::ImplGetColor( sal_uLong nNumb
)
215 sal_uInt8
* pString
= mpStringBuf
;
216 sal_uInt8
* pPtr
= ( mpColMap
+ nNumb
* ( 4 + mnCpp
) );
217 bool bStatus
= ImplGetString();
221 for ( sal_uLong i
= 0; i
< mnCpp
; i
++ )
222 *pPtr
++ = *pString
++;
223 bStatus
= ImplGetColSub ( pPtr
);
228 // ImpGetScanLine reads the string mpBufSize and writes the pixel in the
229 // Bitmap. Parameter nY is the horizontal position.
231 bool XPMReader::ImplGetScanLine( sal_uLong nY
)
233 bool bStatus
= ImplGetString();
234 sal_uInt8
* pString
= mpStringBuf
;
243 aWhite
= mpMaskAcc
->GetBestMatchingColor( Color( COL_WHITE
) );
244 aBlack
= mpMaskAcc
->GetBestMatchingColor( Color( COL_BLACK
) );
246 if ( mnStringSize
!= ( mnWidth
* mnCpp
))
251 if ( mpFastColorTable
)
253 for ( i
= 0; i
< mnWidth
; i
++ )
255 j
= (*pString
++) << 8;
257 sal_uInt8 k
= (sal_uInt8
)mpFastColorTable
[ j
];
258 mpAcc
->SetPixel( nY
, i
, BitmapColor( (sal_uInt8
)k
) );
261 mpMaskAcc
->SetPixel( nY
, i
,
262 ( mpColMap
[ k
* (mnCpp
+ 4) + mnCpp
] ) ? aWhite
: aBlack
);
265 else for ( i
= 0; i
< mnWidth
; i
++ )
268 for ( j
= 0; j
< mnColors
; j
++ )
270 if ( ImplCompare( pString
, pColor
, mnCpp
, XPMCASESENSITIVE
) )
272 if ( mnColors
> 256 )
273 mpAcc
->SetPixel( nY
, i
, Color ( pColor
[3], pColor
[4], pColor
[5] ) );
275 mpAcc
->SetPixel( nY
, i
, BitmapColor( (sal_uInt8
) j
) );
278 mpMaskAcc
->SetPixel( nY
, i
, (
279 pColor
[ mnCpp
] ) ? aWhite
: aBlack
);
283 pColor
+= ( mnCpp
+ 4 );
293 // tries to determine a colour value from mpStringBuf
294 // if a colour was found the RGB value is written a pDest[1]..pDest[2]
295 // pDest[0] contains 0xFF if the colour is transparent otherwise 0
297 bool XPMReader::ImplGetColSub( sal_uInt8
* pDest
)
299 unsigned char cTransparent
[] = "None";
301 bool bColStatus
= false;
303 if ( ImplGetColKey( 'c' ) || ImplGetColKey( 'm' ) || ImplGetColKey( 'g' ) )
305 // hexentry for RGB or HSV color ?
306 if ( *mpPara
== '#' )
310 switch ( mnParaSize
)
313 ImplGetRGBHex ( pDest
, 6 );
316 ImplGetRGBHex ( pDest
, 2 );
319 ImplGetRGBHex ( pDest
, 0 );
326 // maybe pixel is transparent
327 else if ( ImplCompare( &cTransparent
[0], mpPara
, 4 ))
331 mbTransparent
= true;
333 // last we will try to get the colorname
334 else if ( mnParaSize
> 2 ) // name must enlarge the minimum size
339 if ( pRGBTable
[ i
].name
== NULL
)
341 if ( std::strlen(pRGBTable
[i
].name
) > mnParaSize
&&
342 pRGBTable
[ i
].name
[ mnParaSize
] == 0 )
344 if ( ImplCompare ( reinterpret_cast<unsigned char const *>(pRGBTable
[ i
].name
),
345 mpPara
, mnParaSize
, XPMCASENONSENSITIVE
) )
349 *pDest
++ = pRGBTable
[ i
].red
;
350 *pDest
++ = pRGBTable
[ i
].green
;
351 *pDest
++ = pRGBTable
[ i
].blue
;
361 // ImplGetColKey searches string mpStringBuf for a parameter 'nKey'
362 // and returns a boolean. (if TRUE mpPara and mnParaSize will be set)
364 bool XPMReader::ImplGetColKey( sal_uInt8 nKey
)
366 sal_uInt8 nTemp
, nPrev
= ' ';
368 mpPara
= mpStringBuf
+ mnCpp
+ 1;
371 while ( *mpPara
!= 0 )
373 if ( *mpPara
== nKey
)
375 nTemp
= *( mpPara
+ 1 );
376 if ( nTemp
== ' ' || nTemp
== 0x09 )
378 if ( nPrev
== ' ' || nPrev
== 0x09 )
388 while ( (*mpPara
== ' ') || (*mpPara
== 0x09) )
394 while ( *(mpPara
+mnParaSize
) != ' ' && *(mpPara
+mnParaSize
) != 0x09 &&
395 *(mpPara
+mnParaSize
) != 0 )
401 return mnParaSize
!= 0;
404 // ImplGetRGBHex translates the ASCII-Hexadecimalvalue belonging to mpPara
405 // in a RGB value and writes this to pDest
406 // below formats should be contained in mpPara:
407 // if nAdd = 0 : '#12ab12' -> RGB = 0x12, 0xab, 0x12
408 // 2 : '#1234abcd1234' " " " "
409 // 6 : '#12345678abcdefab12345678' " " " "
411 void XPMReader::ImplGetRGBHex( sal_uInt8
* pDest
,sal_uLong nAdd
)
413 sal_uInt8
* pPtr
= mpPara
+1;
414 sal_uInt8 nHex
, nTemp
;
416 for ( sal_uLong i
= 0; i
< 3; i
++ )
418 nHex
= (*pPtr
++) - '0';
420 nHex
= ((nHex
- 'A' + '0') & 7) + 10;
422 nTemp
= (*pPtr
++) - '0';
424 nTemp
= ((nTemp
- 'A' + '0') & 7) + 10;
425 nHex
= ( nHex
<< 4 ) + nTemp
;
428 *pDest
++ = (sal_uInt8
)nHex
;
432 // ImplGetUlong returns the value of a up to 6-digit long ASCII-decimal number.
434 sal_uLong
XPMReader::ImplGetULONG( sal_uLong nPara
)
436 if ( ImplGetPara ( nPara
) )
438 sal_uLong nRetValue
= 0;
439 sal_uInt8
* pPtr
= mpPara
;
441 if ( ( mnParaSize
> 6 ) || ( mnParaSize
== 0 ) ) return 0;
442 for ( sal_uLong i
= 0; i
< mnParaSize
; i
++ )
444 sal_uInt8 j
= (*pPtr
++) - 48;
445 if ( j
> 9 ) return 0; // ascii is invalid
454 bool XPMReader::ImplCompare( sal_uInt8
const * pSource
, sal_uInt8
const * pDest
, sal_uLong nSize
, sal_uLong nMode
)
458 if ( nMode
== XPMCASENONSENSITIVE
)
460 for ( sal_uLong i
= 0; i
< nSize
; i
++ )
462 if ( ( pSource
[i
]&~0x20 ) != ( pDest
[i
]&~0x20 ) )
471 for ( sal_uLong i
= 0; i
< nSize
; i
++ )
473 if ( pSource
[i
] != pDest
[i
] )
483 // ImplGetPara tries to retrieve nNumb (0...x) parameters from mpStringBuf.
484 // Parameters are separated by spaces or tabs.
485 // If a parameter was found then the return value is TRUE and mpPara + mnParaSize
488 bool XPMReader::ImplGetPara ( sal_uLong nNumb
)
492 sal_uInt8
* pPtr
= mpStringBuf
;
493 sal_uLong nCount
= 0;
495 if ( ( *pPtr
!= ' ' ) && ( *pPtr
!= 0x09 ) )
507 while ( pSize
< mnStringSize
)
513 if ( ( nByte
== ' ' ) || ( nByte
== 0x09 ) )
515 if ( nCount
== nNumb
)
525 if ( ( nByte
!= ' ' ) && ( nByte
!= 0x09 ) )
535 return ( ( nCount
== nNumb
) && ( mpPara
) );
538 // The next string is read and stored in mpStringBuf (terminated with 0);
539 // mnStringSize contains the size of the string read.
540 // Comments like '//' and '/*....*/' are skipped.
542 bool XPMReader::ImplGetString()
544 sal_uInt8 sID
[] = "/* XPM */";
545 sal_uInt8
* pString
= mpStringBuf
;
550 while( mbStatus
&& ( mnStatus
!= XPMFINISHED
) )
552 if ( mnTempAvail
== 0 )
554 mnTempAvail
= mrIStm
.Read( mpTempBuf
, XPMTEMPBUFSIZE
);
555 if ( mnTempAvail
== 0 )
558 mpTempPtr
= mpTempBuf
;
560 if ( mnIdentifier
== XPMIDENTIFIER
)
562 if ( mnTempAvail
<= 50 )
564 mbStatus
= false; // file is too short to be a correct XPM format
567 for ( int i
= 0; i
< 9; i
++ ) // searching for "/* XPM */"
568 if ( *mpTempPtr
++ != sID
[i
] )
577 mcLastByte
= mcThisByte
;
578 mcThisByte
= *mpTempPtr
++;
581 if ( mnStatus
& XPMDOUBLE
)
583 if ( mcThisByte
== 0x0a )
584 mnStatus
&=~XPMDOUBLE
;
587 if ( mnStatus
& XPMREMARK
)
589 if ( ( mcThisByte
== '/' ) && ( mcLastByte
== '*' ) )
590 mnStatus
&=~XPMREMARK
;
593 if ( mnStatus
& XPMSTRING
) // characters in string
595 if ( mcThisByte
== '"' )
597 mnStatus
&=~XPMSTRING
; // end of parameter by eol
600 if ( mnStringSize
>= ( XPMSTRINGBUF
- 1 ) )
605 *pString
++ = mcThisByte
;
611 { // characters beside string
612 switch ( mcThisByte
)
615 if ( mcLastByte
== '/' ) mnStatus
|= XPMREMARK
;
618 if ( mcLastByte
== '/' ) mnStatus
|= XPMDOUBLE
;
620 case '"' : mnStatus
|= XPMSTRING
;
623 if ( mnIdentifier
== XPMDEFINITION
)
627 if ( mnIdentifier
== XPMENDEXT
)
628 mnStatus
= XPMFINISHED
;
638 bool ImportXPM( SvStream
& rStm
, Graphic
& rGraphic
)
640 XPMReader
* pXPMReader
= static_cast<XPMReader
*>(rGraphic
.GetContext());
641 ReadState eReadState
;
645 pXPMReader
= new XPMReader( rStm
);
647 rGraphic
.SetContext( NULL
);
648 eReadState
= pXPMReader
->ReadXPM( rGraphic
);
650 if( eReadState
== XPMREAD_ERROR
)
655 else if( eReadState
== XPMREAD_OK
)
658 rGraphic
.SetContext( pXPMReader
);
663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */