/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_svtools.hxx" #ifndef _BMPACC_HXX #include #endif #ifndef _GRAPH_HXX #include #endif #include "rgbtable.hxx" #define _XPMPRIVATE #include "xpmread.hxx" // ------------- // - XPMReader - // ------------- XPMReader::XPMReader( SvStream& rStm ) : mrIStm ( rStm ), mpAcc ( NULL ), mpMaskAcc ( NULL ), mnLastPos ( rStm.Tell() ), mnWidth ( 0 ), mnHeight ( 0 ), mnColors ( 0 ), mnCpp ( 0 ), mbTransparent ( sal_False ), mbStatus ( sal_True ), mnStatus ( 0 ), mnIdentifier ( XPMIDENTIFIER ), mcThisByte ( 0 ), mnTempAvail ( 0 ), mpFastColorTable( NULL ), mpColMap ( NULL ) { } // ------------------------------------------------------------------------ XPMReader::~XPMReader() { if( mpAcc ) maBmp.ReleaseAccess( mpAcc ); } // ------------------------------------------------------------------------ #ifdef _MSC_VER #pragma optimize ("",off) #endif ReadState XPMReader::ReadXPM( Graphic& rGraphic ) { ReadState eReadState; sal_uInt8 cDummy; // sehen, ob wir _alles_ lesen koennen mrIStm.Seek( STREAM_SEEK_TO_END ); mrIStm >> cDummy; // falls wir nicht alles lesen koennen // kehren wir zurueck und warten auf neue Daten if ( mrIStm.GetError() != ERRCODE_IO_PENDING ) { mrIStm.Seek( mnLastPos ); mbStatus = sal_True; if ( mbStatus ) { mpStringBuf = new sal_uInt8 [ XPMSTRINGBUF ]; mpTempBuf = new sal_uInt8 [ XPMTEMPBUFSIZE ]; if ( ( mbStatus = ImplGetString() ) == sal_True ) { mnIdentifier = XPMVALUES; // Bitmap informationen einholen mnWidth = ImplGetULONG( 0 ); mnHeight = ImplGetULONG( 1 ); mnColors = ImplGetULONG( 2 ); mnCpp = ImplGetULONG( 3 ); } if ( mnColors > ( SAL_MAX_UINT32 / ( 4 + mnCpp ) ) ) mbStatus = sal_False; if ( ( mnWidth * mnCpp ) >= XPMSTRINGBUF ) mbStatus = sal_False; if ( mbStatus && mnWidth && mnHeight && mnColors && mnCpp ) { mnIdentifier = XPMCOLORS; // mpColMap beinhaltet fuer jede vorhandene // Farbe: ( mnCpp )Byte(s)-> ASCII Eintrag der der Farbe zugeordnet ist // 1 Byte -> 0xff wenn Farbe transparent ist // 3 Bytes -> RGB Wert der Farbe mpColMap = new sal_uInt8[ mnColors * ( 4 + mnCpp ) ]; if ( mpColMap ) { for ( sal_uLong i = 0; i < mnColors; i++ ) { if ( ImplGetColor( i ) == sal_False ) { mbStatus = sal_False; break; } } } else mbStatus = sal_False; if ( mbStatus ) { // bei mehr als 256 Farben wird eine 24 Bit Grafik erstellt sal_uInt16 nBits = 1; if ( mnColors > 256 ) nBits = 24; else if ( mnColors > 16 ) nBits = 8; else if ( mnColors > 2 ) nBits = 4; else nBits = 1; maBmp = Bitmap( Size( mnWidth, mnHeight ), nBits ); mpAcc = maBmp.AcquireWriteAccess(); // mbTransparent ist sal_True wenn mindestens eine Farbe Transparent ist if ( mbTransparent ) { maMaskBmp = Bitmap( Size( mnWidth, mnHeight ), 1 ); if ( ( mpMaskAcc = maMaskBmp.AcquireWriteAccess() ) == NULL ) mbStatus = sal_False; } if( mpAcc && mbStatus ) { sal_uLong i; if ( mnColors <=256 ) // palette is only needed by using less than 257 { // colors sal_uInt8* pPtr = &mpColMap[mnCpp]; for ( i = 0; i < mnColors; i++ ) { mpAcc->SetPaletteColor( (sal_uInt8)i, Color( pPtr[1], pPtr[2], pPtr[3] ) ); pPtr += ( mnCpp + 4 ); } // using 2 charakters per pixel and less than 257 Colors we speed up if ( mnCpp == 2 ) // by using a 64kb indexing table { mpFastColorTable = new sal_uInt8[ 256 * 256 ]; for ( pPtr = mpColMap, i = 0; i < mnColors; i++, pPtr += mnCpp + 4 ) { sal_uLong j = pPtr[ 0 ] << 8; j += pPtr[ 1 ]; mpFastColorTable[ j ] = (sal_uInt8)i; } } } // now we get the bitmap data mnIdentifier = XPMPIXELS; for ( i = 0; i < mnHeight; i++ ) { if ( ImplGetScanLine( i ) == sal_False ) { mbStatus = sal_False; break; } } mnIdentifier = XPMEXTENSIONS; } } } delete[] mpFastColorTable; delete[] mpColMap; delete[] mpStringBuf; delete[] mpTempBuf; } if( mbStatus ) { if ( mpMaskAcc ) { maMaskBmp.ReleaseAccess ( mpMaskAcc), mpMaskAcc = NULL; maBmp.ReleaseAccess( mpAcc ), mpAcc = NULL; rGraphic = Graphic( BitmapEx( maBmp, maMaskBmp ) ); } else { maBmp.ReleaseAccess( mpAcc ), mpAcc = NULL; rGraphic = maBmp; } eReadState = XPMREAD_OK; } else { if ( mpMaskAcc ) maMaskBmp.ReleaseAccess ( mpMaskAcc), mpMaskAcc = NULL; if ( mpAcc ) maBmp.ReleaseAccess( mpAcc ), mpAcc = NULL; eReadState = XPMREAD_ERROR; } } else { mrIStm.ResetError(); eReadState = XPMREAD_NEED_MORE; } return eReadState; } #ifdef _MSC_VER #pragma optimize ("",on) #endif // ------------------------------------------------------------------------ // ImplGetColor ermittelt saemtliche Farbwerte, // die Rueckgabe ist sal_True wenn saemtliche Farben zugeordnet werden konnten sal_Bool XPMReader::ImplGetColor( sal_uLong nNumb ) { sal_uInt8* pString = mpStringBuf; sal_uInt8* pPtr = ( mpColMap + nNumb * ( 4 + mnCpp ) ); sal_Bool bStatus = ImplGetString(); if ( bStatus ) { for ( sal_uLong i = 0; i < mnCpp; i++ ) *pPtr++ = *pString++; bStatus = ImplGetColSub ( pPtr ); } return bStatus; } // ------------------------------------------------------------------------ // ImpGetScanLine liest den String mpBufSize aus und schreibt die Pixel in die // Bitmap. Der Parameter nY gibt die horizontale Position an. sal_Bool XPMReader::ImplGetScanLine( sal_uLong nY ) { sal_Bool bStatus = ImplGetString(); sal_uInt8* pString = mpStringBuf; sal_uInt8* pColor; BitmapColor aWhite; BitmapColor aBlack; if ( bStatus ) { if ( mpMaskAcc ) { aWhite = mpMaskAcc->GetBestMatchingColor( Color( COL_WHITE ) ); aBlack = mpMaskAcc->GetBestMatchingColor( Color( COL_BLACK ) ); } if ( mnStringSize != ( mnWidth * mnCpp )) bStatus = sal_False; else { sal_uLong i, j; if ( mpFastColorTable ) { for ( i = 0; i < mnWidth; i++ ) { j = (*pString++) << 8; j += *pString++; sal_uInt8 k = (sal_uInt8)mpFastColorTable[ j ]; mpAcc->SetPixel( nY, i, BitmapColor( (sal_uInt8)k ) ); if ( mpMaskAcc ) mpMaskAcc->SetPixel( nY, i, ( mpColMap[ k * (mnCpp + 4) + mnCpp] ) ? aWhite : aBlack ); } } else for ( i = 0; i < mnWidth; i++ ) { pColor = mpColMap; for ( j = 0; j < mnColors; j++ ) { if ( ImplCompare( pString, pColor, mnCpp, XPMCASESENSITIVE ) == sal_True ) { if ( mnColors > 256 ) mpAcc->SetPixel( nY, i, Color ( pColor[3], pColor[4], pColor[5] ) ); else mpAcc->SetPixel( nY, i, BitmapColor( (sal_uInt8) j ) ); if ( mpMaskAcc ) mpMaskAcc->SetPixel( nY, i, ( pColor[ mnCpp ] ) ? aWhite : aBlack ); break; } pColor += ( mnCpp + 4 ); } pString += mnCpp; } } } return bStatus; } // ------------------------------------------------------------------------ // versucht aus mpStringBuf einen Farbwert zu uebermitteln // wurde eine Farbe gefunden wird an pDest[1]..pDest[2] der RGB wert geschrieben // pDest[0] enthaelt 0xff wenn die Farbe transparent ist sonst 0 sal_Bool XPMReader::ImplGetColSub( sal_uInt8* pDest ) { unsigned char cTransparent[] = "None"; sal_Bool bColStatus = sal_False; if ( ImplGetColKey( 'c' ) || ImplGetColKey( 'm' ) || ImplGetColKey( 'g' ) ) { // hexentry for RGB or HSV color ? if ( *mpPara == '#' ) { *pDest++ = 0; bColStatus = sal_True; switch ( mnParaSize ) { case 25 : ImplGetRGBHex ( pDest, 6 ); break; case 13 : ImplGetRGBHex ( pDest, 2 ); break; case 7 : ImplGetRGBHex ( pDest, 0 ); break; default: bColStatus = sal_False; break; } } // maybe pixel is transparent else if ( ImplCompare( &cTransparent[0], mpPara, 4 )) { *pDest++ = 0xff; bColStatus = sal_True; mbTransparent = sal_True; } // last we will try to get the colorname else if ( mnParaSize > 2 ) // name must enlarge the minimum size { sal_uLong i = 0; while ( sal_True ) { if ( pRGBTable[ i ].name == NULL ) break; if ( pRGBTable[ i ].name[ mnParaSize ] == 0 ) { if ( ImplCompare ( (unsigned char*)pRGBTable[ i ].name, mpPara, mnParaSize, XPMCASENONSENSITIVE ) ) { bColStatus = sal_True; *pDest++ = 0; *pDest++ = pRGBTable[ i ].red; *pDest++ = pRGBTable[ i ].green; *pDest++ = pRGBTable[ i ].blue; } } i++; } } } return bColStatus; } // ------------------------------------------------------------------------ // ImplGetColKey durchsuch den String mpStringBuf nach einem Parameter 'nKey' // und gibt einen sal_Bool zurueck. ( wenn sal_True werden mpPara und mnParaSize gesetzt ) sal_Bool XPMReader::ImplGetColKey( sal_uInt8 nKey ) { sal_uInt8 nTemp, nPrev = ' '; mpPara = mpStringBuf + mnCpp + 1; mnParaSize = 0; while ( *mpPara != 0 ) { if ( *mpPara == nKey ) { nTemp = *( mpPara + 1 ); if ( nTemp == ' ' || nTemp == 0x09 ) { if ( nPrev == ' ' || nPrev == 0x09 ) break; } } nPrev = *mpPara; mpPara++; } if ( *mpPara ) { mpPara++; while ( (*mpPara == ' ') || (*mpPara == 0x09) ) { mpPara++; } if ( *mpPara != 0 ) { while ( *(mpPara+mnParaSize) != ' ' && *(mpPara+mnParaSize) != 0x09 && *(mpPara+mnParaSize) != 0 ) { mnParaSize++; } } } return ( mnParaSize ) ? sal_True : sal_False; } // ------------------------------------------------------------------------ // ImplGetRGBHex uebersetzt den ASCII-Hexadezimalwert der sich bei mpPara befindet // in einen RGB wert und schreibt diesen nach pDest // folgende Formate muessen sich bei mpPara befinden: // wenn nAdd = 0 : '#12ab12' -> RGB = 0x12, 0xab, 0x12 // 2 : '#1234abcd1234' " " " " // 6 : '#12345678abcdefab12345678' " " " " void XPMReader::ImplGetRGBHex( sal_uInt8* pDest,sal_uLong nAdd ) { sal_uInt8* pPtr = mpPara+1; sal_uInt8 nHex, nTemp; for ( sal_uLong i = 0; i < 3; i++ ) { nHex = (*pPtr++) - '0'; if ( nHex > 9 ) nHex = ((nHex - 'A' + '0') & 7) + 10; nTemp = (*pPtr++) - '0'; if ( nTemp > 9 ) nTemp = ((nTemp - 'A' + '0') & 7) + 10; nHex = ( nHex << 4 ) + nTemp; pPtr += nAdd; *pDest++ = (sal_uInt8)nHex; } } // ------------------------------------------------------------------------ // ImplGetUlong gibt den wert einer bis zu 6stelligen ASCII-Dezimalzahl zurueck. sal_uLong XPMReader::ImplGetULONG( sal_uLong nPara ) { if ( ImplGetPara ( nPara ) ) { sal_uLong nRetValue = 0; sal_uInt8* pPtr = mpPara; if ( ( mnParaSize > 6 ) || ( mnParaSize == 0 ) ) return 0; for ( sal_uLong i = 0; i < mnParaSize; i++ ) { sal_uInt8 j = (*pPtr++) - 48; if ( j > 9 ) return 0; // ascii is invalid nRetValue*=10; nRetValue+=j; } return nRetValue; } else return 0; } // ------------------------------------------------------------------------ sal_Bool XPMReader::ImplCompare( sal_uInt8* pSource, sal_uInt8* pDest, sal_uLong nSize, sal_uLong nMode ) { sal_Bool bRet = sal_True; if ( nMode == XPMCASENONSENSITIVE ) { for ( sal_uLong i = 0; i < nSize; i++ ) { if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) ) { bRet = sal_False; break; } } } else { for ( sal_uLong i = 0; i < nSize; i++ ) { if ( pSource[i] != pDest[i] ) { bRet = sal_False; break; } } } return bRet; } // ------------------------------------------------------------------------ // ImplGetPara versucht den nNumb ( 0...x ) Parameter aus mpStringBuf zu ermitteln. // Ein Parameter ist durch Spaces oder Tabs von den anderen getrennt. // Konnte der Parameter gefunden werden ist der Rueckgabewert sal_True und mpPara + mnParaSize // werden gesetzt. sal_Bool XPMReader::ImplGetPara ( sal_uLong nNumb ) { sal_uInt8 nByte; sal_uLong pSize = 0; sal_uInt8* pPtr = mpStringBuf; sal_uLong nCount = 0; if ( ( *pPtr != ' ' ) && ( *pPtr != 0x09 ) ) { mpPara = pPtr; mnParaSize = 0; nCount = 0; } else { mpPara = NULL; nCount = 0xffffffff; } while ( pSize < mnStringSize ) { nByte = *pPtr; if ( mpPara ) { if ( ( nByte == ' ' ) || ( nByte == 0x09 ) ) { if ( nCount == nNumb ) break; else mpPara = NULL; } else mnParaSize++; } else { if ( ( nByte != ' ' ) && ( nByte != 0x09 ) ) { mpPara = pPtr; mnParaSize = 1; nCount++; } } pSize++; pPtr++; } return ( ( nCount == nNumb ) && ( mpPara ) ) ? sal_True : sal_False; } // ------------------------------------------------------------------------ // Der naechste String wird ausgelesen und in mpStringBuf (mit 0 abgeschlossen) abgelegt; // mnStringSize enthaelt die Groesse des gelesenen Strings. // Bemerkungen wie '//' und '/*.....*/' werden uebersprungen. sal_Bool XPMReader::ImplGetString( void ) { sal_uInt8 sID[] = "/* XPM */"; sal_uInt8* pString = mpStringBuf; mnStringSize = 0; mpStringBuf[0] = 0; while( mbStatus && ( mnStatus != XPMFINISHED ) ) { if ( mnTempAvail == 0 ) { mnTempAvail = mrIStm.Read( mpTempBuf, XPMTEMPBUFSIZE ); if ( mnTempAvail == 0 ) break; mpTempPtr = mpTempBuf; if ( mnIdentifier == XPMIDENTIFIER ) { if ( mnTempAvail <= 50 ) { mbStatus = sal_False; // file is too short to be a correct XPM format break; } for ( int i = 0; i < 9; i++ ) // searching for "/* XPM */" if ( *mpTempPtr++ != sID[i] ) { mbStatus = sal_False; break; } mnTempAvail-=9; mnIdentifier++; } } mcLastByte = mcThisByte; mcThisByte = *mpTempPtr++; mnTempAvail--; if ( mnStatus & XPMDOUBLE ) { if ( mcThisByte == 0x0a ) mnStatus &=~XPMDOUBLE; continue; } if ( mnStatus & XPMREMARK ) { if ( ( mcThisByte == '/' ) && ( mcLastByte == '*' ) ) mnStatus &=~XPMREMARK; continue; } if ( mnStatus & XPMSTRING ) // characters in string { if ( mcThisByte == '"' ) { mnStatus &=~XPMSTRING; // end of parameter by eol break; } if ( mnStringSize >= ( XPMSTRINGBUF - 1 ) ) { mbStatus = sal_False; break; } *pString++ = mcThisByte; pString[0] = 0; mnStringSize++; continue; } else { // characters beside string switch ( mcThisByte ) { case '*' : if ( mcLastByte == '/' ) mnStatus |= XPMREMARK; break; case '/' : if ( mcLastByte == '/' ) mnStatus |= XPMDOUBLE; break; case '"' : mnStatus |= XPMSTRING; break; case '{' : if ( mnIdentifier == XPMDEFINITION ) mnIdentifier++; break; case '}' : if ( mnIdentifier == XPMENDEXT ) mnStatus = XPMFINISHED; break; } } } return mbStatus; } // ------------- // - ImportXPM - // ------------- sal_Bool ImportXPM( SvStream& rStm, Graphic& rGraphic ) { XPMReader* pXPMReader = (XPMReader*) rGraphic.GetContext(); ReadState eReadState; sal_Bool bRet = sal_True; if( !pXPMReader ) pXPMReader = new XPMReader( rStm ); rGraphic.SetContext( NULL ); eReadState = pXPMReader->ReadXPM( rGraphic ); if( eReadState == XPMREAD_ERROR ) { bRet = sal_False; delete pXPMReader; } else if( eReadState == XPMREAD_OK ) delete pXPMReader; else rGraphic.SetContext( pXPMReader ); return bRet; }