#include "stdafx.h" #include #include #include #include #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: указатель = | // +---------------------------------+ // | psBlock: указатель = | // +---------------------------------+ // | if | // | ... | // | psOperator: Return | // +---------------------------------+ // | else | // | ... | // | psOperator: Return | // +---------------------------------+ // | ... | // // Если оператор = 'if', указатель все задан, но не используется 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; } } }