Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / source / filter / ixpm / xpmread.cxx
blob6d7646c827f3f77dac401535b1f04804d29fbce8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
23 #define _XPMPRIVATE
24 #include "xpmread.hxx"
25 #include <cstring>
27 XPMReader::XPMReader(SvStream& rStm)
28 : mrIStm(rStm)
29 , mpAcc(NULL)
30 , mpMaskAcc(NULL)
31 , mnLastPos(rStm.Tell())
32 , mnWidth(0)
33 , mnHeight(0)
34 , mnColors(0)
35 , mnCpp(0)
36 , mbTransparent(false)
37 , mbStatus(true)
38 , mnStatus( 0 )
39 , mnIdentifier(XPMIDENTIFIER)
40 , mcThisByte(0)
41 , mcLastByte(0)
42 , mnTempAvail(0)
43 , mpTempBuf(NULL)
44 , mpTempPtr(NULL)
45 , mpFastColorTable(NULL)
46 , mpColMap(NULL)
47 , mnStringSize(0)
48 , mpStringBuf(NULL)
49 , mnParaSize(0)
50 , mpPara(NULL)
54 XPMReader::~XPMReader()
56 if( mpAcc )
57 Bitmap::ReleaseAccess( mpAcc );
60 ReadState XPMReader::ReadXPM( Graphic& rGraphic )
62 ReadState eReadState;
63 sal_uInt8 cDummy;
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 );
74 mbStatus = true;
76 if ( mbStatus )
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 ) ) )
90 mbStatus = false;
91 if ( ( mnWidth * mnCpp ) >= XPMSTRINGBUF )
92 mbStatus = false;
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 ) )
106 mbStatus = false;
107 break;
111 if ( mbStatus )
113 // create a 24bit graphic when more as 256 colours present
114 sal_uInt16 nBits = 1;
115 if ( mnColors > 256 )
116 nBits = 24;
117 else if ( mnColors > 16 )
118 nBits = 8;
119 else if ( mnColors > 2 )
120 nBits = 4;
121 else
122 nBits = 1;
124 maBmp = Bitmap( Size( mnWidth, mnHeight ), nBits );
125 mpAcc = maBmp.AcquireWriteAccess();
127 // mbTransparent is TRUE if at least one colour is transparent
128 if ( mbTransparent )
130 maMaskBmp = Bitmap( Size( mnWidth, mnHeight ), 1 );
131 if ( ( mpMaskAcc = maMaskBmp.AcquireWriteAccess() ) == NULL )
132 mbStatus = false;
134 if( mpAcc && mbStatus )
136 sal_uLong i;
137 if ( mnColors <=256 ) // palette is only needed by using less than 257
138 { // colors
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;
154 j += pPtr[ 1 ];
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 ) )
165 mbStatus = false;
166 break;
169 mnIdentifier = XPMEXTENSIONS;
174 delete[] mpFastColorTable;
175 delete[] mpColMap;
176 delete[] mpStringBuf;
177 delete[] mpTempBuf;
180 if( mbStatus )
182 if ( mpMaskAcc )
184 Bitmap::ReleaseAccess ( mpMaskAcc), mpMaskAcc = NULL;
185 Bitmap::ReleaseAccess( mpAcc ), mpAcc = NULL;
186 rGraphic = Graphic( BitmapEx( maBmp, maMaskBmp ) );
188 else
190 Bitmap::ReleaseAccess( mpAcc ), mpAcc = NULL;
191 rGraphic = maBmp;
193 eReadState = XPMREAD_OK;
195 else
197 if ( mpMaskAcc ) Bitmap::ReleaseAccess ( mpMaskAcc), mpMaskAcc = NULL;
198 if ( mpAcc ) Bitmap::ReleaseAccess( mpAcc ), mpAcc = NULL;
199 eReadState = XPMREAD_ERROR;
202 else
204 mrIStm.ResetError();
205 eReadState = XPMREAD_NEED_MORE;
207 return eReadState;
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();
219 if ( bStatus )
221 for ( sal_uLong i = 0; i < mnCpp; i++ )
222 *pPtr++ = *pString++;
223 bStatus = ImplGetColSub ( pPtr );
225 return bStatus;
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;
235 sal_uInt8* pColor;
236 BitmapColor aWhite;
237 BitmapColor aBlack;
239 if ( bStatus )
241 if ( mpMaskAcc )
243 aWhite = mpMaskAcc->GetBestMatchingColor( Color( COL_WHITE ) );
244 aBlack = mpMaskAcc->GetBestMatchingColor( Color( COL_BLACK ) );
246 if ( mnStringSize != ( mnWidth * mnCpp ))
247 bStatus = false;
248 else
250 sal_uLong i, j;
251 if ( mpFastColorTable )
253 for ( i = 0; i < mnWidth; i++ )
255 j = (*pString++) << 8;
256 j += *pString++;
257 sal_uInt8 k = (sal_uInt8)mpFastColorTable[ j ];
258 mpAcc->SetPixel( nY, i, BitmapColor( (sal_uInt8)k ) );
260 if ( mpMaskAcc )
261 mpMaskAcc->SetPixel( nY, i,
262 ( mpColMap[ k * (mnCpp + 4) + mnCpp] ) ? aWhite : aBlack );
265 else for ( i = 0; i < mnWidth; i++ )
267 pColor = mpColMap;
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] ) );
274 else
275 mpAcc->SetPixel( nY, i, BitmapColor( (sal_uInt8) j ) );
277 if ( mpMaskAcc )
278 mpMaskAcc->SetPixel( nY, i, (
279 pColor[ mnCpp ] ) ? aWhite : aBlack );
281 break;
283 pColor += ( mnCpp + 4 );
285 pString += mnCpp;
290 return bStatus;
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 == '#' )
308 *pDest++ = 0;
309 bColStatus = true;
310 switch ( mnParaSize )
312 case 25 :
313 ImplGetRGBHex ( pDest, 6 );
314 break;
315 case 13 :
316 ImplGetRGBHex ( pDest, 2 );
317 break;
318 case 7 :
319 ImplGetRGBHex ( pDest, 0 );
320 break;
321 default:
322 bColStatus = false;
323 break;
326 // maybe pixel is transparent
327 else if ( ImplCompare( &cTransparent[0], mpPara, 4 ))
329 *pDest++ = 0xff;
330 bColStatus = true;
331 mbTransparent = true;
333 // last we will try to get the colorname
334 else if ( mnParaSize > 2 ) // name must enlarge the minimum size
336 sal_uLong i = 0;
337 while ( true )
339 if ( pRGBTable[ i ].name == NULL )
340 break;
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 ) )
347 bColStatus = true;
348 *pDest++ = 0;
349 *pDest++ = pRGBTable[ i ].red;
350 *pDest++ = pRGBTable[ i ].green;
351 *pDest++ = pRGBTable[ i ].blue;
354 i++;
358 return bColStatus;
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;
369 mnParaSize = 0;
371 while ( *mpPara != 0 )
373 if ( *mpPara == nKey )
375 nTemp = *( mpPara + 1 );
376 if ( nTemp == ' ' || nTemp == 0x09 )
378 if ( nPrev == ' ' || nPrev == 0x09 )
379 break;
382 nPrev = *mpPara;
383 mpPara++;
385 if ( *mpPara )
387 mpPara++;
388 while ( (*mpPara == ' ') || (*mpPara == 0x09) )
390 mpPara++;
392 if ( *mpPara != 0 )
394 while ( *(mpPara+mnParaSize) != ' ' && *(mpPara+mnParaSize) != 0x09 &&
395 *(mpPara+mnParaSize) != 0 )
397 mnParaSize++;
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';
419 if ( nHex > 9 )
420 nHex = ((nHex - 'A' + '0') & 7) + 10;
422 nTemp = (*pPtr++) - '0';
423 if ( nTemp > 9 )
424 nTemp = ((nTemp - 'A' + '0') & 7) + 10;
425 nHex = ( nHex << 4 ) + nTemp;
427 pPtr += nAdd;
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
446 nRetValue*=10;
447 nRetValue+=j;
449 return nRetValue;
451 else return 0;
454 bool XPMReader::ImplCompare( sal_uInt8 const * pSource, sal_uInt8 const * pDest, sal_uLong nSize, sal_uLong nMode )
456 bool bRet = true;
458 if ( nMode == XPMCASENONSENSITIVE )
460 for ( sal_uLong i = 0; i < nSize; i++ )
462 if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
464 bRet = false;
465 break;
469 else
471 for ( sal_uLong i = 0; i < nSize; i++ )
473 if ( pSource[i] != pDest[i] )
475 bRet = false;
476 break;
480 return bRet;
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
486 // are set.
488 bool XPMReader::ImplGetPara ( sal_uLong nNumb )
490 sal_uInt8 nByte;
491 sal_uLong pSize = 0;
492 sal_uInt8* pPtr = mpStringBuf;
493 sal_uLong nCount = 0;
495 if ( ( *pPtr != ' ' ) && ( *pPtr != 0x09 ) )
497 mpPara = pPtr;
498 mnParaSize = 0;
499 nCount = 0;
501 else
503 mpPara = NULL;
504 nCount = 0xffffffff;
507 while ( pSize < mnStringSize )
509 nByte = *pPtr;
511 if ( mpPara )
513 if ( ( nByte == ' ' ) || ( nByte == 0x09 ) )
515 if ( nCount == nNumb )
516 break;
517 else
518 mpPara = NULL;
520 else
521 mnParaSize++;
523 else
525 if ( ( nByte != ' ' ) && ( nByte != 0x09 ) )
527 mpPara = pPtr;
528 mnParaSize = 1;
529 nCount++;
532 pSize++;
533 pPtr++;
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;
547 mnStringSize = 0;
548 mpStringBuf[0] = 0;
550 while( mbStatus && ( mnStatus != XPMFINISHED ) )
552 if ( mnTempAvail == 0 )
554 mnTempAvail = mrIStm.Read( mpTempBuf, XPMTEMPBUFSIZE );
555 if ( mnTempAvail == 0 )
556 break;
558 mpTempPtr = mpTempBuf;
560 if ( mnIdentifier == XPMIDENTIFIER )
562 if ( mnTempAvail <= 50 )
564 mbStatus = false; // file is too short to be a correct XPM format
565 break;
567 for ( int i = 0; i < 9; i++ ) // searching for "/* XPM */"
568 if ( *mpTempPtr++ != sID[i] )
570 mbStatus = false;
571 break;
573 mnTempAvail-=9;
574 mnIdentifier++;
577 mcLastByte = mcThisByte;
578 mcThisByte = *mpTempPtr++;
579 mnTempAvail--;
581 if ( mnStatus & XPMDOUBLE )
583 if ( mcThisByte == 0x0a )
584 mnStatus &=~XPMDOUBLE;
585 continue;
587 if ( mnStatus & XPMREMARK )
589 if ( ( mcThisByte == '/' ) && ( mcLastByte == '*' ) )
590 mnStatus &=~XPMREMARK;
591 continue;
593 if ( mnStatus & XPMSTRING ) // characters in string
595 if ( mcThisByte == '"' )
597 mnStatus &=~XPMSTRING; // end of parameter by eol
598 break;
600 if ( mnStringSize >= ( XPMSTRINGBUF - 1 ) )
602 mbStatus = false;
603 break;
605 *pString++ = mcThisByte;
606 pString[0] = 0;
607 mnStringSize++;
608 continue;
610 else
611 { // characters beside string
612 switch ( mcThisByte )
614 case '*' :
615 if ( mcLastByte == '/' ) mnStatus |= XPMREMARK;
616 break;
617 case '/' :
618 if ( mcLastByte == '/' ) mnStatus |= XPMDOUBLE;
619 break;
620 case '"' : mnStatus |= XPMSTRING;
621 break;
622 case '{' :
623 if ( mnIdentifier == XPMDEFINITION )
624 mnIdentifier++;
625 break;
626 case '}' :
627 if ( mnIdentifier == XPMENDEXT )
628 mnStatus = XPMFINISHED;
629 break;
633 return mbStatus;
636 // - ImportXPM -
638 bool ImportXPM( SvStream& rStm, Graphic& rGraphic )
640 XPMReader* pXPMReader = static_cast<XPMReader*>(rGraphic.GetContext());
641 ReadState eReadState;
642 bool bRet = true;
644 if( !pXPMReader )
645 pXPMReader = new XPMReader( rStm );
647 rGraphic.SetContext( NULL );
648 eReadState = pXPMReader->ReadXPM( rGraphic );
650 if( eReadState == XPMREAD_ERROR )
652 bRet = false;
653 delete pXPMReader;
655 else if( eReadState == XPMREAD_OK )
656 delete pXPMReader;
657 else
658 rGraphic.SetContext( pXPMReader );
660 return bRet;
663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */