mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-04-07 13:55:33 +08:00
1867 lines
41 KiB
C++
1867 lines
41 KiB
C++
#include "stdafx.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include "MemoryUtils.h"
|
|
#include "Object.h"
|
|
#include "Dict.h"
|
|
#include "Stream.h"
|
|
#include "Function.h"
|
|
|
|
//------------------------------------------------------------------------
|
|
// Function
|
|
//------------------------------------------------------------------------
|
|
|
|
Function::Function()
|
|
{
|
|
}
|
|
|
|
Function::~Function()
|
|
{
|
|
}
|
|
|
|
Function *Function::Parse(Object *pFuncObject)
|
|
{
|
|
Dict *pDict;
|
|
|
|
// Ôóíêöèÿ çàäàåòñÿ ëèáî êàê ñëîâàðü, ëèáî ïîòîê, ëèáî êàê òîæäåñòâåííàÿ ôóíêöèÿ
|
|
if ( pFuncObject->IsStream() )
|
|
{
|
|
pDict = pFuncObject->StreamGetDict();
|
|
}
|
|
else if ( pFuncObject->IsDict() )
|
|
{
|
|
pDict = pFuncObject->GetDict();
|
|
}
|
|
else if ( pFuncObject->IsName("Identity") )
|
|
{
|
|
return new IdentityFunction();
|
|
}
|
|
else
|
|
{
|
|
// TO DO: Error "Expected function dictionary or stream"
|
|
return NULL;
|
|
}
|
|
|
|
Object oTemp;
|
|
if ( !pDict->Search("FunctionType", &oTemp)->IsInt() )
|
|
{
|
|
// TO DO: Error "Function type is missing or wrong type"
|
|
oTemp.Free();
|
|
return NULL;
|
|
}
|
|
int nFuncType = oTemp.GetInt();
|
|
oTemp.Free();
|
|
|
|
Function *pFunc = NULL;
|
|
if ( 0 == nFuncType ) // Sampled function
|
|
{
|
|
pFunc = new SampledFunction( pFuncObject, pDict);
|
|
}
|
|
else if ( 2 == nFuncType ) // Exponential interpolation function
|
|
{
|
|
pFunc = new ExponentialFunction( pFuncObject, pDict);
|
|
}
|
|
else if ( 3 == nFuncType ) // Stitching function
|
|
{
|
|
pFunc = new StitchingFunction( pFuncObject, pDict);
|
|
}
|
|
else if ( 4 == nFuncType ) // PostScript calculator function
|
|
{
|
|
pFunc = new PostScriptFunction( pFuncObject, pDict);
|
|
}
|
|
else
|
|
{
|
|
// TO DO: Error "Unimplemented function type"
|
|
return NULL;
|
|
}
|
|
if ( !pFunc->IsValid() )
|
|
{
|
|
delete pFunc;
|
|
return NULL;
|
|
}
|
|
|
|
return pFunc;
|
|
}
|
|
|
|
BOOL Function::Initialize(Dict *pDict)
|
|
{
|
|
Object oArray;
|
|
// Domain (Âñåãäà äîëæåí ïðèñóòñòâîâàòü)
|
|
if ( !pDict->Search("Domain", &oArray)->IsArray() )
|
|
{
|
|
// TO DO: Error "Function is missing domain"
|
|
oArray.Free();
|
|
return FALSE;
|
|
}
|
|
m_nInputDim = oArray.ArrayGetLength() / 2;
|
|
|
|
if ( m_nInputDim > funcMaxInputs )
|
|
{
|
|
// TO DO: Error "Functions with more than funcMaxInputs inputs are unsupported"
|
|
oArray.Free();
|
|
return FALSE;
|
|
}
|
|
for ( int nIndex = 0; nIndex < m_nInputDim; ++nIndex )
|
|
{
|
|
Object oTemp;
|
|
oArray.ArrayGet(2 * nIndex, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function domain array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return FALSE;
|
|
}
|
|
m_arrDomain[nIndex][0] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
oArray.ArrayGet(2 * nIndex + 1, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function domain array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return FALSE;
|
|
}
|
|
m_arrDomain[nIndex][1] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
}
|
|
oArray.Free();
|
|
|
|
// Range (Ýòîãî ïîëÿ ìîæåò è íå áûòü)
|
|
m_bHasRange = FALSE;
|
|
m_nOutputDim = 0;
|
|
if ( pDict->Search("Range", &oArray)->IsArray() )
|
|
{
|
|
m_bHasRange = TRUE;
|
|
m_nOutputDim = oArray.ArrayGetLength() / 2;
|
|
if ( m_nOutputDim > funcMaxOutputs )
|
|
{
|
|
// TO DO: Error "Functions with more than funcMaxOutputs outputs are unsupported"
|
|
oArray.Free();
|
|
return FALSE;
|
|
}
|
|
for ( int nIndex = 0; nIndex < m_nOutputDim; ++nIndex)
|
|
{
|
|
Object oTemp;
|
|
oArray.ArrayGet(2 * nIndex, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function range array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return FALSE;
|
|
}
|
|
m_arrRange[nIndex][0] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
oArray.ArrayGet(2 * nIndex + 1, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function range array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return FALSE;
|
|
}
|
|
m_arrRange[nIndex][1] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
}
|
|
}
|
|
oArray.Free();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// IdentityFunction
|
|
//------------------------------------------------------------------------
|
|
|
|
IdentityFunction::IdentityFunction()
|
|
{
|
|
// Çàïîëíÿåì ïðîèçâîëüíûìè çíà÷åíèÿìè
|
|
m_nInputDim = funcMaxInputs;
|
|
m_nOutputDim = funcMaxOutputs;
|
|
|
|
for ( int nIndex = 0; nIndex < funcMaxInputs; ++nIndex)
|
|
{
|
|
m_arrDomain[nIndex][0] = 0;
|
|
m_arrDomain[nIndex][1] = 1;
|
|
}
|
|
m_bHasRange = FALSE;
|
|
}
|
|
|
|
IdentityFunction::~IdentityFunction()
|
|
{
|
|
}
|
|
|
|
void IdentityFunction::Transform(double *pInput, double *pOutput)
|
|
{
|
|
for ( int nIndex = 0; nIndex < funcMaxOutputs; ++nIndex)
|
|
{
|
|
pOutput[nIndex] = pInput[nIndex]; // òîæäåñòâåííàÿ æå ôóíêöèÿ :)
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// SampledFunction
|
|
//------------------------------------------------------------------------
|
|
|
|
SampledFunction::SampledFunction(Object *pFuncObject, Dict *pDict)
|
|
{
|
|
m_pSamples = NULL;
|
|
m_pBuffer = NULL;
|
|
m_bValid = FALSE;
|
|
|
|
if ( !Initialize(pDict) )
|
|
{
|
|
return;
|
|
}
|
|
if ( !m_bHasRange )
|
|
{
|
|
// TO DO: Error "Type 0 function is missing range"
|
|
return;
|
|
}
|
|
if ( m_nInputDim > sampledFuncMaxInputs)
|
|
{
|
|
// TO DO: Error "Sampled functions with more than sampledFuncMaxInputs inputs are unsupported"
|
|
return;
|
|
}
|
|
|
|
m_pBuffer = (double *)MemUtilsMallocArray( 1 << m_nInputDim, sizeof(double) );
|
|
|
|
if ( !pFuncObject->IsStream() )
|
|
{
|
|
// TO DO: Error "Type 0 function isn't a stream"
|
|
return;
|
|
}
|
|
Stream *pStream = pFuncObject->GetStream();
|
|
|
|
// Size
|
|
Object oArray;
|
|
if ( !pDict->Search("Size", &oArray)->IsArray() || oArray.ArrayGetLength() != m_nInputDim )
|
|
{
|
|
// TO DO: Error "Function has missing or invalid size array"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
for ( int nIndex = 0; nIndex < m_nInputDim; ++nIndex )
|
|
{
|
|
Object oTemp;
|
|
oArray.ArrayGet( nIndex, &oTemp);
|
|
if ( !oTemp.IsInt() )
|
|
{
|
|
// TO DO: Error "Illegal value in function size array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrSize[nIndex] = oTemp.GetInt();
|
|
oTemp.Free();
|
|
}
|
|
oArray.Free();
|
|
|
|
m_arrIndexMult[0] = m_nOutputDim;
|
|
for ( int nIndex = 1; nIndex < m_nInputDim; ++nIndex )
|
|
{
|
|
m_arrIndexMult[nIndex] = m_arrIndexMult[nIndex - 1] * m_arrSize[nIndex - 1];
|
|
}
|
|
|
|
// BitsPerSample
|
|
if ( !pDict->Search("BitsPerSample", &oArray)->IsInt() )
|
|
{
|
|
// TO DO: Error "Function has missing or invalid BitsPerSample"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
int nSampleBits = oArray.GetInt();
|
|
double dSampleMult = 1.0 / (pow(2.0, (double)nSampleBits) - 1);
|
|
oArray.Free();
|
|
|
|
// TO DO: Ñäåëàòü ÷òåíèå ïîëÿ Order, è ðåàëèçîâàòü ôóíêöèþ Transform
|
|
|
|
// Encode
|
|
if ( pDict->Search("Encode", &oArray)->IsArray() && oArray.ArrayGetLength() == 2 * m_nInputDim )
|
|
{
|
|
for ( int nIndex = 0; nIndex < m_nInputDim; ++nIndex)
|
|
{
|
|
Object oTemp;
|
|
oArray.ArrayGet(2 * nIndex, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function encode array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrEncoder[nIndex][0] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
|
|
oArray.ArrayGet(2 * nIndex + 1, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function encode array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrEncoder[nIndex][1] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( int nIndex = 0; nIndex < m_nInputDim; ++nIndex)
|
|
{
|
|
m_arrEncoder[nIndex][0] = 0;
|
|
m_arrEncoder[nIndex][1] = m_arrSize[nIndex] - 1;
|
|
}
|
|
}
|
|
oArray.Free();
|
|
|
|
for ( int nIndex = 0; nIndex < m_nInputDim; ++nIndex )
|
|
{
|
|
m_arrInputMult[nIndex] = ( m_arrEncoder[nIndex][1] - m_arrEncoder[nIndex][0] ) / ( m_arrDomain[nIndex][1] - m_arrDomain[nIndex][0] );
|
|
}
|
|
|
|
// Decode
|
|
if ( pDict->Search("Decode", &oArray)->IsArray() && oArray.ArrayGetLength() == 2 * m_nOutputDim )
|
|
{
|
|
for ( int nIndex = 0; nIndex < m_nOutputDim; ++nIndex )
|
|
{
|
|
Object oTemp;
|
|
oArray.ArrayGet(2 * nIndex, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function decode array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrDecoder[nIndex][0] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
|
|
oArray.ArrayGet(2 * nIndex + 1, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function decode array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrDecoder[nIndex][1] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( int nIndex = 0; nIndex < m_nOutputDim; ++nIndex )
|
|
{
|
|
m_arrDecoder[nIndex][0] = m_arrRange[nIndex][0];
|
|
m_arrDecoder[nIndex][1] = m_arrRange[nIndex][1];
|
|
}
|
|
}
|
|
oArray.Free();
|
|
|
|
// Samples
|
|
m_nSamplesCount = m_nOutputDim;
|
|
for ( int nIndex = 0; nIndex < m_nInputDim; ++nIndex )
|
|
m_nSamplesCount *= m_arrSize[nIndex];
|
|
m_pSamples = (double *)MemUtilsMallocArray( m_nSamplesCount, sizeof(double));
|
|
|
|
unsigned int unBuf = 0;
|
|
int nBitsCount = 0;
|
|
unsigned int unBitMask = (1 << nSampleBits) - 1;
|
|
pStream->Reset();
|
|
for (int nIndex = 0; nIndex < m_nSamplesCount; ++nIndex)
|
|
{
|
|
unsigned int unValue = 0;
|
|
if ( nSampleBits == 8 )
|
|
{
|
|
unValue = pStream->GetChar();
|
|
}
|
|
else if ( nSampleBits == 16 )
|
|
{
|
|
unValue = pStream->GetChar();
|
|
unValue = (unValue << 8) + pStream->GetChar();
|
|
}
|
|
else if ( nSampleBits == 32 )
|
|
{
|
|
unValue = pStream->GetChar();
|
|
unValue = (unValue << 8) + pStream->GetChar();
|
|
unValue = (unValue << 8) + pStream->GetChar();
|
|
unValue = (unValue << 8) + pStream->GetChar();
|
|
}
|
|
else
|
|
{
|
|
while ( nBitsCount < nSampleBits )
|
|
{
|
|
unBuf = (unBuf << 8) | ( pStream->GetChar() & 0xff );
|
|
nBitsCount += 8;
|
|
}
|
|
unValue = (unBuf >> (nBitsCount - nSampleBits)) & unBitMask;
|
|
nBitsCount -= nSampleBits;
|
|
}
|
|
m_pSamples[nIndex] = (double)unValue * dSampleMult;
|
|
}
|
|
pStream->Close();
|
|
|
|
m_bValid = TRUE;
|
|
return;
|
|
}
|
|
|
|
SampledFunction::~SampledFunction()
|
|
{
|
|
MemUtilsFree( m_pSamples );
|
|
MemUtilsFree( m_pBuffer );
|
|
}
|
|
|
|
SampledFunction::SampledFunction(SampledFunction *pFunc)
|
|
{
|
|
memcpy( this, pFunc, sizeof(SampledFunction));
|
|
m_pSamples = (double *)MemUtilsMallocArray( m_nSamplesCount, sizeof(double));
|
|
memcpy( m_pSamples, pFunc->m_pSamples, m_nSamplesCount * sizeof(double));
|
|
m_pBuffer = (double *)MemUtilsMallocArray(1 << m_nInputDim, sizeof(double));
|
|
}
|
|
|
|
void SampledFunction::Transform(double *pInput, double *pOutput)
|
|
{
|
|
int arrE[funcMaxInputs][2];
|
|
double arrEFrac0[funcMaxInputs];
|
|
double arrEFrac1[funcMaxInputs];
|
|
|
|
for (int nIndex = 0; nIndex < m_nInputDim; ++nIndex)
|
|
{
|
|
double dValue = ( pInput[nIndex] - m_arrDomain[nIndex][0] ) * m_arrInputMult[nIndex] + m_arrEncoder[nIndex][0];
|
|
if ( dValue < 0 )
|
|
{
|
|
dValue = 0;
|
|
}
|
|
else if ( dValue > m_arrSize[nIndex] - 1 )
|
|
{
|
|
dValue = m_arrSize[nIndex] - 1;
|
|
}
|
|
arrE[nIndex][0] = (int)dValue;
|
|
if ( ( arrE[nIndex][1] = arrE[nIndex][0] + 1 ) >= m_arrSize[nIndex] )
|
|
{
|
|
arrE[nIndex][1] = arrE[nIndex][0];
|
|
}
|
|
arrEFrac1[nIndex] = dValue - arrE[nIndex][0];
|
|
arrEFrac0[nIndex] = 1 - arrEFrac1[nIndex];
|
|
}
|
|
|
|
for ( int nI = 0; nI < m_nOutputDim; ++nI)
|
|
{
|
|
for ( int nJ = 0; nJ < (1 << m_nInputDim); ++nJ )
|
|
{
|
|
int nIdx = nI;
|
|
for ( int nK = 0, t = nJ; nK < m_nInputDim; ++nK, t >>= 1 )
|
|
{
|
|
nIdx += m_arrIndexMult[nK] * ( arrE[nK][t & 1] );
|
|
}
|
|
m_pBuffer[nJ] = m_pSamples[nIdx];
|
|
}
|
|
|
|
int nT = 0;
|
|
for ( int nJ = 0, nT = (1 << m_nInputDim); nJ < m_nInputDim; ++nJ, nT >>= 1 )
|
|
{
|
|
for ( int nK = 0; nK < nT; nK += 2)
|
|
{
|
|
m_pBuffer[nK >> 1] = arrEFrac0[nJ] * m_pBuffer[nK] + arrEFrac1[nJ] * m_pBuffer[nK + 1];
|
|
}
|
|
}
|
|
|
|
pOutput[nI] = m_pBuffer[0] * (m_arrDecoder[nI][1] - m_arrDecoder[nI][0]) + m_arrDecoder[nI][0];
|
|
if (pOutput[nI] < m_arrRange[nI][0])
|
|
{
|
|
pOutput[nI] = m_arrRange[nI][0];
|
|
}
|
|
else if (pOutput[nI] > m_arrRange[nI][1])
|
|
{
|
|
pOutput[nI] = m_arrRange[nI][1];
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// ExponentialFunction
|
|
//------------------------------------------------------------------------
|
|
|
|
ExponentialFunction::ExponentialFunction(Object *pFuncObject, Dict *pDict)
|
|
{
|
|
m_bValid = FALSE;
|
|
|
|
if ( !Initialize(pDict) )
|
|
{
|
|
return;
|
|
}
|
|
if ( m_nInputDim != 1 )
|
|
{
|
|
// TO DO: Error "Exponential function with more than one input"
|
|
return;
|
|
}
|
|
|
|
// C0
|
|
Object oArray;
|
|
if ( pDict->Search("C0", &oArray)->IsArray() )
|
|
{
|
|
if ( m_bHasRange && oArray.ArrayGetLength() != m_nOutputDim )
|
|
{
|
|
// TO DO: Error "Function's C0 array is wrong length"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_nOutputDim = oArray.ArrayGetLength();
|
|
for ( int nIndex = 0; nIndex < m_nOutputDim; ++nIndex )
|
|
{
|
|
Object oTemp;
|
|
oArray.ArrayGet( nIndex, &oTemp);
|
|
if ( !oTemp.IsNum( ))
|
|
{
|
|
// TO DO: Error "Illegal value in function C0 array"
|
|
oArray.Free();
|
|
oTemp.Free();
|
|
return;
|
|
}
|
|
m_arrC0[nIndex] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( m_bHasRange && m_nOutputDim != 1 )
|
|
{
|
|
// TO DO: Error "Function's C0 array is wrong length"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_nOutputDim = 1;
|
|
m_arrC0[0] = 0;
|
|
}
|
|
oArray.Free();
|
|
|
|
// C1
|
|
if ( pDict->Search("C1", &oArray)->IsArray() )
|
|
{
|
|
if ( oArray.ArrayGetLength() != m_nOutputDim )
|
|
{
|
|
// TO DO: Error "Function's C1 array is wrong length"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
for ( int nIndex = 0; nIndex < m_nOutputDim; ++nIndex )
|
|
{
|
|
Object oTemp;
|
|
oArray.ArrayGet( nIndex, &oTemp);
|
|
if ( !oTemp.IsNum() )
|
|
{
|
|
// TO DO: Error "Illegal value in function C1 array"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrC1[nIndex] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( m_nOutputDim != 1 )
|
|
{
|
|
// TO DO: Error "Function's C1 array is wrong length"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrC1[0] = 1;
|
|
}
|
|
oArray.Free();
|
|
|
|
// N (exponent)
|
|
if ( !pDict->Search("N", &oArray)->IsNum() )
|
|
{
|
|
// TO DO: Error "Function has missing or invalid N"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_dN = oArray.GetNum();
|
|
oArray.Free();
|
|
|
|
m_bValid = TRUE;
|
|
return;
|
|
}
|
|
|
|
ExponentialFunction::~ExponentialFunction()
|
|
{
|
|
}
|
|
|
|
ExponentialFunction::ExponentialFunction(ExponentialFunction *pFunc)
|
|
{
|
|
memcpy( this, pFunc, sizeof(ExponentialFunction));
|
|
}
|
|
|
|
void ExponentialFunction::Transform(double *pInput, double *pOutput)
|
|
{
|
|
double dX = 0;
|
|
|
|
if ( pInput[0] < m_arrDomain[0][0] )
|
|
{
|
|
dX = m_arrDomain[0][0];
|
|
}
|
|
else if ( pInput[0] > m_arrDomain[0][1] )
|
|
{
|
|
dX = m_arrDomain[0][1];
|
|
}
|
|
else
|
|
{
|
|
dX = pInput[0];
|
|
}
|
|
for ( int nIndex = 0; nIndex < m_nOutputDim; ++nIndex )
|
|
{
|
|
pOutput[nIndex] = m_arrC0[nIndex] + pow( dX, m_dN) * (m_arrC1[nIndex] - m_arrC0[nIndex]);
|
|
if ( m_bHasRange )
|
|
{
|
|
if ( pOutput[nIndex] < m_arrRange[nIndex][0] )
|
|
{
|
|
pOutput[nIndex] = m_arrRange[nIndex][0];
|
|
}
|
|
else if ( pOutput[nIndex] > m_arrRange[nIndex][1] )
|
|
{
|
|
pOutput[nIndex] = m_arrRange[nIndex][1];
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// StitchingFunction
|
|
//------------------------------------------------------------------------
|
|
|
|
StitchingFunction::StitchingFunction(Object *pFuncObject, Dict *pDict)
|
|
{
|
|
m_bValid = FALSE;
|
|
m_ppFuncs = NULL;
|
|
m_arrBounds = NULL;
|
|
m_arrEncode = NULL;
|
|
m_arrScale = NULL;
|
|
|
|
if ( !Initialize(pDict) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( m_nInputDim != 1 )
|
|
{
|
|
// TO DO: Error "Stitching function with more than one input"
|
|
return;
|
|
}
|
|
|
|
// Functions
|
|
Object oArray;
|
|
if ( !pDict->Search("Functions", &oArray)->IsArray() )
|
|
{
|
|
// TO DO: Error "Missing 'Functions' entry in stitching function"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
|
|
m_nCount = oArray.ArrayGetLength();
|
|
m_ppFuncs = (Function**)MemUtilsMallocArray( m_nCount, sizeof(Function *));
|
|
m_arrBounds = (double *)MemUtilsMallocArray( m_nCount + 1, sizeof(double));
|
|
m_arrEncode = (double *)MemUtilsMallocArray( 2 * m_nCount, sizeof(double));
|
|
m_arrScale = (double *)MemUtilsMallocArray( m_nCount, sizeof(double));
|
|
|
|
for ( int nIndex = 0; nIndex < m_nCount; ++nIndex )
|
|
{
|
|
m_ppFuncs[nIndex] = NULL;
|
|
}
|
|
|
|
for ( int nIndex = 0; nIndex < m_nCount; ++nIndex )
|
|
{
|
|
Object oTemp;
|
|
if ( !( m_ppFuncs[nIndex] = Function::Parse( oArray.ArrayGet( nIndex, &oTemp) ) ) )
|
|
{
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
|
|
if ( nIndex > 0 && ( m_ppFuncs[nIndex]->GetInputSize() != 1 || m_ppFuncs[nIndex]->GetOutputSize() != m_ppFuncs[0]->GetOutputSize() ) )
|
|
{
|
|
// TO DO: Error "Incompatible subfunctions in stitching function"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
oTemp.Free();
|
|
}
|
|
oArray.Free();
|
|
|
|
// Bounds
|
|
if ( !pDict->Search("Bounds", &oArray)->IsArray() || oArray.ArrayGetLength() != m_nCount - 1 )
|
|
{
|
|
// TO DO: Error "Missing or invalid 'Bounds' entry in stitching function"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrBounds[0] = m_arrDomain[0][0];
|
|
for ( int nIndex = 1; nIndex < m_nCount; ++nIndex )
|
|
{
|
|
Object oTemp;
|
|
if ( !oArray.ArrayGet( nIndex - 1, &oTemp)->IsNum() )
|
|
{
|
|
// TO DO: Error "Invalid type in 'Bounds' array in stitching function"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrBounds[nIndex] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
}
|
|
m_arrBounds[m_nCount] = m_arrDomain[0][1];
|
|
oArray.Free();
|
|
|
|
// Encode
|
|
if ( !pDict->Search("Encode", &oArray)->IsArray() || oArray.ArrayGetLength() != 2 * m_nCount )
|
|
{
|
|
// TO DO: Error "Missing or invalid 'Encode' entry in stitching function"
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
|
|
for ( int nIndex = 0; nIndex < 2 * m_nCount; ++nIndex )
|
|
{
|
|
Object oTemp;
|
|
if ( !oArray.ArrayGet( nIndex, &oTemp)->IsNum() )
|
|
{
|
|
// TO DO: Error "Invalid type in 'Encode' array in stitching function"
|
|
oTemp.Free();
|
|
oArray.Free();
|
|
return;
|
|
}
|
|
m_arrEncode[nIndex] = oTemp.GetNum();
|
|
oTemp.Free();
|
|
}
|
|
oArray.Free();
|
|
|
|
for ( int nIndex = 0; nIndex < m_nCount; ++nIndex )
|
|
{
|
|
if ( m_arrBounds[nIndex] == m_arrBounds[nIndex + 1] )
|
|
{
|
|
// ×òîáû èçáåæàòü äåëåíèå íà 0, â äàííîì ñëó÷àå ôóíêöèÿ ñ íîìåðîì nIndex íå áóäåò èñïîëüçîâàòüñÿ
|
|
m_arrScale[nIndex] = 0;
|
|
}
|
|
else
|
|
{
|
|
m_arrScale[nIndex] = ( m_arrEncode[2 * nIndex + 1] - m_arrEncode[2 * nIndex]) / (m_arrBounds[nIndex + 1] - m_arrBounds[nIndex]);
|
|
}
|
|
}
|
|
|
|
m_bValid = TRUE;
|
|
return;
|
|
}
|
|
|
|
StitchingFunction::StitchingFunction(StitchingFunction *pFunc)
|
|
{
|
|
m_nCount = pFunc->m_nCount;
|
|
m_ppFuncs = (Function **)MemUtilsMallocArray( m_nCount, sizeof(Function *));
|
|
|
|
for ( int nIndex = 0; nIndex < m_nCount; ++nIndex )
|
|
{
|
|
m_ppFuncs[nIndex] = pFunc->m_ppFuncs[nIndex]->Copy();
|
|
}
|
|
|
|
m_arrBounds = (double *)MemUtilsMallocArray( m_nCount + 1, sizeof(double));
|
|
memcpy( m_arrBounds, pFunc->m_arrBounds, ( m_nCount + 1 ) * sizeof(double));
|
|
|
|
m_arrEncode = (double *)MemUtilsMallocArray(2 * m_nCount, sizeof(double));
|
|
memcpy( m_arrEncode, pFunc->m_arrEncode, 2 * m_nCount * sizeof(double));
|
|
|
|
m_arrScale = (double *)MemUtilsMallocArray( m_nCount, sizeof(double));
|
|
memcpy( m_arrScale, pFunc->m_arrScale, m_nCount * sizeof(double));
|
|
|
|
m_bValid = TRUE;
|
|
}
|
|
|
|
StitchingFunction::~StitchingFunction()
|
|
{
|
|
if ( m_ppFuncs )
|
|
{
|
|
for ( int nIndex = 0; nIndex < m_nCount; ++nIndex )
|
|
{
|
|
delete m_ppFuncs[nIndex];
|
|
}
|
|
}
|
|
MemUtilsFree(m_ppFuncs);
|
|
MemUtilsFree(m_arrBounds);
|
|
MemUtilsFree(m_arrEncode);
|
|
MemUtilsFree(m_arrScale);
|
|
}
|
|
|
|
void StitchingFunction::Transform(double *pInput, double *pOutput)
|
|
{
|
|
double dX;
|
|
|
|
if ( pInput[0] < m_arrDomain[0][0] )
|
|
{
|
|
dX = m_arrDomain[0][0];
|
|
}
|
|
else if ( pInput[0] > m_arrDomain[0][1] )
|
|
{
|
|
dX = m_arrDomain[0][1];
|
|
}
|
|
else
|
|
{
|
|
dX = pInput[0];
|
|
}
|
|
|
|
int nIndex = 0;
|
|
for ( nIndex = 0; nIndex < m_nCount - 1; ++nIndex )
|
|
{
|
|
if ( dX < m_arrBounds[nIndex + 1] )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
dX = m_arrEncode[2 * nIndex] + (dX - m_arrBounds[nIndex]) * m_arrScale[nIndex];
|
|
m_ppFuncs[nIndex]->Transform( &dX, pOutput);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// PostScriptFunction
|
|
//------------------------------------------------------------------------
|
|
|
|
enum PSOperations
|
|
{
|
|
psOperationAbs,
|
|
psOperationAdd,
|
|
psOperationAnd,
|
|
psOperationAtan,
|
|
psOperationBitshift,
|
|
psOperationCeiling,
|
|
psOperationCopy,
|
|
psOperationCos,
|
|
psOperationCvi,
|
|
psOperationCvr,
|
|
psOperationDiv,
|
|
psOperationDup,
|
|
psOperationEq,
|
|
psOperationExch,
|
|
psOperationExp,
|
|
psOperationFalse,
|
|
psOperationFloor,
|
|
psOperationGe,
|
|
psOperationGt,
|
|
psOperationIdiv,
|
|
psOperationIndex,
|
|
psOperationLe,
|
|
psOperationLn,
|
|
psOperationLog,
|
|
psOperationLt,
|
|
psOperationMod,
|
|
psOperationMul,
|
|
psOperationNe,
|
|
psOperationNeg,
|
|
psOperationNot,
|
|
psOperationOr,
|
|
psOperationPop,
|
|
psOperationRoll,
|
|
psOperationRound,
|
|
psOperationSin,
|
|
psOperationSqrt,
|
|
psOperationSub,
|
|
psOperationTrue,
|
|
psOperationTruncate,
|
|
psOperationXor,
|
|
psOperationIf,
|
|
psOperationIfelse,
|
|
psOperationReturn
|
|
};
|
|
|
|
// Ñïèñîê èäåò â àëôàâèòíîì ïîðÿäêå. Íîìåð ýëåìåíòà çäåñü, ñîîòâåòñòâóåò
|
|
// îïåðàöèè â ïåðå÷èñëÿåìîì òèïå PSOperations
|
|
char *c_sPSOperationNames[] =
|
|
{
|
|
"abs",
|
|
"add",
|
|
"and",
|
|
"atan",
|
|
"bitshift",
|
|
"ceiling",
|
|
"copy",
|
|
"cos",
|
|
"cvi",
|
|
"cvr",
|
|
"div",
|
|
"dup",
|
|
"eq",
|
|
"exch",
|
|
"exp",
|
|
"false",
|
|
"floor",
|
|
"ge",
|
|
"gt",
|
|
"idiv",
|
|
"index",
|
|
"le",
|
|
"ln",
|
|
"log",
|
|
"lt",
|
|
"mod",
|
|
"mul",
|
|
"ne",
|
|
"neg",
|
|
"not",
|
|
"or",
|
|
"pop",
|
|
"roll",
|
|
"round",
|
|
"sin",
|
|
"sqrt",
|
|
"sub",
|
|
"true",
|
|
"truncate",
|
|
"xor"
|
|
};
|
|
|
|
#define PSOperationsCount (sizeof(c_sPSOperationNames) / sizeof(char *))
|
|
|
|
enum PSObjectType
|
|
{
|
|
psBool,
|
|
psInt,
|
|
psReal,
|
|
psOperator,
|
|
psBlock
|
|
};
|
|
|
|
// Â ïîòîêå, îïåðàòîðû 'if'/'ifelse' çàíèìàþò òðè ñëîòà.
|
|
//
|
|
// +---------------------------------+
|
|
// | psOperator: If / Ifelse |
|
|
// +---------------------------------+
|
|
// | psBlock: óêàçàòåëü = <A> |
|
|
// +---------------------------------+
|
|
// | psBlock: óêàçàòåëü = <B> |
|
|
// +---------------------------------+
|
|
// | if |
|
|
// | ... |
|
|
// | psOperator: Return |
|
|
// +---------------------------------+
|
|
// <A> | else |
|
|
// | ... |
|
|
// | psOperator: Return |
|
|
// +---------------------------------+
|
|
// <B> | ... |
|
|
//
|
|
// Åñëè îïåðàòîð = 'if', óêàçàòåëü <A> âñå çàäàí, íî íå èñïîëüçóåòñÿ
|
|
|
|
struct PSObject
|
|
{
|
|
PSObjectType eType;
|
|
union
|
|
{
|
|
BOOL uBool; // boolean (stack only)
|
|
int uInt; // integer (stack and code)
|
|
double uReal; // real (stack and code)
|
|
PSOperations uOperation; // operator (code only)
|
|
int uBlock; // if/ifelse block pointer (code only)
|
|
};
|
|
};
|
|
|
|
#define psStackSize 100
|
|
|
|
class PSStack
|
|
{
|
|
public:
|
|
|
|
PSStack()
|
|
{
|
|
m_nStackSize = psStackSize;
|
|
}
|
|
void PushBool(BOOL bBool)
|
|
{
|
|
if ( CheckOverflow() )
|
|
{
|
|
m_arrStack[--m_nStackSize].eType = psBool;
|
|
m_arrStack[m_nStackSize].uBool = bBool;
|
|
}
|
|
}
|
|
|
|
void PushInt(int nInteger)
|
|
{
|
|
if ( CheckOverflow() )
|
|
{
|
|
m_arrStack[--m_nStackSize].eType = psInt;
|
|
m_arrStack[m_nStackSize].uInt = nInteger;
|
|
}
|
|
}
|
|
|
|
void PushReal(double dReal)
|
|
{
|
|
if ( CheckOverflow() )
|
|
{
|
|
m_arrStack[--m_nStackSize].eType = psReal;
|
|
m_arrStack[m_nStackSize].uReal = dReal;
|
|
}
|
|
}
|
|
|
|
BOOL PopBool()
|
|
{
|
|
if ( CheckUnderflow() && CheckType( psBool, psBool) )
|
|
{
|
|
return m_arrStack[m_nStackSize++].uBool;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int PopInt()
|
|
{
|
|
if ( CheckUnderflow() && CheckType( psInt, psInt) )
|
|
{
|
|
return m_arrStack[m_nStackSize++].uInt;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
double PopNum()
|
|
{
|
|
if ( CheckUnderflow() && CheckType( psInt, psReal) )
|
|
{
|
|
double dReturn = ( m_arrStack[m_nStackSize].eType == psInt ) ? (double)m_arrStack[m_nStackSize].uInt : m_arrStack[m_nStackSize].uReal;
|
|
++m_nStackSize;
|
|
return dReturn;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
BOOL Empty()
|
|
{
|
|
return m_nStackSize == psStackSize;
|
|
}
|
|
BOOL TopIsInt()
|
|
{
|
|
return m_nStackSize < psStackSize && m_arrStack[m_nStackSize].eType == psInt;
|
|
}
|
|
BOOL TopTwoAreInts()
|
|
{
|
|
return m_nStackSize < psStackSize - 1 && m_arrStack[m_nStackSize].eType == psInt && m_arrStack[m_nStackSize + 1].eType == psInt;
|
|
}
|
|
BOOL TopIsReal()
|
|
{
|
|
return m_nStackSize < psStackSize && m_arrStack[m_nStackSize].eType == psReal;
|
|
}
|
|
BOOL TopTwoAreNums()
|
|
{
|
|
return m_nStackSize < psStackSize - 1 && (m_arrStack[m_nStackSize].eType == psInt || m_arrStack[m_nStackSize].eType == psReal) && (m_arrStack[m_nStackSize + 1].eType == psInt || m_arrStack[m_nStackSize + 1].eType == psReal);
|
|
}
|
|
|
|
void Copy(int nCount)
|
|
{
|
|
if ( m_nStackSize + nCount > psStackSize )
|
|
{
|
|
// TO DO: Error "Stack underflow in PostScript function"
|
|
return;
|
|
}
|
|
if ( !CheckOverflow(nCount) )
|
|
{
|
|
return;
|
|
}
|
|
for ( int nIndex = m_nStackSize + nCount - 1; nIndex >= m_nStackSize; --nIndex)
|
|
{
|
|
m_arrStack[nIndex - nCount] = m_arrStack[nIndex];
|
|
}
|
|
m_nStackSize -= nCount;
|
|
}
|
|
|
|
void Roll(int n, int j)
|
|
{
|
|
int i, k;
|
|
|
|
if ( j >= 0 )
|
|
{
|
|
j %= n;
|
|
}
|
|
else
|
|
{
|
|
j = -j % n;
|
|
if ( j != 0 )
|
|
{
|
|
j = n - j;
|
|
}
|
|
}
|
|
if ( n <= 0 || j == 0 )
|
|
{
|
|
return;
|
|
}
|
|
for (i = 0; i < j; ++i)
|
|
{
|
|
PSObject oPSObject = m_arrStack[m_nStackSize];
|
|
for ( k = m_nStackSize; k < m_nStackSize + n - 1; ++k )
|
|
{
|
|
m_arrStack[k] = m_arrStack[k + 1];
|
|
}
|
|
m_arrStack[m_nStackSize + n - 1] = oPSObject;
|
|
}
|
|
}
|
|
|
|
void Index(int nIndex)
|
|
{
|
|
if ( !CheckOverflow() )
|
|
{
|
|
return;
|
|
}
|
|
--m_nStackSize;
|
|
m_arrStack[m_nStackSize] = m_arrStack[m_nStackSize + 1 + nIndex];
|
|
}
|
|
|
|
void Pop()
|
|
{
|
|
if ( !CheckUnderflow() )
|
|
{
|
|
return;
|
|
}
|
|
++m_nStackSize;
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
BOOL CheckOverflow(int nCount = 1)
|
|
{
|
|
if ( m_nStackSize - nCount < 0 )
|
|
{
|
|
// TO DO: Error "Stack overflow in PostScript function"
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CheckUnderflow()
|
|
{
|
|
if ( m_nStackSize == psStackSize )
|
|
{
|
|
// TO DO: Error "Stack underflow in PostScript function"
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CheckType(PSObjectType eType1, PSObjectType eType2)
|
|
{
|
|
if ( m_arrStack[m_nStackSize].eType != eType1 && m_arrStack[m_nStackSize].eType != eType2 )
|
|
{
|
|
// TO DO: Error "Type mismatch in PostScript function"
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
private:
|
|
|
|
PSObject m_arrStack[psStackSize];
|
|
int m_nStackSize;
|
|
};
|
|
|
|
PostScriptFunction::PostScriptFunction(Object *pFuncObject, Dict *pDict)
|
|
{
|
|
m_pCode = NULL;
|
|
m_nCodeSize = 0;
|
|
m_bValid = FALSE;
|
|
|
|
if ( !Initialize(pDict) )
|
|
{
|
|
return;
|
|
}
|
|
if ( !m_bHasRange )
|
|
{
|
|
// TO DO: Error "Type 4 function is missing range"
|
|
return;
|
|
}
|
|
|
|
if ( !pFuncObject->IsStream() )
|
|
{
|
|
// TO DO: Error "Type 4 function isn't a stream"
|
|
return;
|
|
}
|
|
Stream *pStream = pFuncObject->GetStream();
|
|
|
|
//----- parse the function
|
|
m_seCodeString = new StringExt();
|
|
pStream->Reset();
|
|
|
|
StringExt *seToken;
|
|
if ( !( seToken = GetToken(pStream) ) || seToken->Compare("{") )
|
|
{
|
|
// TO DO: Error "Expected '{' at start of PostScript function"
|
|
MemUtilsFree( seToken );
|
|
pStream->Close();
|
|
return;
|
|
}
|
|
if ( seToken )
|
|
delete seToken;
|
|
int nCodePos = 0;
|
|
if ( !ParseCode( pStream, &nCodePos) )
|
|
{
|
|
pStream->Close();
|
|
return;
|
|
}
|
|
pStream->Close();
|
|
|
|
m_bValid = TRUE;
|
|
}
|
|
|
|
PostScriptFunction::PostScriptFunction(PostScriptFunction *pFunc)
|
|
{
|
|
memcpy( this, pFunc, sizeof(PostScriptFunction));
|
|
m_pCode = (PSObject *)MemUtilsMallocArray( m_nCodeSize, sizeof(PSObject));
|
|
memcpy( m_pCode, pFunc->m_pCode, m_nCodeSize * sizeof(PSObject));
|
|
m_seCodeString = pFunc->m_seCodeString->Copy();
|
|
}
|
|
|
|
PostScriptFunction::~PostScriptFunction()
|
|
{
|
|
MemUtilsFree(m_pCode);
|
|
if ( m_seCodeString )
|
|
delete m_seCodeString;
|
|
}
|
|
|
|
void PostScriptFunction::Transform(double *pInput, double *pOutput)
|
|
{
|
|
PSStack *pStack = new PSStack();
|
|
if ( !pStack )
|
|
return;
|
|
|
|
for ( int nIndex = 0; nIndex < m_nInputDim; ++nIndex )
|
|
{
|
|
pStack->PushReal( pInput[nIndex] );
|
|
}
|
|
Exec( pStack, 0);
|
|
|
|
for ( int nIndex = m_nOutputDim - 1; nIndex >= 0; --nIndex )
|
|
{
|
|
pOutput[nIndex] = pStack->PopNum();
|
|
if ( pOutput[nIndex] < m_arrRange[nIndex][0] )
|
|
{
|
|
pOutput[nIndex] = m_arrRange[nIndex][0];
|
|
}
|
|
else if ( pOutput[nIndex] > m_arrRange[nIndex][1] )
|
|
{
|
|
pOutput[nIndex] = m_arrRange[nIndex][1];
|
|
}
|
|
}
|
|
delete pStack;
|
|
}
|
|
|
|
BOOL PostScriptFunction::ParseCode(Stream *pStream, int *pnCodePos)
|
|
{
|
|
StringExt *seToken;
|
|
|
|
while (1)
|
|
{
|
|
if ( !( seToken = GetToken(pStream) ) )
|
|
{
|
|
// TO DO: Error "Unexpected end of PostScript function stream"
|
|
return FALSE;
|
|
}
|
|
if ( !seToken )
|
|
return FALSE;
|
|
|
|
char *pCurChar = seToken->GetBuffer();
|
|
|
|
if ( isdigit(*pCurChar) || *pCurChar == '.' || *pCurChar == '-' )
|
|
{
|
|
BOOL bReal = FALSE;
|
|
for (++pCurChar; *pCurChar; ++pCurChar)
|
|
{
|
|
if ( *pCurChar == '.' )
|
|
{
|
|
bReal = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
ResizeCode(*pnCodePos);
|
|
if ( bReal )
|
|
{
|
|
m_pCode[*pnCodePos].eType = psReal;
|
|
m_pCode[*pnCodePos].uReal = atof( seToken->GetBuffer() );
|
|
}
|
|
else
|
|
{
|
|
m_pCode[*pnCodePos].eType = psInt;
|
|
m_pCode[*pnCodePos].uInt = atoi( seToken->GetBuffer() );
|
|
}
|
|
++*pnCodePos;
|
|
delete seToken;
|
|
}
|
|
else if ( !seToken->Compare("{") )
|
|
{
|
|
delete seToken;
|
|
int nOperatorPos = *pnCodePos;
|
|
int nElsePos = 0;
|
|
*pnCodePos += 3;
|
|
ResizeCode( nOperatorPos + 2);
|
|
if ( !ParseCode( pStream, pnCodePos) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ( !( seToken = GetToken(pStream) ) )
|
|
{
|
|
// TO DO: Error "Unexpected end of PostScript function stream"
|
|
return FALSE;
|
|
}
|
|
if ( !seToken->Compare("{") )
|
|
{
|
|
nElsePos = *pnCodePos;
|
|
if ( !ParseCode(pStream, pnCodePos) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
delete seToken;
|
|
if ( !( seToken = GetToken(pStream) ) )
|
|
{
|
|
// TO DO: Error "Unexpected end of PostScript function stream"
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nElsePos = -1;
|
|
}
|
|
if ( !seToken->Compare("if") )
|
|
{
|
|
if ( nElsePos >= 0 )
|
|
{
|
|
// TO DO: Error "Got 'if' operator with two blocks in PostScript function"
|
|
return FALSE;
|
|
}
|
|
m_pCode[nOperatorPos].eType = psOperator;
|
|
m_pCode[nOperatorPos].uOperation = psOperationIf;
|
|
m_pCode[nOperatorPos + 2].eType = psBlock;
|
|
m_pCode[nOperatorPos + 2].uBlock = *pnCodePos;
|
|
}
|
|
else if ( !seToken->Compare("ifelse") )
|
|
{
|
|
if ( nElsePos < 0 )
|
|
{
|
|
// TO DO: Error "Got 'ifelse' operator with one blocks in PostScript function"
|
|
return FALSE;
|
|
}
|
|
m_pCode[nOperatorPos].eType = psOperator;
|
|
m_pCode[nOperatorPos].uOperation = psOperationIfelse;
|
|
m_pCode[nOperatorPos + 1].eType = psBlock;
|
|
m_pCode[nOperatorPos + 1].uBlock = nElsePos;
|
|
m_pCode[nOperatorPos + 2].eType = psBlock;
|
|
m_pCode[nOperatorPos + 2].uBlock = *pnCodePos;
|
|
}
|
|
else
|
|
{
|
|
// TO DO: Error "Expected if/ifelse operator in PostScript function"
|
|
delete seToken;
|
|
return FALSE;
|
|
}
|
|
delete seToken;
|
|
}
|
|
else if ( !seToken->Compare("}") )
|
|
{
|
|
delete seToken;
|
|
ResizeCode(*pnCodePos);
|
|
m_pCode[*pnCodePos].eType = psOperator;
|
|
m_pCode[*pnCodePos].uOperation = psOperationReturn;
|
|
++*pnCodePos;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
int nFirst = -1;
|
|
int nSecond = PSOperationsCount;
|
|
// invariant: psOpNames[a] < tok < psOpNames[b]
|
|
int nCompareRes = 0;
|
|
while ( nSecond - nFirst > 1 )
|
|
{
|
|
int nMiddle = ( nFirst + nSecond ) / 2;
|
|
nCompareRes = seToken->Compare(c_sPSOperationNames[nMiddle]);
|
|
if ( nCompareRes > 0 )
|
|
{
|
|
nFirst = nMiddle;
|
|
}
|
|
else if ( nCompareRes < 0 )
|
|
{
|
|
nSecond = nMiddle;
|
|
}
|
|
else
|
|
{
|
|
nFirst = nSecond = nMiddle;
|
|
}
|
|
}
|
|
if ( nCompareRes != 0 )
|
|
{
|
|
// TO DO: Error "Unknown operator in PostScript function"
|
|
delete seToken;
|
|
return FALSE;
|
|
}
|
|
delete seToken;
|
|
ResizeCode(*pnCodePos);
|
|
m_pCode[*pnCodePos].eType = psOperator;
|
|
m_pCode[*pnCodePos].uOperation = (PSOperations)nFirst;
|
|
++*pnCodePos;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
StringExt *PostScriptFunction::GetToken(Stream *pStream)
|
|
{
|
|
int nChar = 0;
|
|
|
|
StringExt *seResult = new StringExt();
|
|
BOOL bComment = FALSE;
|
|
|
|
while (1)
|
|
{
|
|
if ( ( nChar = pStream->GetChar() ) == EOF )
|
|
{
|
|
break;
|
|
}
|
|
m_seCodeString->Append(nChar);
|
|
if ( bComment )
|
|
{
|
|
if ( nChar == '\x0a' || nChar == '\x0d' )
|
|
{
|
|
bComment = FALSE;
|
|
}
|
|
}
|
|
else if ( nChar == '%' )
|
|
{
|
|
bComment = TRUE;
|
|
}
|
|
else if ( !isspace(nChar) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if ( nChar == '{' || nChar == '}' )
|
|
{
|
|
seResult->Append((char)nChar);
|
|
}
|
|
else if ( isdigit(nChar) || nChar == '.' || nChar == '-' )
|
|
{
|
|
while (1)
|
|
{
|
|
seResult->Append((char)nChar);
|
|
nChar = pStream->LookChar();
|
|
if ( nChar == EOF || !(isdigit(nChar) || nChar == '.' || nChar == '-') )
|
|
{
|
|
break;
|
|
}
|
|
pStream->GetChar();
|
|
m_seCodeString->Append(nChar);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (1)
|
|
{
|
|
seResult->Append((char)nChar);
|
|
nChar = pStream->LookChar();
|
|
if ( nChar == EOF || !isalnum(nChar) )
|
|
{
|
|
break;
|
|
}
|
|
pStream->GetChar();
|
|
m_seCodeString->Append(nChar);
|
|
}
|
|
}
|
|
return seResult;
|
|
}
|
|
|
|
void PostScriptFunction::ResizeCode(int nNewSize)
|
|
{
|
|
if ( nNewSize >= m_nCodeSize )
|
|
{
|
|
m_nCodeSize += 64;
|
|
m_pCode = (PSObject *)MemUtilsReallocArray( m_pCode, m_nCodeSize, sizeof(PSObject));
|
|
}
|
|
}
|
|
|
|
void PostScriptFunction::Exec(PSStack *pStack, int codePtr)
|
|
{
|
|
int nInteger1 = 0, nInteger2 = 0;
|
|
double dReal1 = 0, dReal2 = 0;
|
|
BOOL bBool1 = FALSE, bBool2 = FALSE;
|
|
|
|
while (1)
|
|
{
|
|
switch ( m_pCode[codePtr].eType )
|
|
{
|
|
case psInt:
|
|
pStack->PushInt( m_pCode[codePtr++].uInt );
|
|
break;
|
|
case psReal:
|
|
pStack->PushReal( m_pCode[codePtr++].uReal );
|
|
break;
|
|
case psOperator:
|
|
switch ( m_pCode[codePtr++].uOperation )
|
|
{
|
|
case psOperationAbs:
|
|
if ( pStack->TopIsInt() )
|
|
{
|
|
pStack->PushInt( abs(pStack->PopInt()) );
|
|
}
|
|
else
|
|
{
|
|
pStack->PushReal( fabs(pStack->PopNum()) );
|
|
}
|
|
break;
|
|
case psOperationAdd:
|
|
if ( pStack->TopTwoAreInts() )
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushInt( nInteger1 + nInteger2 );
|
|
}
|
|
else
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushReal( dReal1 + dReal2 );
|
|
}
|
|
break;
|
|
case psOperationAnd:
|
|
if ( pStack->TopTwoAreInts() )
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushInt(nInteger1 & nInteger2);
|
|
}
|
|
else
|
|
{
|
|
bBool2 = pStack->PopBool();
|
|
bBool1 = pStack->PopBool();
|
|
pStack->PushBool( bBool1 && bBool2 );
|
|
}
|
|
break;
|
|
case psOperationAtan:
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushReal( atan2( dReal1, dReal2));
|
|
break;
|
|
case psOperationBitshift:
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
if ( nInteger2 > 0 )
|
|
{
|
|
pStack->PushInt(nInteger1 << nInteger2);
|
|
}
|
|
else if ( nInteger2 < 0 )
|
|
{
|
|
pStack->PushInt((int)((unsigned int)nInteger1 >> nInteger2));
|
|
}
|
|
else
|
|
{
|
|
pStack->PushInt(nInteger1);
|
|
}
|
|
break;
|
|
case psOperationCeiling:
|
|
if ( !pStack->TopIsInt() )
|
|
{
|
|
pStack->PushReal( ceil(pStack->PopNum()) );
|
|
}
|
|
break;
|
|
case psOperationCopy:
|
|
pStack->Copy( pStack->PopInt() );
|
|
break;
|
|
case psOperationCos:
|
|
pStack->PushReal( cos(pStack->PopNum()) );
|
|
break;
|
|
case psOperationCvi:
|
|
if ( !pStack->TopIsInt() )
|
|
{
|
|
pStack->PushInt((int)pStack->PopNum());
|
|
}
|
|
break;
|
|
case psOperationCvr:
|
|
if ( !pStack->TopIsReal() )
|
|
{
|
|
pStack->PushReal( pStack->PopNum());
|
|
}
|
|
break;
|
|
case psOperationDiv:
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushReal( dReal1 / dReal2);
|
|
break;
|
|
case psOperationDup:
|
|
pStack->Copy(1);
|
|
break;
|
|
case psOperationEq:
|
|
if ( pStack->TopTwoAreInts() )
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushBool(nInteger1 == nInteger2);
|
|
}
|
|
else if ( pStack->TopTwoAreNums() )
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushBool( dReal1 == dReal2 );
|
|
}
|
|
else
|
|
{
|
|
bBool2 = pStack->PopBool();
|
|
bBool1 = pStack->PopBool();
|
|
pStack->PushBool( bBool1 == bBool2 );
|
|
}
|
|
break;
|
|
case psOperationExch:
|
|
pStack->Roll(2, 1);
|
|
break;
|
|
case psOperationExp:
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushReal( pow( dReal1, dReal2 ));
|
|
break;
|
|
case psOperationFalse:
|
|
pStack->PushBool(FALSE);
|
|
break;
|
|
case psOperationFloor:
|
|
if ( !pStack->TopIsInt() )
|
|
{
|
|
pStack->PushReal(floor(pStack->PopNum()));
|
|
}
|
|
break;
|
|
case psOperationGe:
|
|
if ( pStack->TopTwoAreInts() )
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushBool(nInteger1 >= nInteger2);
|
|
}
|
|
else
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushBool( dReal1 >= dReal2 );
|
|
}
|
|
break;
|
|
case psOperationGt:
|
|
if ( pStack->TopTwoAreInts() )
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushBool(nInteger1 > nInteger2);
|
|
}
|
|
else
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushBool( dReal1 > dReal2 );
|
|
}
|
|
break;
|
|
case psOperationIdiv:
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushInt(nInteger1 / nInteger2);
|
|
break;
|
|
case psOperationIndex:
|
|
pStack->Index(pStack->PopInt());
|
|
break;
|
|
case psOperationLe:
|
|
if (pStack->TopTwoAreInts())
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushBool(nInteger1 <= nInteger2);
|
|
}
|
|
else
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushBool( dReal1 <= dReal2 );
|
|
}
|
|
break;
|
|
case psOperationLn:
|
|
pStack->PushReal(log(pStack->PopNum()));
|
|
break;
|
|
case psOperationLog:
|
|
pStack->PushReal(log10(pStack->PopNum()));
|
|
break;
|
|
case psOperationLt:
|
|
if ( pStack->TopTwoAreInts() )
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushBool(nInteger1 < nInteger2);
|
|
}
|
|
else
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushBool( dReal1 < dReal2 );
|
|
}
|
|
break;
|
|
case psOperationMod:
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushInt(nInteger1 % nInteger2);
|
|
break;
|
|
case psOperationMul:
|
|
if (pStack->TopTwoAreInts())
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
//~ should check for out-of-range, and push a real instead
|
|
pStack->PushInt(nInteger1 * nInteger2);
|
|
}
|
|
else
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushReal( dReal1 * dReal2 );
|
|
}
|
|
break;
|
|
case psOperationNe:
|
|
if (pStack->TopTwoAreInts())
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushBool(nInteger1 != nInteger2);
|
|
}
|
|
else if (pStack->TopTwoAreNums())
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushBool( dReal1 != dReal2 );
|
|
}
|
|
else
|
|
{
|
|
bBool2 = pStack->PopBool();
|
|
bBool1 = pStack->PopBool();
|
|
pStack->PushBool( bBool1 != bBool2 );
|
|
}
|
|
break;
|
|
case psOperationNeg:
|
|
if ( pStack->TopIsInt() )
|
|
{
|
|
pStack->PushInt(-pStack->PopInt());
|
|
}
|
|
else
|
|
{
|
|
pStack->PushReal(-pStack->PopNum());
|
|
}
|
|
break;
|
|
case psOperationNot:
|
|
if ( pStack->TopIsInt() )
|
|
{
|
|
pStack->PushInt(~pStack->PopInt());
|
|
}
|
|
else
|
|
{
|
|
pStack->PushBool(!pStack->PopBool());
|
|
}
|
|
break;
|
|
case psOperationOr:
|
|
if ( pStack->TopTwoAreInts() )
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushInt(nInteger1 | nInteger2);
|
|
}
|
|
else
|
|
{
|
|
bBool2 = pStack->PopBool();
|
|
bBool1 = pStack->PopBool();
|
|
pStack->PushBool( bBool1 || bBool2 );
|
|
}
|
|
break;
|
|
case psOperationPop:
|
|
pStack->Pop();
|
|
break;
|
|
case psOperationRoll:
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->Roll( nInteger1, nInteger2);
|
|
break;
|
|
case psOperationRound:
|
|
if ( !pStack->TopIsInt() )
|
|
{
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushReal(( dReal1 >= 0 ) ? floor( dReal1 + 0.5 ) : ceil( dReal1 - 0.5 ));
|
|
}
|
|
break;
|
|
case psOperationSin:
|
|
pStack->PushReal(sin(pStack->PopNum()));
|
|
break;
|
|
case psOperationSqrt:
|
|
pStack->PushReal(sqrt(pStack->PopNum()));
|
|
break;
|
|
case psOperationSub:
|
|
if (pStack->TopTwoAreInts())
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushInt(nInteger1 - nInteger2);
|
|
}
|
|
else
|
|
{
|
|
dReal2 = pStack->PopNum();
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushReal( dReal1 - dReal2 );
|
|
}
|
|
break;
|
|
case psOperationTrue:
|
|
pStack->PushBool(TRUE);
|
|
break;
|
|
case psOperationTruncate:
|
|
if (!pStack->TopIsInt())
|
|
{
|
|
dReal1 = pStack->PopNum();
|
|
pStack->PushReal(( dReal1 >= 0 ) ? floor( dReal1 ) : ceil( dReal1 ));
|
|
}
|
|
break;
|
|
case psOperationXor:
|
|
if (pStack->TopTwoAreInts())
|
|
{
|
|
nInteger2 = pStack->PopInt();
|
|
nInteger1 = pStack->PopInt();
|
|
pStack->PushInt(nInteger1 ^ nInteger2);
|
|
}
|
|
else
|
|
{
|
|
bBool2 = pStack->PopBool();
|
|
bBool1 = pStack->PopBool();
|
|
pStack->PushBool( bBool1 ^ bBool2 );
|
|
}
|
|
break;
|
|
case psOperationIf:
|
|
bBool1 = pStack->PopBool();
|
|
if ( bBool1 )
|
|
{
|
|
Exec(pStack, codePtr + 2);
|
|
}
|
|
codePtr = m_pCode[codePtr + 1].uBlock;
|
|
break;
|
|
case psOperationIfelse:
|
|
bBool1 = pStack->PopBool();
|
|
if ( bBool1 )
|
|
{
|
|
Exec(pStack, codePtr + 2);
|
|
}
|
|
else
|
|
{
|
|
Exec(pStack, m_pCode[codePtr].uBlock);
|
|
}
|
|
codePtr = m_pCode[codePtr + 1].uBlock;
|
|
break;
|
|
case psOperationReturn:
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
// TO DO: Error "Internal: bad object in PostScript function code"
|
|
break;
|
|
}
|
|
}
|
|
}
|