mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-04-07 13:55:33 +08:00
421 lines
11 KiB
C++
421 lines
11 KiB
C++
#include "stdafx.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "MemoryUtils.h"
|
|
#include "SMathExt.h"
|
|
#include "SScreen.h"
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
static SScreenParams DefaultParams =
|
|
{
|
|
sscreenDispersed, // Type
|
|
2, // Size
|
|
2, // Dot Radius
|
|
1.0, // Gamma
|
|
0.0, // Black Threshold
|
|
1.0 // White Threshold
|
|
};
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
struct SScreenPoint
|
|
{
|
|
int nX;
|
|
int nY;
|
|
int nDist;
|
|
};
|
|
|
|
static int CompareDistances(const void *pFirstPoint, const void *pSecondPoint)
|
|
{
|
|
return ((SScreenPoint *)pFirstPoint)->nDist - ((SScreenPoint *)pSecondPoint)->nDist;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------------------
|
|
// SScreen
|
|
//-------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
// If <clustered> is true, this generates a 45 degree screen using a circular dot spot function. DPI = resolution / ((size / 2) *
|
|
// sqrt(2)). If <clustered> is false, this generates an optimal threshold matrix using recursive tesselation. Gamma correction
|
|
// (gamma = 1 / 1.33) is also computed here.
|
|
SScreen::SScreen(SScreenParams *pParams)
|
|
{
|
|
if ( !pParams )
|
|
{
|
|
pParams = &DefaultParams;
|
|
}
|
|
|
|
switch ( pParams->eType )
|
|
{
|
|
case sscreenDispersed:
|
|
// m_nSize äîëæåí áûòü ñòåïåíüþ 2
|
|
for ( m_nSize = 1; m_nSize < pParams->nSize; m_nSize <<= 1 ) ;
|
|
m_pMatrix = (unsigned char *)MemUtilsMallocArray( m_nSize * m_nSize, sizeof(unsigned char) );
|
|
BuildDispersedMatrix( m_nSize / 2, m_nSize / 2, 1, m_nSize / 2, 1 );
|
|
break;
|
|
case sscreenClustered:
|
|
// m_nSize äîëæåí áûòü ÷åòíûì
|
|
m_nSize = (pParams->nSize >> 1) << 1;
|
|
if ( m_nSize < 2 )
|
|
{
|
|
m_nSize = 2;
|
|
}
|
|
m_pMatrix = (unsigned char *)MemUtilsMallocArray( m_nSize * m_nSize, sizeof(unsigned char) );
|
|
BuildClusteredMatrix();
|
|
break;
|
|
|
|
case sscreenStochasticClustered:
|
|
// m_nSize äîëæåí áûòü íå ìåíüøå, ÷åì 2 * Radius
|
|
if ( pParams->nSize < 2 * pParams->nDotRadius )
|
|
{
|
|
m_nSize = 2 * pParams->nDotRadius;
|
|
}
|
|
else
|
|
{
|
|
m_nSize = pParams->nSize;
|
|
}
|
|
m_pMatrix = (unsigned char *)MemUtilsMallocArray( m_nSize * m_nSize, sizeof(unsigned char) );
|
|
BuildSCDMatrix( pParams->nDotRadius );
|
|
break;
|
|
}
|
|
|
|
// Âûïîëíÿåì ãàììà-êîððåêöèþ è âû÷èñëÿåì m_unMinValue/m_unMaxValue çíà÷åíèÿ
|
|
m_unMinValue = 255;
|
|
m_unMaxValue = 0;
|
|
|
|
unsigned char unBlack = round( (double)255.0 * pParams->dBlackThreshold );
|
|
if ( unBlack < 1 )
|
|
{
|
|
unBlack = 1;
|
|
}
|
|
unsigned char unWhite = round( (double)255.0 * pParams->dWhiteThreshold );
|
|
if ( unWhite > 255 )
|
|
{
|
|
unWhite = 255;
|
|
}
|
|
|
|
for ( int nIndex = 0; nIndex < m_nSize * m_nSize; ++nIndex )
|
|
{
|
|
unsigned char unPixel = round( (double)255.0 * pow((double)m_pMatrix[nIndex] / 255.0, pParams->dGamma ) );
|
|
if ( unPixel < unBlack )
|
|
{
|
|
unPixel = unBlack;
|
|
}
|
|
else if ( unPixel >= unWhite )
|
|
{
|
|
unPixel = unWhite;
|
|
}
|
|
m_pMatrix[nIndex] = unPixel;
|
|
|
|
if ( unPixel < m_unMinValue )
|
|
{
|
|
m_unMinValue = unPixel;
|
|
}
|
|
else if ( unPixel > m_unMaxValue )
|
|
{
|
|
m_unMaxValue = unPixel;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SScreen::BuildDispersedMatrix(int nX, int nY, int nValue, int nDelta, int nOffset)
|
|
{
|
|
if ( nDelta == 0 )
|
|
{
|
|
// [1, m_nSize ^ 2] --> [1, 255]
|
|
m_pMatrix[nX * m_nSize + nY] = 1 + (254 * (nValue - 1)) / (m_nSize * m_nSize - 1);
|
|
}
|
|
else
|
|
{
|
|
BuildDispersedMatrix( nX, nY, nValue , nDelta / 2, 4 * nOffset );
|
|
BuildDispersedMatrix( (nX + nDelta) % m_nSize, (nY + nDelta) % m_nSize, nValue + nOffset, nDelta / 2, 4 * nOffset );
|
|
BuildDispersedMatrix( (nX + nDelta) % m_nSize, nY, nValue + 2 * nOffset, nDelta / 2, 4 * nOffset );
|
|
BuildDispersedMatrix( (nX + 2 * nDelta) % m_nSize, (nY + nDelta) % m_nSize, nValue + 3 * nOffset, nDelta / 2, 4 * nOffset );
|
|
}
|
|
}
|
|
|
|
void SScreen::BuildClusteredMatrix()
|
|
{
|
|
double dDx, dDy;
|
|
|
|
int nSize_2 = m_nSize >> 1;
|
|
|
|
// Èíèöèàëèçàöèÿ
|
|
for ( int nY = 0; nY < m_nSize; ++nY )
|
|
{
|
|
for ( int nX = 0; nX < m_nSize; ++nX )
|
|
{
|
|
m_pMatrix[ nY * m_nSize + nX ] = 0;
|
|
}
|
|
}
|
|
|
|
// build the distance matrix
|
|
double *pDistance = (double *)MemUtilsMallocArray( m_nSize * nSize_2, sizeof(double) );
|
|
|
|
for ( int nY = 0; nY < nSize_2; ++nY )
|
|
{
|
|
for ( int nX = 0; nX < nSize_2; ++nX )
|
|
{
|
|
if ( nX + nY < nSize_2 - 1 )
|
|
{
|
|
dDx = (double)nX + 0.5 - 0;
|
|
dDy = (double)nY + 0.5 - 0;
|
|
}
|
|
else
|
|
{
|
|
dDx = (double)nX + 0.5 - (double)nSize_2;
|
|
dDy = (double)nY + 0.5 - (double)nSize_2;
|
|
}
|
|
pDistance[nY * nSize_2 + nX] = dDx * dDx + dDy * dDy;
|
|
}
|
|
}
|
|
|
|
for ( int nY = 0; nY < nSize_2; ++nY )
|
|
{
|
|
for ( int nX = 0; nX < nSize_2; ++nX )
|
|
{
|
|
if ( nX < nY )
|
|
{
|
|
dDx = (double)nX + 0.5 - 0;
|
|
dDy = (double)nY + 0.5 - (double)nSize_2;
|
|
}
|
|
else
|
|
{
|
|
dDx = (double)nX + 0.5 - (double)nSize_2;
|
|
dDy = (double)nY + 0.5 - 0;
|
|
}
|
|
pDistance[(nSize_2 + nY) * nSize_2 + nX] = dDx * dDx + dDy * dDy;
|
|
}
|
|
}
|
|
|
|
// Ñòðîèì ìàòðèöó
|
|
m_unMinValue = 1;
|
|
m_unMaxValue = 0;
|
|
|
|
int nX1 = 0, nY1 = 0;
|
|
|
|
for ( int nIndex = 0; nIndex < m_nSize * nSize_2; ++nIndex )
|
|
{
|
|
double dDist = -1;
|
|
|
|
for ( int nY = 0; nY < m_nSize; ++nY )
|
|
{
|
|
for ( int nX = 0; nX < nSize_2; ++nX )
|
|
{
|
|
if ( m_pMatrix[nY * m_nSize + nX] == 0 && pDistance[nY * nSize_2 + nX] > dDist )
|
|
{
|
|
nX1 = nX;
|
|
nY1 = nY;
|
|
dDist = pDistance[nY1 * nSize_2 + nX1];
|
|
}
|
|
}
|
|
}
|
|
|
|
// [0, 2 * m_nSize * nSize_2 - 1] --> [1, 255]
|
|
unsigned char unValue = 1 + (254 * (2 * nIndex)) / (2 * m_nSize * nSize_2 - 1);
|
|
m_pMatrix[nY1 * m_nSize + nX1] = unValue;
|
|
|
|
unValue = 1 + (254 * (2 * nIndex + 1)) / (2 * m_nSize * nSize_2 - 1);
|
|
if ( nY1 < nSize_2 )
|
|
{
|
|
m_pMatrix[(nY1 + nSize_2) * m_nSize + nX1 + nSize_2] = unValue;
|
|
}
|
|
else
|
|
{
|
|
m_pMatrix[(nY1 - nSize_2) * m_nSize + nX1 + nSize_2] = unValue;
|
|
}
|
|
}
|
|
|
|
MemUtilsFree( pDistance );
|
|
}
|
|
|
|
int SScreen::Distance(int nX0, int nY0, int nX1, int nY1)
|
|
{
|
|
int nDx0 = abs( nX0 - nX1 );
|
|
int nDx1 = m_nSize - nDx0;
|
|
int nDx = nDx0 < nDx1 ? nDx0 : nDx1;
|
|
int nDy0 = abs( nY0 - nY1 );
|
|
int nDy1 = m_nSize - nDy0;
|
|
int nDy = nDy0 < nDy1 ? nDy0 : nDy1;
|
|
return nDx * nDx + nDy * nDy;
|
|
}
|
|
|
|
void SScreen::BuildSCDMatrix(int nRadius)
|
|
{
|
|
srand( 123 );
|
|
|
|
SScreenPoint *pPoints = (SScreenPoint *)MemUtilsMallocArray( m_nSize * m_nSize, sizeof(SScreenPoint) );
|
|
int nIndex = 0;
|
|
for ( int nY = 0; nY < m_nSize; ++nY )
|
|
{
|
|
for ( int nX = 0; nX < m_nSize; ++nX )
|
|
{
|
|
pPoints[nIndex].nX = nX;
|
|
pPoints[nIndex].nY = nY;
|
|
++nIndex;
|
|
}
|
|
}
|
|
|
|
for ( nIndex = 0; nIndex < m_nSize * m_nSize; ++nIndex )
|
|
{
|
|
int nTempIndex = nIndex + (int)( (double)( m_nSize * m_nSize - nIndex ) * (double)rand() / ( (double)RAND_MAX + 1.0) );
|
|
int nTempX = pPoints[nIndex].nX;
|
|
int nTempY = pPoints[nIndex].nY;
|
|
pPoints[nIndex].nX = pPoints[nTempIndex].nX;
|
|
pPoints[nIndex].nY = pPoints[nTempIndex].nY;
|
|
pPoints[nTempIndex].nX = nTempX;
|
|
pPoints[nTempIndex].nY = nTempY;
|
|
}
|
|
|
|
char *pTemplate = (char *)MemUtilsMallocArray( (nRadius + 1) * (nRadius + 1), sizeof(char) );
|
|
for ( int nY = 0; nY <= nRadius; ++nY )
|
|
{
|
|
for ( int nX = 0; nX <= nRadius; ++nX )
|
|
{
|
|
pTemplate[nY * (nRadius + 1) + nX] = (nX * nY <= nRadius * nRadius) ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
char *pGrid = (char *)MemUtilsMallocArray( m_nSize * m_nSize, sizeof(char) );
|
|
for ( int nY = 0; nY < m_nSize; ++nY )
|
|
{
|
|
for ( int nX = 0; nX < m_nSize; ++nX )
|
|
{
|
|
pGrid[nY * m_nSize + nX] = 0;
|
|
}
|
|
}
|
|
|
|
int nDotsCount = 0;
|
|
int nDotsSize = 32;
|
|
SScreenPoint *pDots = (SScreenPoint *)MemUtilsMallocArray( nDotsSize, sizeof(SScreenPoint) );
|
|
for ( nIndex = 0; nIndex < m_nSize * m_nSize; ++nIndex )
|
|
{
|
|
int nX = pPoints[nIndex].nX;
|
|
int nY = pPoints[nIndex].nY;
|
|
if ( !pGrid[nY * m_nSize + nX] )
|
|
{
|
|
if ( nDotsCount == nDotsSize )
|
|
{
|
|
nDotsSize *= 2;
|
|
pDots = (SScreenPoint *)MemUtilsReallocArray( pDots, nDotsSize, sizeof(SScreenPoint) );
|
|
}
|
|
pDots[nDotsCount++] = pPoints[nIndex];
|
|
for ( int nYY = 0; nYY <= nRadius; ++nYY )
|
|
{
|
|
int nY0 = (nY + nYY) % m_nSize;
|
|
int nY1 = (nY - nYY + m_nSize) % m_nSize;
|
|
for ( int nXX = 0; nXX <= nRadius; ++nXX )
|
|
{
|
|
if ( pTemplate[nYY * (nRadius + 1) + nXX] )
|
|
{
|
|
int nX0 = (nX + nXX) % m_nSize;
|
|
int nX1 = (nX - nXX + m_nSize) % m_nSize;
|
|
pGrid[nY0 * m_nSize + nX0] = 1;
|
|
pGrid[nY0 * m_nSize + nX1] = 1;
|
|
pGrid[nY1 * m_nSize + nX0] = 1;
|
|
pGrid[nY1 * m_nSize + nX1] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MemUtilsFree( pTemplate );
|
|
MemUtilsFree( pGrid );
|
|
|
|
int *pRegion = (int *)MemUtilsMallocArray( m_nSize * m_nSize, sizeof(int) );
|
|
int *pDist = (int *)MemUtilsMallocArray( m_nSize * m_nSize, sizeof(int) );
|
|
for ( int nY = 0; nY < m_nSize; ++nY )
|
|
{
|
|
for ( int nX = 0; nX < m_nSize; ++nX )
|
|
{
|
|
int nMinIndex = 0;
|
|
int nMinDist = Distance(pDots[0].nX, pDots[0].nY, nX, nY);
|
|
for ( nIndex = 1; nIndex < nDotsCount; ++nIndex )
|
|
{
|
|
int nDistance = Distance(pDots[nIndex].nX, pDots[nIndex].nY, nX, nY);
|
|
if ( nDistance < nMinDist )
|
|
{
|
|
nMinIndex = nIndex;
|
|
nMinDist = nDistance;
|
|
}
|
|
}
|
|
pRegion[nY * m_nSize + nX] = nMinIndex;
|
|
pDist[nY * m_nSize + nX] = nMinDist;
|
|
}
|
|
}
|
|
|
|
for ( nIndex = 0; nIndex < nDotsCount; ++nIndex )
|
|
{
|
|
int nCount = 0;
|
|
for ( int nY = 0; nY < m_nSize; ++nY )
|
|
{
|
|
for ( int nX = 0; nX < m_nSize; ++nX )
|
|
{
|
|
if ( pRegion[nY * m_nSize + nX] == nIndex )
|
|
{
|
|
pPoints[nCount].nX = nX;
|
|
pPoints[nCount].nY = nY;
|
|
pPoints[nCount].nDist = Distance(pDots[nIndex].nX, pDots[nIndex].nY, nX, nY);
|
|
++nCount;
|
|
}
|
|
}
|
|
}
|
|
qsort( pPoints, nCount, sizeof(SScreenPoint), &CompareDistances );
|
|
for ( int nJ = 0; nJ < nCount; ++nJ )
|
|
{
|
|
// [0 .. n - 1] --> [255 .. 1]
|
|
m_pMatrix[pPoints[nJ].nY * m_nSize + pPoints[nJ].nX] = 255 - (254 * nJ) / (nCount - 1);
|
|
}
|
|
}
|
|
|
|
MemUtilsFree( pPoints );
|
|
MemUtilsFree( pRegion );
|
|
MemUtilsFree( pDist );
|
|
MemUtilsFree( pDots );
|
|
}
|
|
|
|
SScreen::SScreen(SScreen *pScreen)
|
|
{
|
|
m_nSize = pScreen->m_nSize;
|
|
m_pMatrix = (unsigned char *)MemUtilsMallocArray( m_nSize * m_nSize, sizeof(unsigned char) );
|
|
memcpy( m_pMatrix, pScreen->m_pMatrix, m_nSize * m_nSize * sizeof(unsigned char) );
|
|
m_unMinValue = pScreen->m_unMinValue;
|
|
m_unMaxValue = pScreen->m_unMaxValue;
|
|
}
|
|
|
|
SScreen::~SScreen()
|
|
{
|
|
MemUtilsFree( m_pMatrix );
|
|
}
|
|
|
|
int SScreen::GetGrayPixel(int nX, int nY, unsigned char unValue)
|
|
{
|
|
int nXX, nYY;
|
|
|
|
if ( unValue < m_unMinValue )
|
|
{
|
|
return 0;
|
|
}
|
|
if ( unValue >= m_unMaxValue )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if ( ( nXX = nX % m_nSize ) < 0 )
|
|
{
|
|
nXX = -nXX;
|
|
}
|
|
if ( ( nYY = nY % m_nSize ) < 0 )
|
|
{
|
|
nYY = -nYY;
|
|
}
|
|
return unValue < m_pMatrix[nYY * m_nSize + nXX] ? 0 : 1;
|
|
}
|
|
|
|
BOOL SScreen::IsStatic(unsigned char unValue)
|
|
{
|
|
return ( unValue < m_unMinValue || unValue >= m_unMaxValue );
|
|
}
|