Files
core/PdfReader/Src/Function.cpp
Ilya.Kirillov eb965c1d6d Добавлен новый проект PdfReader для кроссплатформенного чтения Pdf.
git-svn-id: svn://fileserver/activex/AVS/Sources/TeamlabOffice/trunk/ServerComponents@62480 954022d7-b5bf-4e40-9824-e11837661b57
2016-05-21 00:01:53 +03:00

1867 lines
42 KiB
C++

#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"
namespace PdfReader
{
//------------------------------------------------------------------------
// 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;
}
}
}
}