blob: 1380d1babf18f1b5eb3dae24424654490991d684 [file] [log] [blame]
/**************************************************************
*
* 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 <vcl/bmpacc.hxx>
#endif
#ifndef _GRAPH_HXX
#include <vcl/graph.hxx>
#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;
}