mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-04-07 13:55:33 +08:00
516 lines
11 KiB
C++
516 lines
11 KiB
C++
#include "stdafx.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "MemoryUtils.h"
|
|
#include "SMathExt.h"
|
|
#include "SXPath.h"
|
|
#include "SBitmap.h"
|
|
#include "SXPathScanner.h"
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
struct SIntersect
|
|
{
|
|
int nX0; // Ïåðåñå÷åíèå ñåãìåíòà ñ ïîëóîòðåçêîì [ y, y + 1 )
|
|
int nX1; //
|
|
int nCount; // EO/NZWN ñ÷åò÷èê
|
|
};
|
|
|
|
static int CompareIntersect(const void *pFirstInter, const void *pSecondInter)
|
|
{
|
|
return ((SIntersect *)pFirstInter)->nX0 - ((SIntersect *)pSecondInter)->nX0;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------------------
|
|
// SXPathScanner
|
|
//-------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
SXPathScanner::SXPathScanner(SXPath *pXPath, BOOL bEO)
|
|
{
|
|
m_pXPath = pXPath;
|
|
m_bEO = bEO;
|
|
|
|
// Âû÷èñëèì BBox
|
|
if ( m_pXPath->m_nSegmentsCount == 0 )
|
|
{
|
|
m_nMinX = m_nMinY = 1;
|
|
m_nMaxX = m_nMaxY = 0;
|
|
}
|
|
else
|
|
{
|
|
double dMinX, dMaxX, dMinY, dMaxY;
|
|
SXPathSegment *pSegment = &m_pXPath->m_pSegments[0];
|
|
if ( pSegment->dFirstX <= pSegment->dSecondX )
|
|
{
|
|
dMinX = pSegment->dFirstX;
|
|
dMaxX = pSegment->dSecondX;
|
|
}
|
|
else
|
|
{
|
|
dMinX = pSegment->dSecondX;
|
|
dMaxX = pSegment->dFirstX;
|
|
}
|
|
if ( pSegment->unFlags & XPathFlip )
|
|
{
|
|
dMinY = pSegment->dSecondY;
|
|
dMaxY = pSegment->dFirstY;
|
|
}
|
|
else
|
|
{
|
|
dMinY = pSegment->dFirstY;
|
|
dMaxY = pSegment->dSecondY;
|
|
}
|
|
|
|
for ( int nIndex = 1; nIndex < m_pXPath->m_nSegmentsCount; ++nIndex )
|
|
{
|
|
pSegment = &m_pXPath->m_pSegments[nIndex];
|
|
if ( pSegment->dFirstX < dMinX )
|
|
{
|
|
dMinX = pSegment->dFirstX;
|
|
}
|
|
else if ( pSegment->dFirstX > dMaxX )
|
|
{
|
|
dMaxX = pSegment->dFirstX;
|
|
}
|
|
|
|
if ( pSegment->dSecondX < dMinX )
|
|
{
|
|
dMinX = pSegment->dSecondX;
|
|
}
|
|
else if ( pSegment->dSecondX > dMaxX )
|
|
{
|
|
dMaxX = pSegment->dSecondX;
|
|
}
|
|
|
|
if ( pSegment->unFlags & XPathFlip )
|
|
{
|
|
if ( pSegment->dFirstY > dMaxY )
|
|
{
|
|
dMaxY = pSegment->dFirstY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pSegment->dSecondY > dMaxY)
|
|
{
|
|
dMaxY = pSegment->dSecondY;
|
|
}
|
|
}
|
|
}
|
|
m_nMinX = (int)floor(dMinX);
|
|
m_nMaxX = (int)floor(dMaxX);
|
|
m_nMinY = (int)floor(dMinY);
|
|
m_nMaxY = (int)floor(dMaxY);
|
|
}
|
|
|
|
m_nInterY = m_nMinY - 1;
|
|
m_nXPathIndex = 0;
|
|
|
|
m_pInter = NULL;
|
|
m_nInterLen = m_nInterSize = 0;
|
|
}
|
|
|
|
SXPathScanner::~SXPathScanner()
|
|
{
|
|
MemUtilsFree( m_pInter );
|
|
}
|
|
|
|
void SXPathScanner::GetBBoxAA(int *pnMinX, int *pnMinY, int *pnMaxX, int *pnMaxY)
|
|
{
|
|
*pnMinX = m_nMinX / AntiAliasingSize;
|
|
*pnMinY = m_nMinY / AntiAliasingSize;
|
|
*pnMaxX = m_nMaxX / AntiAliasingSize;
|
|
*pnMaxY = m_nMaxY / AntiAliasingSize;
|
|
}
|
|
|
|
void SXPathScanner::GetSpanBounds(int nY, int *pnSpanMinX, int *pnSpanMaxX)
|
|
{
|
|
if ( m_nInterY != nY )
|
|
{
|
|
ComputeIntersections( nY );
|
|
}
|
|
|
|
if ( m_nInterLen > 0 )
|
|
{
|
|
*pnSpanMinX = m_pInter[0].nX0;
|
|
*pnSpanMaxX = m_pInter[m_nInterLen - 1].nX1;
|
|
}
|
|
else
|
|
{
|
|
*pnSpanMinX = m_nMaxX + 1;
|
|
*pnSpanMaxX = m_nMaxX;
|
|
}
|
|
}
|
|
|
|
BOOL SXPathScanner::IsInsidePath(int nX, int nY)
|
|
{
|
|
if ( m_nInterY != nY )
|
|
{
|
|
ComputeIntersections( nY );
|
|
}
|
|
|
|
int nCount = 0;
|
|
for ( int nIndex = 0; nIndex < m_nInterLen && m_pInter[nIndex].nX0 <= nX; ++nIndex )
|
|
{
|
|
if ( nX <= m_pInter[nIndex].nX1 )
|
|
{
|
|
return TRUE;
|
|
}
|
|
nCount += m_pInter[nIndex].nCount;
|
|
}
|
|
return m_bEO ? (nCount & 1) : (nCount != 0);
|
|
}
|
|
|
|
BOOL SXPathScanner::IsInsidePath(int nX0, int nX1, int nY)
|
|
{
|
|
if ( m_nInterY != nY )
|
|
{
|
|
ComputeIntersections(nY);
|
|
}
|
|
|
|
int nCount = 0, nIndex = 0;
|
|
for ( nIndex = 0; nIndex < m_nInterLen && m_pInter[nIndex].nX1 < nX0; ++nIndex )
|
|
{
|
|
nCount += m_pInter[nIndex].nCount;
|
|
}
|
|
|
|
// ïðîìåæóòîê [ nX0, nXm ] âñåãäà âíóòðè Path
|
|
int nXm = nX0 - 1;
|
|
while ( nXm < nX1 )
|
|
{
|
|
if ( nIndex >= m_nInterLen )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( m_pInter[nIndex].nX0 > nXm + 1 && !(m_bEO ? (nCount & 1) : (nCount != 0) ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
if ( m_pInter[nIndex].nX1 > nXm )
|
|
{
|
|
nXm = m_pInter[nIndex].nX1;
|
|
}
|
|
nCount += m_pInter[nIndex].nCount;
|
|
++nIndex;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SXPathScanner::GetNextSpan(int nY, int *pnX0, int *pnX1)
|
|
{
|
|
if ( m_nInterY != nY )
|
|
{
|
|
ComputeIntersections( nY );
|
|
}
|
|
|
|
if ( m_nInterIndex >= m_nInterLen )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int nX0 = m_pInter[m_nInterIndex].nX0;
|
|
int nX1 = m_pInter[m_nInterIndex].nX1;
|
|
|
|
m_nInterCount += m_pInter[m_nInterIndex].nCount;
|
|
++m_nInterIndex;
|
|
|
|
while ( m_nInterIndex < m_nInterLen && ( m_pInter[m_nInterIndex].nX0 <= nX1 || ( m_bEO ? (m_nInterCount & 1) : (m_nInterCount != 0) ) ) )
|
|
{
|
|
if ( m_pInter[m_nInterIndex].nX1 > nX1 )
|
|
{
|
|
nX1 = m_pInter[m_nInterIndex].nX1;
|
|
}
|
|
m_nInterCount += m_pInter[m_nInterIndex].nCount;
|
|
++m_nInterIndex;
|
|
}
|
|
*pnX0 = nX0;
|
|
*pnX1 = nX1;
|
|
return TRUE;
|
|
}
|
|
|
|
void SXPathScanner::ComputeIntersections(int nY)
|
|
{
|
|
double dSegMinX, dSegMaxX, dSegMinY, dSegMaxY, dX0, dX1;
|
|
|
|
// Èùåì ïåðâûé ñåãìåíò, ïåðåñåêàþùèéñÿ ñ [ nY, nY + 1 )
|
|
int nFirst = ( nY >= m_nInterY ) ? m_nXPathIndex : 0;
|
|
|
|
while ( nFirst < m_pXPath->m_nSegmentsCount && m_pXPath->m_pSegments[nFirst].dFirstY < nY && m_pXPath->m_pSegments[nFirst].dSecondY < nY )
|
|
{
|
|
++nFirst;
|
|
}
|
|
m_nXPathIndex = nFirst;
|
|
|
|
// Èùåì âñå ñåãìåíòû, ïåðåñåêàþùèåñÿ ñ [ nY, nY + 1 ), è ñîçäàåì ýëåìåíò Intersect äëÿ êàæäîãî
|
|
m_nInterLen = 0;
|
|
for ( int nIndex = nFirst; nIndex < m_pXPath->m_nSegmentsCount; ++nIndex )
|
|
{
|
|
SXPathSegment *pSegment = &m_pXPath->m_pSegments[nIndex];
|
|
if ( pSegment->unFlags & XPathFlip )
|
|
{
|
|
dSegMinY = pSegment->dSecondY;
|
|
dSegMaxY = pSegment->dFirstY;
|
|
}
|
|
else
|
|
{
|
|
dSegMinY = pSegment->dFirstY;
|
|
dSegMaxY = pSegment->dSecondY;
|
|
}
|
|
|
|
// ySegMin < nY + 1
|
|
// nY <= ySegMax
|
|
if ( dSegMinY >= nY + 1 )
|
|
{
|
|
break;
|
|
}
|
|
if ( dSegMaxY < nY )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( m_nInterLen == m_nInterSize )
|
|
{
|
|
if ( m_nInterSize == 0 )
|
|
{
|
|
m_nInterSize = 16;
|
|
}
|
|
else
|
|
{
|
|
m_nInterSize *= 2;
|
|
}
|
|
m_pInter = (SIntersect *)MemUtilsReallocArray( m_pInter, m_nInterSize, sizeof(SIntersect) );
|
|
}
|
|
|
|
if ( pSegment->unFlags & XPathHoriz )
|
|
{
|
|
dX0 = pSegment->dFirstX;
|
|
dX1 = pSegment->dSecondX;
|
|
}
|
|
else if ( pSegment->unFlags & XPathVert )
|
|
{
|
|
dX0 = dX1 = pSegment->dFirstX;
|
|
}
|
|
else
|
|
{
|
|
if ( pSegment->dFirstX < pSegment->dSecondX )
|
|
{
|
|
dSegMinX = pSegment->dFirstX;
|
|
dSegMaxX = pSegment->dSecondX;
|
|
}
|
|
else
|
|
{
|
|
dSegMinX = pSegment->dSecondX;
|
|
dSegMaxX = pSegment->dFirstX;
|
|
}
|
|
|
|
// Ïåðåñå÷åíèå ñ âåðõíåé âåðøèíîé
|
|
dX0 = pSegment->dFirstX + ((double)nY - pSegment->dFirstY) * pSegment->dDxDy;
|
|
// Ïåðåñå÷åíèå ñ íèæíåé âåðøèíîé
|
|
dX1 = pSegment->dFirstX + ((double)nY + 1 - pSegment->dFirstY) * pSegment->dDxDy;
|
|
|
|
if ( dX0 < dSegMinX )
|
|
{
|
|
dX0 = dSegMinX;
|
|
}
|
|
else if ( dX0 > dSegMaxX )
|
|
{
|
|
dX0 = dSegMaxX;
|
|
}
|
|
if ( dX1 < dSegMinX )
|
|
{
|
|
dX1 = dSegMinX;
|
|
}
|
|
else if ( dX1 > dSegMaxX )
|
|
{
|
|
dX1 = dSegMaxX;
|
|
}
|
|
}
|
|
if ( dX0 < dX1 )
|
|
{
|
|
m_pInter[m_nInterLen].nX0 = (int)floor(dX0);
|
|
m_pInter[m_nInterLen].nX1 = (int)floor(dX1);
|
|
}
|
|
else
|
|
{
|
|
m_pInter[m_nInterLen].nX0 = (int)floor(dX1);
|
|
m_pInter[m_nInterLen].nX1 = (int)floor(dX0);
|
|
}
|
|
if ( dSegMinY <= nY && (double)nY < dSegMaxY && !(pSegment->unFlags & XPathHoriz) )
|
|
{
|
|
m_pInter[m_nInterLen].nCount = m_bEO ? 1 : (pSegment->unFlags & XPathFlip) ? 1 : -1;
|
|
}
|
|
else
|
|
{
|
|
m_pInter[m_nInterLen].nCount = 0;
|
|
}
|
|
++m_nInterLen;
|
|
}
|
|
|
|
qsort( m_pInter, m_nInterLen, sizeof(SIntersect), &CompareIntersect );
|
|
|
|
m_nInterY = nY;
|
|
m_nInterIndex = 0;
|
|
m_nInterCount = 0;
|
|
}
|
|
|
|
void SXPathScanner::RenderAALine(SBitmap *pAABuffer, int *pnX0, int *pnX1, int nY)
|
|
{
|
|
memset( pAABuffer->GetData(), 0, pAABuffer->GetStride() * pAABuffer->GetHeight() );
|
|
int nMinX = pAABuffer->GetWidth();
|
|
int nMaxX = -1;
|
|
for ( int nIndexY = 0; nIndexY < AntiAliasingSize; ++nIndexY )
|
|
{
|
|
ComputeIntersections( AntiAliasingSize * nY + nIndexY );
|
|
|
|
while ( m_nInterIndex < m_nInterLen )
|
|
{
|
|
int nX0 = m_pInter[m_nInterIndex].nX0;
|
|
int nX1 = m_pInter[m_nInterIndex].nX1;
|
|
m_nInterCount += m_pInter[m_nInterIndex].nCount;
|
|
++m_nInterIndex;
|
|
|
|
while ( m_nInterIndex < m_nInterLen && ( m_pInter[m_nInterIndex].nX0 <= nX1 || ( m_bEO ? (m_nInterCount & 1) : (m_nInterCount != 0) ) ) )
|
|
{
|
|
if ( m_pInter[m_nInterIndex].nX1 > nX1 )
|
|
{
|
|
nX1 = m_pInter[m_nInterIndex].nX1;
|
|
}
|
|
m_nInterCount += m_pInter[m_nInterIndex].nCount;
|
|
++m_nInterIndex;
|
|
}
|
|
if ( nX0 < 0 )
|
|
{
|
|
nX0 = 0;
|
|
}
|
|
++nX1;
|
|
if ( nX1 > pAABuffer->GetWidth() )
|
|
{
|
|
nX1 = pAABuffer->GetWidth();
|
|
}
|
|
|
|
if ( nX0 < nX1 )
|
|
{
|
|
int nTempX = nX0;
|
|
SColorPointer pColor = pAABuffer->GetData() + nIndexY * pAABuffer->GetStride() + (nTempX >> 3);
|
|
|
|
if ( nTempX & 7 )
|
|
{
|
|
unsigned char unMask = 0xff >> (nTempX & 7);
|
|
if ( ( nTempX & ~7 ) == ( nX1 & ~7 ) )
|
|
{
|
|
unMask &= (unsigned char)(0xff00 >> (nX1 & 7));
|
|
}
|
|
*pColor++ |= unMask;
|
|
nTempX = (nTempX & ~7) + 8;
|
|
}
|
|
for (; nTempX + 7 < nX1; nTempX += 8)
|
|
{
|
|
*pColor++ |= 0xff;
|
|
}
|
|
if ( nTempX < nX1 )
|
|
{
|
|
*pColor |= (unsigned char)(0xff00 >> (nX1 & 7));
|
|
}
|
|
}
|
|
if ( nX0 < nMinX )
|
|
{
|
|
nMinX = nX0;
|
|
}
|
|
if ( nX1 > nMaxX )
|
|
{
|
|
nMaxX = nX1;
|
|
}
|
|
}
|
|
}
|
|
*pnX0 = nMinX / AntiAliasingSize;
|
|
*pnX1 = (nMaxX - 1) / AntiAliasingSize;
|
|
}
|
|
|
|
void SXPathScanner::ClipAALine(SBitmap *pAABuffer, int *pnX0, int *pnX1, int nY)
|
|
{
|
|
int nX0, nX1;
|
|
|
|
for ( int nIndexY = 0; nIndexY < AntiAliasingSize; ++nIndexY )
|
|
{
|
|
int nXX = *pnX0 * AntiAliasingSize;
|
|
ComputeIntersections( AntiAliasingSize * nY + nIndexY );
|
|
while ( m_nInterIndex < m_nInterLen && nXX < (*pnX1 + 1) * AntiAliasingSize )
|
|
{
|
|
nX0 = m_pInter[m_nInterIndex].nX0;
|
|
nX1 = m_pInter[m_nInterIndex].nX1;
|
|
m_nInterCount += m_pInter[m_nInterIndex].nCount;
|
|
++m_nInterIndex;
|
|
|
|
while ( m_nInterIndex < m_nInterLen && ( m_pInter[m_nInterIndex].nX0 <= nX1 || ( m_bEO ? (m_nInterCount & 1) : (m_nInterCount != 0) ) ) )
|
|
{
|
|
if ( m_pInter[m_nInterIndex].nX1 > nX1 )
|
|
{
|
|
nX1 = m_pInter[m_nInterIndex].nX1;
|
|
}
|
|
m_nInterCount += m_pInter[m_nInterIndex].nCount;
|
|
++m_nInterIndex;
|
|
}
|
|
if ( nX0 > pAABuffer->GetWidth() )
|
|
{
|
|
nX0 = pAABuffer->GetWidth();
|
|
}
|
|
|
|
// Óñòàíàâëèâàåì 0
|
|
if ( nXX < nX0 )
|
|
{
|
|
SColorPointer pColor = pAABuffer->GetData() + nIndexY * pAABuffer->GetStride() + (nXX >> 3);
|
|
if ( nXX & 7 )
|
|
{
|
|
unsigned char unMask = (unsigned char)(0xff00 >> (nXX & 7));
|
|
if ( (nXX & ~7) == (nX0 & ~7) )
|
|
{
|
|
unMask |= 0xff >> (nX0 & 7);
|
|
}
|
|
*pColor++ &= unMask;
|
|
nXX = (nXX & ~7) + 8;
|
|
}
|
|
for (; nXX + 7 <= nX0; nXX += 8)
|
|
{
|
|
*pColor++ = 0x00;
|
|
}
|
|
if ( nXX <= nX0 )
|
|
{
|
|
*pColor &= 0xff >> (nX0 & 7);
|
|
}
|
|
}
|
|
if ( nX1 >= nXX )
|
|
{
|
|
nXX = nX1 + 1;
|
|
}
|
|
}
|
|
nX0 = (*pnX1 + 1) * AntiAliasingSize;
|
|
|
|
// Óñòàíàâëèâàåì 0
|
|
if ( nXX < nX0 )
|
|
{
|
|
SColorPointer pColor = pAABuffer->GetData() + nIndexY * pAABuffer->GetStride() + (nXX >> 3);
|
|
if ( nXX & 7 )
|
|
{
|
|
unsigned char unMask = (unsigned char)(0xff00 >> (nXX & 7));
|
|
if ( (nXX & ~7) == (nX0 & ~7) )
|
|
{
|
|
unMask &= 0xff >> (nX0 & 7);
|
|
}
|
|
*pColor++ &= unMask;
|
|
nXX = (nXX & ~7) + 8;
|
|
}
|
|
for (; nXX + 7 <= nX0; nXX += 8)
|
|
{
|
|
*pColor++ = 0x00;
|
|
}
|
|
if ( nXX <= nX0 )
|
|
{
|
|
*pColor &= 0xff >> (nX0 & 7);
|
|
}
|
|
}
|
|
}
|
|
}
|