Files
core/ASCImageStudio3/AVSImageView5/Tracking/TextPainterStatic.h

2326 lines
68 KiB
C++

#pragma once
#include <ImageColors.h>
#include <Utilities.h>
#include <ImageIO.h>
#include <ImagePaintState.h>
#include <ImageSerializeObjects.h>
#include <TransformConstants.h>
#include <ImageTransformsCore.h>
#include <ImageTransformsCoreConstants.h>
#include <ImageTransformsCoreWrappers.h>
#include <ImageTransformsCoreGdiplus.h>
#include <ImageTransformsCoreBGRA.h>
#include <ImageTransformsCoreCOM.h>
#include <ImagePaintCompose.h>
#include <ImagePaintState.h>
#include <ImageCaching.h>
namespace Tracking
{
namespace Paint
{
namespace Text
{
namespace Constants
{
enum ETextFillMode
{
c_nTextFillMode_Normal = 0,
c_nTextFillMode_Letter = 1,
c_nTextFillMode_HorCylinder = 2,
c_nTextFillMode_VerCylinder = 3
};
enum ETextAnimation
{
c_nTextAnim_AlphaSimple = 1, // ïðîñòîé fade íà âñå áóêâû îäíîâðåìåííî
c_nTextAnim_AlphaLetterAZ = 2, // ïîáóêâåííûé fade ñëåâà íà ïðàâî
c_nTextAnim_AlphaLetterRandom = 3, // ïëàâíûé ïîáóêâåííûé fade â ñëó÷àéíîì ïîðÿäêå
c_nTextAnim_AlphaLetterAZSmooth = 4, // ïëàâíûé ïîáóêâåííûé fade ñëåâà íà ïðàâî
c_nTextAnim_AlphaLetterRandomGlass = 5, // ïëàâíûé ïîáóêâåííûé fade â ñëó÷àéíîì ïîðÿäêå c ýôôåêòîì Ëóïà
c_nTextAnim_AlphaLetterAZSmoothGlass = 6, // ïëàâíûé ïîáóêâåííûé fade ñëåâà íà ïðàâî ñ ýôôåêòîì Ëóïà
c_nTextAnim_AlphaLetterRandomScale = 7, // ïëàâíûé ïîáóêâåííûé fade â ñëó÷àéíîì ïîðÿäêå c óâåëè÷åíèåì
c_nTextAnim_AlphaLetterAZSmoothScale = 8, // ïëàâíûé ïîáóêâåííûé fade ñëåâà íà ïðàâî ñ óâåëè÷åíèåì
c_nTextAnim_AlphaDualToCenter = 80, // ïëàâíûé ïîáóêâåííûé fade îò êðà¸â ê öåíòðó
c_nTextAnim_AlphaDualFromCenter = 81, // ïëàâíûé ïîáóêâåííûé fade îò öåíòðà ê êðàÿì
c_nTextAnim_DropLeft = 10, // ïîáóêâåííûé drop ñëåâà ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropTop = 11, // ïîáóêâåííûé drop ñâåðõó ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropRight = 12, // ïîáóêâåííûé drop ñïðàâà ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropBottom = 13, // ïîáóêâåííûé drop ñíèçó ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropTopLeft = 14, // ïîáóêâåííûé drop ñíèçó ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropTopRight = 15, // ïîáóêâåííûé drop ñíèçó ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropBottomLeft = 16, // ïîáóêâåííûé drop ñíèçó ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropBottomRight = 17, // ïîáóêâåííûé drop ñíèçó ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropRotate1 = 20, // ïîáóêâåííûé rotate ïðîòèâ ÷àñîâîé ñòðåëêè ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_DropRotate2 = 21, // ïîáóêâåííûé rotate ïî ÷àñîâîé ñòðåëêå ñ èçìåíåíèåì ïðîçðà÷íîñòè áóêâ
c_nTextAnim_MoveLeft = 30, // äâèæåíèå òåêñòà âëåâî
c_nTextAnim_MoveTop = 31, // äâèæåíèå òåêñòà ââåðõ
c_nTextAnim_MoveRight = 32, // äâèæåíèå òåêñòà âïðàâî
c_nTextAnim_MoveBottom = 33, // äâèæåíèå òåêñòà âíèç
c_nTextAnim_MoveTopLeft = 34, // äâèæåíèå òåêñòà èç âåðõíåãî ëåâîãî óãëà
c_nTextAnim_MoveTopRight = 35, // äâèæåíèå òåêñòà èç âåðõíåãî ïðàâîãî óãëà
c_nTextAnim_MoveBottomLeft = 36, // äâèæåíèå òåêñòà èç íèæíåãî ëåâîãî óãëà
c_nTextAnim_MoveBottomRight = 37, // äâèæåíèå òåêñòà èç íèæíåãî ïðàâîãî óãëà
c_nTextAnim_ScaleFromSmall = 40, // óâåëè÷åíèå äî íîðìàëüíîãî ðàçìåðà (ñ àëüôîé)
c_nTextAnim_ScaleFromLarge = 41, // óìåíüøåíèå äî íîðìàëüíîãî ðàçìåðà (ñ àëüôîé)
c_nTextAnim_ScaleFromXLargeYSmall = 42, // óìåíüøåíèå ïî Õ è óâåëè÷åíèå ïî Y (ñ àëüôîé)
c_nTextAnim_ScaleFromXSmallYLarge = 43, // óâåëè÷åíèå ïî Õ è óìåíüøåíèå ïî Y (ñ àëüôîé)
c_nTextAnim_ScaleFromSmallDropFromTop = 44, // óâåëè÷åíèå è ïàäåíèå ñâåðõó (ñ àëüôîé)
c_nTextAnim_ScaleFromSmallDropFromBottom = 45, // óâåëè÷åíèå è ïðèë¸ò ñíèçó (ñ àëüôîé)
c_nTextAnim_Rotate1 = 50, // âðàùåíèå ïî ÷àñîâîé ñòðåëêå
c_nTextAnim_Rotate2 = 51, // âðàùåíèå ïðîòèâ ÷àñîâîé ñòðåëêè
c_nTextAnim_RotateFromSmall1 = 52, // âðàùåíèå ïî ÷àñîâîé ñòðåëêå ñ óâåëè÷åíèåì
c_nTextAnim_RotateFromSmall2 = 53, // âðàùåíèå ïðîòèâ ÷àñîâîé ñòðåëêè ñ óâåëè÷åíèåì
c_nTextAnim_RotateFromLarge1 = 54, // âðàùåíèå ïî ÷àñîâîé ñòðåëêå ñ óìåíüøåíèåì
c_nTextAnim_RotateFromLarge2 = 55, // âðàùåíèå ïðîòèâ ÷àñîâîé ñòðåëêè ñ óìåíüøåíèåì
c_nTextAnim_StretchScale = 60, // ïîáóêâåííîå ðàñøèðåíèå
c_nTextAnim_StretchHorScale = 61, // ïîáóêâåííîå ðàñøèðåíèå ïî ãîðèçîíòàëè
c_nTextAnim_StretchVerScale = 62, // ïîáóêâåííîå ðàñøèðåíèå ïî âåðòèêàëè
c_nTextAnim_StretchLVerScale = 63, // ïîáóêâåííîå ñæàòèå ïî âåðòèêàëè
c_nTextAnim_StretchLVerHorScale = 64, // ïîáóêâåííîå ðàñòÿæåíèå ïî ãîðèçîíòàëè è ñæàòèå ïî âåðòèêàëè
c_nTextAnim_StretchSmall = 65, // ðàñòÿæåíèå ïî ãîðèçîíòàëè ñ àëüôîé
c_nTextAnim_StretchSmallScale = 66, // ðàñòÿæåíèå ïî ãîðèçíîòàëè ñ ðàñøèðåíèåì áóêâ ïî ãîðèçîíòàëè è âåðòèêàëè
c_nTextAnim_StretchSmallHorScale = 67, // ðàñòÿæåíèå ïî ãîðèçíîòàëè ñ ðàñøèðåíèåì ïî ãîðèçîíòàëè
c_nTextAnim_StretchSmallVerScale = 68, // ðàñòÿæåíèå ïî ãîðèçíîòàëè ñ ðàñøèðåíèåì áóêâ ïî âåðòèêàëè
c_nTextAnim_StretchSmallLargeVerScale = 69, // ðàñòÿæåíèå ïî ãîðèçíîòàëè ñ ñæàòèåì áóêâ ïî âåðòèêàëè
c_nTextAnim_StretchSmallLargeVerHorScale = 70, // ðàñòÿæåíèå ïî ãîðèçíîòàëè ñ ñæàòèåì áóêâ ïî âåðòèêàëè è ðàñøèðåíèåì ïî ãîðèçîíòàëè
c_nTextAnim_StretchLarge = 71, // ñæàòèå ïî ãîðèçîíòàëè ñ àëüôîé
c_nTextAnim_StretchLargeScale = 72, // ñæàòèå ïî ãîðèçíîòàëè ñ ìàñøòàáèðîâàíèåì
c_nTextAnim_StretchLargeHorScale = 73, // ñæàòèå ïî ãîðèçíîòàëè ñ ðàñøèðåíèåì áóêâ ïî ãîðèçîíòàëè
c_nTextAnim_StretchLargeVerScale = 74, // ñæàòèå ïî ãîðèçíîòàëè ñ ðàñøèðåíèåì áóêâ ïî âåðòèêàëè
c_nTextAnim_StretchLargeLargeVerScale = 75, // ñæàòèå ïî ãîðèçíîòàëè ñ æàòèåì áóêâ ïî âåðòèêàëè
c_nTextAnim_StretchLargeLargeVerHorScale = 76, // ñæàòèå ïî ãîðèçíîòàëè ñ ñæàòèåì áóêâ ïî âåðòèêàëè è ðàñøèðåíèåì ïî ãîðèçîíòàëè
};
}
namespace Transforms
{
class CTimeMarks
{
protected:
double* m_pBuffer;
int m_nCountPoints;
int m_nTotalPoints;
public:
CTimeMarks()
: m_pBuffer( 0 ), m_nCountPoints( 0 ), m_nTotalPoints( 0 )
{
}
CTimeMarks( const CTimeMarks& other )
: m_pBuffer( 0 ), m_nCountPoints( 0 ), m_nTotalPoints( 0 )
{
if( other.m_nCountPoints <= 0 )
return;
int nTotalSize = AlignSize(m_nCountPoints);
double* pNewBuffer = new double[nTotalSize];
if( !pNewBuffer )
return;
memcpy( pNewBuffer, other.m_pBuffer, other.m_nCountPoints * sizeof(double) );
m_pBuffer = pNewBuffer;
m_nCountPoints = other.m_nCountPoints;
m_nTotalPoints = nTotalSize;
}
~CTimeMarks()
{
if( m_pBuffer )
delete [] m_pBuffer;
}
void Clear()
{
if( m_pBuffer )
{
delete [] m_pBuffer;
m_pBuffer = 0;
}
m_nCountPoints = 0;
m_nTotalPoints = 0;
}
const CTimeMarks& operator = (const CTimeMarks& other)
{
if( m_nTotalPoints < other.m_nCountPoints )
{
int nTotalSize = AlignSize( other.m_nCountPoints );
double* pNewBuffer = new double[nTotalSize];
if( !pNewBuffer )
{
// êîïèðóåì õîòÿ áû ÷àñòü äàííûõ
memcpy( m_pBuffer, other.m_pBuffer, m_nTotalPoints * sizeof(double) );
m_nCountPoints = m_nTotalPoints;
return *this;
}
if( m_pBuffer )
delete [] m_pBuffer;
m_pBuffer = pNewBuffer;
m_nTotalPoints = nTotalSize;
}
if( other.m_nCountPoints > 0 )
{
memcpy( m_pBuffer, other.m_pBuffer, other.m_nCountPoints * sizeof(double) );
}
m_nCountPoints = other.m_nCountPoints;
return *this;
}
BOOL ReservePoints( int nCountPoints )
{
if( m_nCountPoints + nCountPoints > m_nTotalPoints )
{
int nTotalSize = AlignSize( m_nCountPoints + nCountPoints );
double* pNewBuffer = new double[nTotalSize];
if( !pNewBuffer )
return FALSE;
if( m_pBuffer )
{
memcpy( pNewBuffer, m_pBuffer, m_nCountPoints * sizeof(double) );
delete [] m_pBuffer;
}
m_pBuffer = pNewBuffer;
m_nTotalPoints = nTotalSize;
}
return TRUE;
}
BOOL AddPoint( double duration )
{
if( !ReservePoints( 1 ) )
return FALSE;
ApplyPoint( duration );
return TRUE;
}
BOOL AddPoints( double* pPoints, int nCountPoints, int nStride )
{
if( !pPoints )
return FALSE;
if( !ReservePoints( nCountPoints ) )
return FALSE;
while( nCountPoints-- > 0 )
{
ApplyPoint( *pPoints );
pPoints = (double*)(((__int8*)pPoints) + nStride);
}
return TRUE;
}
void RemoveLastPoint()
{
if( m_nCountPoints > 0 )
{
m_nCountPoints -= 1;
}
}
void RemoveLastPoints( int nCountPoints )
{
if( nCountPoints > 0 )
{
m_nCountPoints -= nCountPoints;
if( m_nCountPoints < 0 )
m_nCountPoints = 0;
}
}
int GetCountPoints() const
{
// íóëåâàÿ âðåìåííàÿ ìåòêà ïðèñóòñòâóåò âèðòóàëüíî,
// ïîýòîìó êîëè÷åñòâî ìåòîê âñåãäà ðàâíî m_nCountPoints + 1
return m_nCountPoints + 1;
}
int GetTotalPoints() const
{
// íóëåâàÿ âðåìåííàÿ ìåòêà ïðèñóòñòâóåò âèðòóàëüíî,
// ïîýòîìó êîëè÷åñòâî ìåòîê âñåãäà ðàâíî m_nTotalPoints + 1
return m_nTotalPoints + 1;
}
void GetMarks( int& nFirstMark, int& nSecondMark, double& dMarkTime, double dTime ) const
{
nFirstMark = 0;
nSecondMark = 0;
dMarkTime = 0;
if( dTime <= 0 )
return;
if( dTime >= 1 )
{
nFirstMark = m_nCountPoints;
nSecondMark = m_nCountPoints;
dMarkTime = 1;
return;
}
if( m_nCountPoints <= 1 )
{
nSecondMark = m_nCountPoints;
dMarkTime = dTime;
return;
}
dTime *= m_pBuffer[m_nCountPoints - 1];
// TODO: ñäåëàòü áèíàðíûé ïîèñê!
double offset = 0;
for( int nPoint = 0; nPoint < m_nCountPoints; ++nPoint )
{
if( dTime <= m_pBuffer[nPoint] )
{
nFirstMark = nPoint;
nSecondMark = nPoint + 1;
dMarkTime = (dTime - offset) / (m_pBuffer[nPoint] - offset);
return;
}
offset = m_pBuffer[nPoint];
}
}
protected:
static int AlignSize( int size )
{
return size + 3 & ~3;
}
void ApplyPoint( double duration )
{
if( duration < IPP_EPS23 )
duration = IPP_EPS23;
if( duration > IPP_MAX_32S )
duration = IPP_MAX_32S;
if( m_nCountPoints > 0 )
duration += m_pBuffer[m_nCountPoints - 1];
m_pBuffer[m_nCountPoints++] = duration;
}
};
template<typename T>
class CTransforms
{
public:
struct TTransform
{
T transform;
int interpolation;
TTransform()
: transform(),
interpolation( TRUE )
{
}
};
struct TData
{
TTransform data;
double duration;
TData()
: data(), duration( 1 )
{
}
};
private:
CTimeMarks m_oMarks;
TTransform* m_pData;
public:
CTransforms()
: m_oMarks(), m_pData( 0 )
{
}
CTransforms( const CTransforms& other )
: m_oMarks( other.m_oMarks ), m_pData( 0 )
{
CreateBuffer( other.m_pData );
}
~CTransforms()
{
Clear();
}
const CTransforms& operator = (const CTransforms& other)
{
RemoveBuffer();
m_oMarks = other.m_oMarks;
CreateBuffer( other.m_pData );
return this*;
}
void Clear()
{
RemoveBuffer();
m_oMarks.Clear();
}
void AddItems( TData* arrItems, int nCountItems )
{
if( !arrItems && nCountItems <= 0 )
return;
int nSize = m_oMarks.GetCountPoints();
int nMarks = nCountItems;
if( !m_pData )
{
--nSize;
--nMarks;
}
if( m_oMarks.AddPoints( &(arrItems[m_pData ? 0 : 1].duration), nMarks, sizeof(TData)) )
{
TTransform* pBuffer = AllocateBuffer( nSize + nCountItems );
if( pBuffer )
{
if( m_pData )
{
memcpy( pBuffer, m_pData, nSize * sizeof(TTransform) );
FreeBuffer( m_pData );
}
IppiSize bound = {sizeof(TTransform), nCountItems};
ippiCopy_8u_C1R( (Ipp8u*)(&arrItems->data), sizeof(TData), (Ipp8u*)(pBuffer + nSize), sizeof(TTransform), bound );
m_pData = pBuffer;
}
else
{
m_oMarks.RemoveLastPoints( nMarks );
}
}
}
void Interpolate( T& result, double time ) const
{
if( m_pData )
{
int index1 = 0;
int index2 = 0;
m_oMarks.GetMarks( index1, index2, time, time );
TTransform* pItem1 = m_pData + index1;
TTransform* pItem2 = m_pData + index2;
if( pItem2->interpolation && pItem1 != pItem2 )
result.Interpolate( pItem1->transform, pItem2->transform, time );
else
result = pItem2->transform;
}
}
protected:
static TTransform* AllocateBuffer( int nCountItems )
{
return (TTransform*)( new __int64[nCountItems * sizeof(TTransform) >> 3] );
}
static void FreeBuffer( TTransform* pBuffer )
{
delete [] (__int64*)(pBuffer);
}
void RemoveBuffer()
{
if( m_pData )
{
TTransform* pBuffer = m_pData;
int nCountItems = m_oMarks.GetCountPoints();
for( ; nCountItems > 0; --nCountItems, ++pBuffer )
{
pBuffer->~TTransform();
}
FreeBuffer( m_pData );
m_pData = 0;
m_oMarks.RemoveLastPoints( nCountItems );
}
}
BOOL CreateBuffer( TTransform* pOtherBuffer )
{
int nSize = m_oMarks.GetCountPoints();
if( pOtherBuffer && nSize > 0 )
{
m_pData = AllocateBuffer( nSize );
if( m_pData )
{
memcpy( m_pData, pOtherBuffer, nSize * sizeof(TTransform) );
return TRUE;
}
}
m_oMarks.RemoveLastPoints( nSize );
return FALSE;
}
};
class CScale
{
public:
double dScaleX;
double dScaleY;
CScale()
: dScaleX( 1 ), dScaleY( 1 )
{
}
void SetParams( double x, double y )
{
dScaleX = x;
dScaleY = y;
}
void Interpolate( const CScale& src1, const CScale& src2, double time )
{
dScaleX = (src2.dScaleX - src1.dScaleX) * time + src1.dScaleX;
dScaleY = (src2.dScaleY - src1.dScaleY) * time + src1.dScaleY;
}
};
class CScaleAt
{
public:
double dScaleX;
double dScaleY;
double dX;
double dY;
CScaleAt()
: dScaleX( 1 ), dScaleY( 1 ), dX( 0 ), dY( 0 )
{
}
void SetParams( double scaleX, double scaleY, double x, double y )
{
dScaleX = scaleX;
dScaleY = scaleY;
dX = x;
dY = y;
}
void Interpolate( const CScaleAt& src1, const CScaleAt& src2, double time )
{
dScaleX = (src2.dScaleX - src1.dScaleX) * time + src1.dScaleX;
dScaleY = (src2.dScaleY - src1.dScaleY) * time + src1.dScaleY;
dX = (src2.dX - src1.dX) * time + src1.dX;
dY = (src2.dY - src1.dY) * time + src1.dY;
}
};
class CShear
{
public:
double dX;
double dY;
CShear()
: dX( 0 ), dY( 0 )
{
}
void SetParams( double x, double y )
{
dX = x;
dY = y;
}
void Interpolate( const CShear& src1, const CShear& src2, double time )
{
dX = (src2.dX - src1.dX) * time + src1.dX;
dY = (src2.dY - src1.dY) * time + src1.dY;
}
};
class CTranslate
{
public:
double dX;
double dY;
CTranslate()
: dX( 0 ), dY( 0 )
{
}
void SetParams( double x, double y )
{
dX = x;
dY = y;
}
void Interpolate( const CTranslate& src1, const CTranslate& src2, double time )
{
dX = (src2.dX - src1.dX) * time + src1.dX;
dY = (src2.dY - src1.dY) * time + src1.dY;
}
};
class CRotate
{
public:
double dAngle;
CRotate()
: dAngle( 0 )
{
}
void SetParams( double angle )
{
dAngle = angle;
}
void Interpolate( const CRotate& src1, const CRotate& src2, double time )
{
dAngle = (src2.dAngle - src1.dAngle) * time + src1.dAngle;
}
};
class CRotateAt
{
public:
double dAngle;
double dX;
double dY;
CRotateAt()
: dAngle( 0 ), dX( 0 ), dY( 0 )
{
}
void SetParams( double angle, double x, double y )
{
dAngle = angle;
dX = x;
dY = y;
}
void Interpolate( const CRotateAt& src1, const CRotateAt& src2, double time )
{
dAngle = (src2.dAngle - src1.dAngle) * time + src1.dAngle;
dX = (src2.dX - src1.dX) * time + src1.dX;
dY = (src2.dY - src1.dY) * time + src1.dY;
}
};
class CTransparency
{
public:
double dFactor;
CTransparency()
: dFactor( 0 )
{
}
void SetParams( double factor )
{
dFactor = factor;
}
void Interpolate( const CTransparency& src1, const CTransparency& src2, double time )
{
dFactor = (src2.dFactor - src1.dFactor) * time + src1.dFactor;
}
};
class CGeometryTransforms
{
public:
virtual void AddToMatrix( Gdiplus::Matrix& oMatrix, double dTime ) = 0;
};
class CScaleTransforms : public CTransforms<CScale>, public CGeometryTransforms
{
public:
virtual void AddToMatrix( Gdiplus::Matrix& oMatrix, double dTime )
{
CScale oScale;
Interpolate( oScale, dTime );
float fx = oScale.dScaleX > 0 ? \
(oScale.dScaleX > IPP_EPS23 ? float(oScale.dScaleX) : IPP_EPS23) : \
(oScale.dScaleX < -IPP_EPS23 ? float(oScale.dScaleX) : -IPP_EPS23);
float fy = oScale.dScaleY > 0 ? \
(oScale.dScaleY > IPP_EPS23 ? float(oScale.dScaleY) : IPP_EPS23) : \
(oScale.dScaleY < -IPP_EPS23 ? float(oScale.dScaleY) : -IPP_EPS23);
oMatrix.Scale( fx, fy, Gdiplus::MatrixOrderAppend );
}
};
class CScaleAtTransforms : public CTransforms<CScaleAt>, public CGeometryTransforms
{
public:
virtual void AddToMatrix( Gdiplus::Matrix& oMatrix, double dTime )
{
CScaleAt oScale;
Interpolate( oScale, dTime );
float fx = oScale.dScaleX > 0 ? \
(oScale.dScaleX > IPP_EPS23 ? float(oScale.dScaleX) : IPP_EPS23) : \
(oScale.dScaleX < -IPP_EPS23 ? float(oScale.dScaleX) : -IPP_EPS23);
float fy = oScale.dScaleY > 0 ? \
(oScale.dScaleY > IPP_EPS23 ? float(oScale.dScaleY) : IPP_EPS23) : \
(oScale.dScaleY < -IPP_EPS23 ? float(oScale.dScaleY) : -IPP_EPS23);
oMatrix.Translate( Gdiplus::REAL(oScale.dX), Gdiplus::REAL(oScale.dY), Gdiplus::MatrixOrderAppend );
oMatrix.Scale( fx, fy, Gdiplus::MatrixOrderAppend );
oMatrix.Translate( Gdiplus::REAL(-oScale.dX), Gdiplus::REAL(-oScale.dY), Gdiplus::MatrixOrderAppend );
}
};
class CShearTransforms : public CTransforms<CShear>, public CGeometryTransforms
{
public:
virtual void AddToMatrix( Gdiplus::Matrix& oMatrix, double dTime )
{
CShear oShear;
Interpolate( oShear, dTime );
oMatrix.Shear( Gdiplus::REAL(oShear.dX), Gdiplus::REAL(oShear.dY), Gdiplus::MatrixOrderAppend );
}
};
class CTranslateTransforms : public CTransforms<CTranslate>, public CGeometryTransforms
{
public:
virtual void AddToMatrix( Gdiplus::Matrix& oMatrix, double dTime )
{
CTranslate oTranslate;
Interpolate( oTranslate, dTime );
oMatrix.Translate( Gdiplus::REAL(oTranslate.dX), Gdiplus::REAL(oTranslate.dY), Gdiplus::MatrixOrderAppend );
}
};
class CRotateTransforms : public CTransforms<CRotate>, public CGeometryTransforms
{
public:
virtual void AddToMatrix( Gdiplus::Matrix& oMatrix, double dTime )
{
CRotate oRotate;
Interpolate( oRotate, dTime );
oMatrix.Rotate( Gdiplus::REAL(oRotate.dAngle), Gdiplus::MatrixOrderAppend );
}
};
class CRotateAtTransforms : public CTransforms<CRotateAt>, public CGeometryTransforms
{
public:
virtual void AddToMatrix( Gdiplus::Matrix& oMatrix, double dTime )
{
CRotateAt oRotateAt;
Interpolate( oRotateAt, dTime );
if( abs(oRotateAt.dX) > IPP_EPS23 || abs(oRotateAt.dY) > IPP_EPS23 )
oMatrix.RotateAt( Gdiplus::REAL(oRotateAt.dAngle), Gdiplus::PointF(Gdiplus::REAL(oRotateAt.dX), Gdiplus::REAL(oRotateAt.dY)), Gdiplus::MatrixOrderAppend );
else
oMatrix.Rotate( Gdiplus::REAL(oRotateAt.dAngle) );
}
};
class CTransparencyTransforms : public CTransforms<CTransparency>
{
public:
void AddToFactor( double& factor, double dTime )
{
CTransparency transparency;
Interpolate( transparency, dTime );
factor *= transparency.dFactor;
}
};
class CTransformManager
{
protected:
ATL::CSimpleArray<CGeometryTransforms*> m_arrpGeometryTransforms;
ATL::CSimpleArray<CTransparencyTransforms*> m_arrpTransparencyTransforms;
double m_dTimeOffset;
double m_dTimeDuration;
public:
CTransformManager()
{
m_dTimeOffset = 0;
m_dTimeDuration = 1;
}
~CTransformManager()
{
ReleaseDynamicData();
}
void Clear()
{
m_dTimeOffset = 0;
m_dTimeDuration = 1;
ReleaseDynamicData();
}
void Update( double dTime, Gdiplus::Matrix& oTransform, double& dTransparency )
{
dTime = (dTime - m_dTimeOffset) / m_dTimeDuration;
// âûïîëíÿåì ïîñëåäîâàòåëüíî ãåîìåòðè÷åñêèå òðàíñôîðìàöèè
CGeometryTransforms** pGeometryData = m_arrpGeometryTransforms.GetData();
if( pGeometryData )
{
int nDataSize = m_arrpGeometryTransforms.GetSize();
for( ; nDataSize > 0; --nDataSize, ++pGeometryData )
{
CGeometryTransforms* pTransforms = *pGeometryData;
if( pTransforms )
{
pTransforms->AddToMatrix( oTransform, dTime );
}
}
}
// âûïîëíÿåì ïîñëåäîâàòåëüíî öâåòîâûå òðàíñôîðìàöèè
CTransparencyTransforms** pTransparencyData = m_arrpTransparencyTransforms.GetData();
if( pTransparencyData )
{
int nDataSize = m_arrpTransparencyTransforms.GetSize();
for( ; nDataSize > 0; --nDataSize, ++pTransparencyData )
{
CTransparencyTransforms* pTransforms = *pTransparencyData;
if( pTransforms )
{
pTransforms->AddToFactor( dTransparency, dTime );
}
}
}
}
void SetTimeParams( double offset, double duration )
{
m_dTimeOffset = offset;
m_dTimeDuration = duration > IPP_EPS52 ? duration : 1;
}
void SetTimeParams( double nLetter, double nCountLetters, double dCountActiveLetters )
{
double offset = 0;
double duration = 1;
if( nCountLetters > 1 && dCountActiveLetters > 0 )
{
double factor = (nCountLetters - 1 + dCountActiveLetters);
offset = nLetter / factor;
duration = dCountActiveLetters / factor;
}
m_dTimeOffset = offset;
m_dTimeDuration = duration;
}
CScaleTransforms* CreateScaleTransforms()
{
CScaleTransforms* pTransforms = new CScaleTransforms();
if( pTransforms )
{
if( m_arrpGeometryTransforms.Add( pTransforms ) )
return pTransforms;
delete pTransforms;
}
return NULL;
}
CScaleAtTransforms* CreateScaleAtTransforms()
{
CScaleAtTransforms* pTransforms = new CScaleAtTransforms();
if( pTransforms )
{
if( m_arrpGeometryTransforms.Add( pTransforms ) )
return pTransforms;
delete pTransforms;
}
return NULL;
}
CShearTransforms* CreateShearTransforms()
{
CShearTransforms* pTransforms = new CShearTransforms();
if( pTransforms )
{
if( m_arrpGeometryTransforms.Add( pTransforms ) )
return pTransforms;
delete pTransforms;
}
return NULL;
}
CTranslateTransforms* CreateTranslateTransforms()
{
CTranslateTransforms* pTransforms = new CTranslateTransforms();
if( pTransforms )
{
if( m_arrpGeometryTransforms.Add( pTransforms ) )
return pTransforms;
delete pTransforms;
}
return NULL;
}
CRotateTransforms* CreateRotateTransforms()
{
CRotateTransforms* pTransforms = new CRotateTransforms();
if( pTransforms )
{
if( m_arrpGeometryTransforms.Add( pTransforms ) )
return pTransforms;
delete pTransforms;
}
return NULL;
}
CRotateAtTransforms* CreateRotateAtTransforms()
{
CRotateAtTransforms* pTransforms = new CRotateAtTransforms();
if( pTransforms )
{
if( m_arrpGeometryTransforms.Add( pTransforms ) )
return pTransforms;
delete pTransforms;
}
return NULL;
}
CTransparencyTransforms* CreateTransparencyTransforms()
{
CTransparencyTransforms* pTransforms = new CTransparencyTransforms();
if( pTransforms )
{
if( m_arrpTransparencyTransforms.Add( pTransforms ) )
return pTransforms;
delete pTransforms;
}
return NULL;
}
void AddScaleTransforms( CScaleTransforms::TData* arrTransforms, int nCountTransforms )
{
CScaleTransforms* pTransforms = CreateScaleTransforms();
if( pTransforms )
{
pTransforms->AddItems( arrTransforms, nCountTransforms );
}
}
void AddScaleAtTransforms( CScaleAtTransforms::TData* arrTransforms, int nCountTransforms )
{
CScaleAtTransforms* pTransforms = CreateScaleAtTransforms();
if( pTransforms )
{
pTransforms->AddItems( arrTransforms, nCountTransforms );
}
}
void AddShearTransforms( CShearTransforms::TData* arrTransforms, int nCountTransforms )
{
CShearTransforms* pTransforms = CreateShearTransforms();
if( pTransforms )
{
pTransforms->AddItems( arrTransforms, nCountTransforms );
}
}
void AddTranslateTransforms( CTranslateTransforms::TData* arrTransforms, int nCountTransforms )
{
CTranslateTransforms* pTransforms = CreateTranslateTransforms();
if( pTransforms )
{
pTransforms->AddItems( arrTransforms, nCountTransforms );
}
}
void AddRotateTransforms( CRotateTransforms::TData* arrTransforms, int nCountTransforms )
{
CRotateTransforms* pTransforms = CreateRotateTransforms();
if( pTransforms )
{
pTransforms->AddItems( arrTransforms, nCountTransforms );
}
}
void AddRotateAtTransforms( CRotateAtTransforms::TData* arrTransforms, int nCountTransforms )
{
CRotateAtTransforms* pTransforms = CreateRotateAtTransforms();
if( pTransforms )
{
pTransforms->AddItems( arrTransforms, nCountTransforms );
}
}
void AddTransparencyTransforms( CTransparencyTransforms::TData* arrTransforms, int nCountTransforms )
{
CTransparencyTransforms* pTransforms = CreateTransparencyTransforms();
if( pTransforms )
{
pTransforms->AddItems( arrTransforms, nCountTransforms );
}
}
void AddShear( double x1, double y1, double x2, double y2, BOOL interpolation = TRUE )
{
Text::Transforms::CShearTransforms::TData arrShears[2];
arrShears[0].data.transform.SetParams( x1, y1 );
arrShears[1].data.transform.SetParams( x2, y2 );
arrShears[1].data.interpolation = interpolation;
AddShearTransforms( arrShears, 2 );
}
void AddScale( double dStartScale, double dFinishScale, BOOL interpolation = TRUE )
{
Text::Transforms::CScaleTransforms::TData arrScales[2];
arrScales[0].data.transform.SetParams( dStartScale, dStartScale );
arrScales[1].data.transform.SetParams( dFinishScale, dFinishScale );
arrScales[1].data.interpolation = interpolation;
AddScaleTransforms( arrScales, 2 );
}
void AddScale( double dStartScaleX, double dStartScaleY, double dFinishScaleX, double dFinishScaleY, BOOL interpolation = TRUE )
{
Text::Transforms::CScaleTransforms::TData arrScales[2];
arrScales[0].data.transform.SetParams( dStartScaleX, dStartScaleY );
arrScales[1].data.transform.SetParams( dFinishScaleX, dFinishScaleY );
arrScales[1].data.interpolation = interpolation;
AddScaleTransforms( arrScales, 2 );
}
void AddRotate( double dStartAngle, double dFinishAngle, BOOL interpolation = TRUE )
{
Text::Transforms::CRotateTransforms::TData arrAngles[2];
arrAngles[0].data.transform.SetParams( dStartAngle );
arrAngles[1].data.transform.SetParams( dFinishAngle );
arrAngles[1].data.interpolation = interpolation;
AddRotateTransforms( arrAngles, 2 );
}
void AddRotateAt( double dAngle1, double dAngle2, double dX1, double dY1, double dX2, double dY2, BOOL interpolation = TRUE )
{
Text::Transforms::CRotateAtTransforms::TData arrAngles[2];
arrAngles[0].data.transform.SetParams( dAngle1, dX1, dY1 );
arrAngles[1].data.transform.SetParams( dAngle2, dX2, dY2 );
arrAngles[1].data.interpolation = interpolation;
AddRotateAtTransforms( arrAngles, 2 );
}
void AddRotateAt( double dAngle1, double dAngle2, double dX, double dY, BOOL interpolation = TRUE )
{
Text::Transforms::CRotateAtTransforms::TData arrAngles[2];
arrAngles[0].data.transform.SetParams( dAngle1, dX, dY );
arrAngles[1].data.transform.SetParams( dAngle2, dX, dY );
arrAngles[1].data.interpolation = interpolation;
AddRotateAtTransforms( arrAngles, 2 );
}
void AddTranslate( double x1, double y1, double x2, double y2, BOOL interpolation = TRUE )
{
Text::Transforms::CTranslateTransforms::TData arrMoves[2];
arrMoves[0].data.transform.SetParams( x1, y1 );
arrMoves[1].data.transform.SetParams( x2, y2 );
arrMoves[1].data.interpolation = interpolation;
AddTranslateTransforms( arrMoves, 2 );
}
void AddTransparency( double dStartFactor, double dEndFactor, BOOL interpolation = TRUE )
{
Text::Transforms::CTransparencyTransforms::TData arrFactors[2];
arrFactors[0].data.transform.SetParams( dStartFactor );
arrFactors[1].data.transform.SetParams( dEndFactor );
arrFactors[1].data.interpolation = interpolation;
AddTransparencyTransforms( arrFactors, 2 );
}
void AddFadeTransparency( double dStartFactor, double dEndFactor, double nStartDuration = 1, double nEndDuration = 1 )
{
Text::Transforms::CTransparencyTransforms::TData arrFactors[3];
arrFactors[0].data.transform.SetParams( dStartFactor );
arrFactors[1].data.transform.SetParams( dEndFactor );
arrFactors[2].data.transform.SetParams( dEndFactor );
arrFactors[2].data.interpolation = FALSE;
arrFactors[2].duration = nEndDuration;
arrFactors[1].duration = nStartDuration;
AddTransparencyTransforms( arrFactors, 3 );
}
protected:
void ReleaseDynamicData()
{
// ïîñëåäîâàòåëüíî óäàëÿåì ãåîìåòðè÷åñêèå òðàíñôîðìàöèè
CGeometryTransforms** pGeometryData = m_arrpGeometryTransforms.GetData();
if( pGeometryData )
{
int nDataSize = m_arrpGeometryTransforms.GetSize();
for( ; nDataSize > 0; --nDataSize, ++pGeometryData )
{
CGeometryTransforms* pItem = *pGeometryData;
if( pItem )
{
delete pItem;
pItem = NULL;
}
}
m_arrpGeometryTransforms.RemoveAll();
}
// ïîñëåäîâàòåëüíî óäàëÿåì öâåòîâûå òðàíñôîðìàöèè
CTransparencyTransforms** pTransparencyData = m_arrpTransparencyTransforms.GetData();
if( pTransparencyData )
{
int nDataSize = m_arrpTransparencyTransforms.GetSize();
for( ; nDataSize > 0; --nDataSize, ++pTransparencyData )
{
CTransparencyTransforms* pItem = *pTransparencyData;
if( pItem )
{
delete pItem;
pItem = NULL;
}
}
m_arrpTransparencyTransforms.RemoveAll();
}
}
};
}
namespace Utils
{
enum
{
c_nLeftBound = 0,
c_nTopBound = 1,
c_nRightBound = 2,
c_nBottomBound = 3,
c_nEmptyBound = -1
};
float GetOffsetRect( const Gdiplus::RectF& oLayoutRect, const Gdiplus::RectF& oInnerRect, int nBound )
{
// ============================================================================
// âû÷èñëÿåò ñìåùåíèå âíóòðåííåãî ïðÿìîóãîëüíèêà îòíîñèòåëüíî âíåøíåãî òàê,
// ÷òî áû îíè ñîïðèêàñàëèñü
//
// nBound: 0 - left, 1 - top, 2 - right, 3 - bottom
// ============================================================================
float dMove = 0;
switch( nBound )
{
case c_nLeftBound: dMove = oLayoutRect.X - oInnerRect.X - oInnerRect.Width; break;
case c_nTopBound: dMove = oLayoutRect.Y - oInnerRect.Y - oInnerRect.Height; break;
case c_nRightBound: dMove = oLayoutRect.X - oInnerRect.X + oLayoutRect.Width; break;
case c_nBottomBound: dMove = oLayoutRect.Y - oInnerRect.Y + oLayoutRect.Height; break;
}
return dMove;
}
float GetHorOffsetCenters( const Gdiplus::RectF& oRect1, const Gdiplus::RectF& oRect2 )
{
return oRect2.X - oRect1.X + (oRect2.Width - oRect1.Width)/2;
}
float GetVerOffsetCenters( const Gdiplus::RectF& oRect1, const Gdiplus::RectF& oRect2 )
{
return oRect2.Y - oRect1.Y + (oRect2.Height - oRect1.Height)/2;
}
float GetHorCenter( const Gdiplus::RectF& oRect )
{
return oRect.X + oRect.Width * 0.5f;
}
float GetVerCenter( const Gdiplus::RectF& oRect )
{
return oRect.Y + oRect.Height * 0.5f;
}
Gdiplus::PointF GetCenter( const Gdiplus::RectF& oRect )
{
return Gdiplus::PointF( oRect.X + oRect.Width * 0.5f, oRect.Y + oRect.Height * 0.5f );
}
void ClearRect( Gdiplus::RectF& oRect )
{
oRect.X = oRect.Y = oRect.Width = oRect.Height = 0;
}
void ClearPoint( Gdiplus::PointF& oPoint )
{
oPoint.X = oPoint.Y = 0;
}
void ClearSize( Gdiplus::SizeF& oSize )
{
oSize.Width = oSize.Height = 0;
}
template<typename T>
class ArrayReflection
{
public:
T* ptr;
int count;
ArrayReflection()
: ptr( 0 ), count( 0 )
{
}
ArrayReflection( T* pData, int nCount )
: ptr( pData ), count( nCount )
{
}
ArrayReflection( ATL::CSimpleArray< T >& oArray )
: ptr( oArray.GetData() ), count( oArray.GetSize() )
{
}
ArrayReflection( ATL::CAtlArray< T >& oArray )
: ptr( oArray.GetData() ), count((int)oArray.GetCount() )
{
}
ArrayReflection( ATL::CSimpleArray< T >& oArray, int offset, int size )
: ptr( oArray.GetData() + offset ), count( size )
{
}
ArrayReflection( ATL::CAtlArray< T >& oArray, int offset, int size )
: ptr( oArray.GetData() + offset ), count( size )
{
}
T& operator [](int index)
{
return ptr[index];
}
const T& operator [](int index) const
{
return ptr[index];
}
};
class CPermutator
{
int* m_pValues;
int m_nCount;
public:
CPermutator()
{
m_pValues = NULL;
m_nCount = 0;
}
CPermutator( int nCount, BOOL bPermutate )
{
m_pValues = NULL;
m_nCount = 0;
Create( nCount, bPermutate );
}
virtual ~CPermutator()
{
Destroy();
}
void Destroy()
{
if (NULL != m_pValues)
{
delete[] m_pValues;
m_pValues = NULL;
}
m_nCount = 0;
}
void Create(int nCount, BOOL bPermutate)
{
Destroy();
if (nCount < 1)
return;
m_nCount = nCount;
m_pValues = new int[nCount];
for (int index = 0; index < nCount; ++index)
m_pValues[index] = index;
if (!bPermutate || nCount < 2)
return;
Refresh();
}
void Refresh()
{
for (int index = 0; index < m_nCount; ++index)
{
int nIndexFrom = index;
int nIndexTo = ::rand() % m_nCount;
int nValue1 = m_pValues[nIndexFrom];
int nValue2 = m_pValues[nIndexTo];
m_pValues[nIndexFrom] = nValue2;
m_pValues[nIndexTo] = nValue1;
}
}
int GetCount()
{
return m_nCount;
}
int operator[](int nIndex)
{
//if (nIndex < 0 || nIndex >= m_nCount)
// return -1;
return m_pValues[nIndex];
}
};
}
class CEntity
{
protected:
Gdiplus::Matrix m_oTransform; // òåêóùàÿ òðàíñôîðìàöèÿ
double m_dTransparency; // òåêóùaÿ ïðîçðà÷íîñòü
Transforms::CTransformManager m_oPrevTransforms; // ìåíåäæåð ïðåäòðàíñôîðìàöèé
Transforms::CTransformManager m_oPostTransforms; // ìåíåäæåð ïîñòòðàíñôîðìàöèé
public:
Gdiplus::PointF m_oOrigin; // êîîðäèíàòû îòíîñèòåëüíî ðîäèòåëÿ
Gdiplus::RectF m_oPathBoundRect; // îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê ïàò÷à áåç òðàíñôîðìàöèé
Gdiplus::PointF m_oPathCenter; // êîîðäèíàòû öåíòðà ïàò÷à (íå îáÿçàòåëüíî ãåîìåòðè÷åñêèé öåíòð!)
public:
CEntity() : m_dTransparency( 1 )
{
}
CEntity( const CEntity& other )
: m_dTransparency( other.m_dTransparency ),
m_oPrevTransforms( other.m_oPrevTransforms ),
m_oPostTransforms( other.m_oPostTransforms ),
m_oOrigin ( other.m_oOrigin ),
m_oPathBoundRect ( other.m_oPathBoundRect ),
m_oPathCenter ( other.m_oPathCenter )
{
float items[6];
other.m_oTransform.GetElements( items );
m_oTransform.SetElements( items[0], items[1], items[2], items[3], items[4], items[5] );
}
//virtual const CEntity& operator = (const CEntity& other)
//{
// return *this
//}
virtual ~CEntity()
{
}
virtual void Clear()
{
m_oTransform.Reset();
m_dTransparency = 1;
m_oPrevTransforms.Clear();
m_oPostTransforms.Clear();
Utils::ClearPoint( m_oOrigin );
Utils::ClearRect ( m_oPathBoundRect );
Utils::ClearPoint( m_oPathCenter );
}
virtual void Update( double dTime, const Gdiplus::Matrix& oParentTransform, double dParentTransparency )
{
// î÷èùàåì òåêóùèå çíà÷åíèÿ
m_oTransform.Reset();
m_dTransparency = 1;
// âûïîëíÿåì ïîñëåäîâàòåëüíûå òðàíñôîðìàöèè
if( dTime < 2 )
m_oPrevTransforms.Update( dTime, m_oTransform, m_dTransparency );
else
m_oPostTransforms.Update( 3 - dTime, m_oTransform, m_dTransparency );
// ïåðåíîñèì ïîçèöèþ îòíîñèòåëüíî âíåøíåé ñèñòåìû êîîðäèíàò
m_oTransform.Translate( m_oOrigin.X, m_oOrigin.Y, Gdiplus::MatrixOrderAppend );
// îáúåäèíÿåì ñ âíåøíèìè äàííûìè
m_oTransform.Multiply( &oParentTransform, Gdiplus::MatrixOrderAppend );
m_dTransparency *= dParentTransparency;
}
const Gdiplus::Matrix& GetTransform() const
{
return m_oTransform;
}
const double GetAlphaFactor() const
{
return m_dTransparency;
}
Transforms::CTransformManager& GetTransformManager( BOOL bPhaseIn )
{
return bPhaseIn ? m_oPrevTransforms : m_oPostTransforms;
}
void RemoveTransforms()
{
m_oPrevTransforms.Clear();
m_oPostTransforms.Clear();
}
};
class CLetterEntity : public CEntity
{
protected:
Gdiplus::RectF m_oLayoutRect; // îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê äëÿ ïàò÷à ïîñëå ïðèìåíåíèÿ òðàíñôîðìîâ
CLetterEntity* m_pNextLetter; // óêàçàòåëü íà ñëåäóþùóþ áóêâó â ñïèñêå
public:
Gdiplus::GraphicsPath* m_pPath; // óêàçàòåëü íà âíåøíèé ïàò÷. íå òðåáóåò ïðèìåíåíèÿ new/delete
BOOL m_bNotLetter; // ïðèçíàê ëèíèè ïåðå÷¸ðêèâàíèÿ(ïîä÷¸ðêèâàíèÿ) äëÿ ñòèëåé Strikeout è Underline
public:
CLetterEntity()
{
m_pNextLetter = NULL;
m_pPath = NULL;
m_bNotLetter = FALSE;
}
virtual ~CLetterEntity()
{
ReleaseChildLetter();
}
virtual void Clear()
{
ReleaseChildLetter();
CEntity::Clear();
m_pPath = NULL;
Utils::ClearRect( m_oLayoutRect );
m_bNotLetter = FALSE;
}
virtual void Update( double dTime, const Gdiplus::Matrix& oParentTransform, double dParentTransparency )
{
CEntity::Update( dTime, oParentTransform, dParentTransparency );
// êîððåêòèðóåì îòíîñèòåëüíî òðàåêòîðèè
m_oTransform.Translate( -m_oPathCenter.X, -m_oPathCenter.Y, Gdiplus::MatrixOrderPrepend );
// ðàñ÷èòûâàåì îãðàíè÷èâàþùèé ïðÿìîóãîëüíèê
if( m_pPath )
{
m_pPath->GetBounds( &m_oLayoutRect, &m_oTransform );
}
}
void Draw( Gdiplus::Graphics& oRender, const Gdiplus::RectF& oClipRect, Painter::CBrush& oBrush, Painter::CTextureManager* pTextureManager, Gdiplus::RectF* pBoundClip )
{
if( !m_pPath || m_dTransparency < IPP_EPS23 )
return;
Gdiplus::RectF oRenderRect = m_oLayoutRect;
oRenderRect.Inflate( 1, 1 );
if( FALSE == oRenderRect.Intersect( oClipRect ) )
return;
//oRender.SetTransform( &m_oTransform );
oBrush.SetTransparency( m_dTransparency );
Gdiplus::Brush* pBrush = oBrush.GetBrush( pTextureManager, pBoundClip );
if( pBrush )
{
oRender.FillPath( pBrush, m_pPath );
}
}
void DrawEdge( Gdiplus::Graphics& oRender, const Gdiplus::RectF& oClipRect, Painter::CEdgeText& oEdge )
{
if( !m_pPath || m_dTransparency < IPP_EPS23 )
return;
float fPenWidth = (float)oEdge.GetDistance();
Gdiplus::RectF oRenderRect = m_oLayoutRect;
oRenderRect.Inflate( 1 + fPenWidth, 1 + fPenWidth );
if( FALSE == oRenderRect.Intersect( oClipRect ) )
return;
oRender.SetTransform( &m_oTransform );
oEdge.SetTransparency( m_dTransparency );
Gdiplus::Pen* pPen = oEdge.GetPen();
if( pPen )
{
oRender.DrawPath( pPen, m_pPath );
}
}
void DrawShadow( Gdiplus::Graphics& oRender, const Gdiplus::RectF& oClipRect, const Gdiplus::Color oColor, float dx, float dy, int nBlurSize )
{
if( !m_pPath || m_dTransparency < IPP_EPS23 )
return;
int nMargin = 5;
Gdiplus::RectF oRenderRect = m_oLayoutRect;
oRenderRect.Inflate( float(nMargin + nBlurSize), float(nMargin + nBlurSize) );
oRenderRect.Offset( dx, dy );
if( FALSE == oRenderRect.Intersect( oClipRect ) )
return;
int w = int(oRenderRect.Width + 0.5f);
int h = int(oRenderRect.Height + 0.5f);
if( w < 1 || h < 1 )
return;
int alpha = int(oColor.GetAlpha() * m_dTransparency + 0.5f);
if( alpha < 0 ) alpha = 0;
if( alpha > 255 ) alpha = 255;
Gdiplus::Color oShadowColor(BYTE(alpha), oColor.GetR(), oColor.GetG(), oColor.GetB());
Gdiplus::SolidBrush oShadowBrush( nBlurSize > 0 ? Gdiplus::Color::Blue : oShadowColor );
Gdiplus::Bitmap oTempBitmap( w, h );
if( true )
{
Gdiplus::Graphics oLocalRender(&oTempBitmap);
oLocalRender.SetTransform( &m_oTransform );
oLocalRender.TranslateTransform( dx-oRenderRect.X, dy-oRenderRect.Y, MatrixOrderAppend );
oLocalRender.SetInterpolationMode(InterpolationModeBilinear);
oLocalRender.SetSmoothingMode(SmoothingModeAntiAlias);
oLocalRender.Clear( Gdiplus::Color(0, 0, 0, 0) );
oLocalRender.FillPath( &oShadowBrush, m_pPath );
}
GDIPLUS_ShadowBlur(oTempBitmap, nBlurSize, oShadowColor.GetValue() );
oRender.DrawImage( &oTempBitmap, oRenderRect.X, oRenderRect.Y );
}
CLetterEntity* AddChildLetter()
{
CLetterEntity* pLetter = new CLetterEntity( *this );
if( pLetter )
{
m_pNextLetter = pLetter;
}
return pLetter;
}
void ReleaseChildLetter()
{
if( m_pNextLetter )
{
m_pNextLetter->ReleaseChildLetter();
delete [] m_pNextLetter;
m_pNextLetter = NULL;
}
}
protected:
static void GDIPLUS_ShadowBlur(Gdiplus::Bitmap& oBitmap, int nBlurSize, DWORD dwShadowColor)
{
if( nBlurSize < 1 )
{
return;
}
Gdiplus::BitmapData oBitmapData;
if( Gdiplus::Ok == oBitmap.LockBits(NULL, Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &oBitmapData) )
{
BGRA_ShadowBlur((BYTE*)oBitmapData.Scan0, oBitmapData.Width, oBitmapData.Height, oBitmapData.Stride, nBlurSize, dwShadowColor);
oBitmap.UnlockBits(&oBitmapData);
}
}
static void BGRA_ShadowBlur( BYTE* pBGRA, int nWidth, int nHeight, int nStride, int nBlurSize, DWORD dwShadowColor )
{
if( !pBGRA || nWidth < 1 || nHeight < 1 || nStride < nWidth*4 || nBlurSize < 1 )
{
return;
}
int nBufStride = nWidth * 4;
BYTE* pBuffer = new BYTE[nBufStride * nHeight];
if( !pBuffer )
{
return;
}
int edge = min(nWidth, nBlurSize);
FillZeroLeftRightEdge( pBuffer, edge, nHeight, nBufStride, nWidth - edge );
BGRA_ShadowBlurVertical(pBGRA, pBuffer, nWidth, nHeight, nStride, nBufStride, nBlurSize);
BGRA_ShadowBlurHorizontal(dwShadowColor, pBuffer, pBGRA, nWidth, nHeight, nBufStride, nStride, nBlurSize);
delete[] pBuffer;
}
static BOOL BGRA_ShadowBlurHorizontal( DWORD dwColor, BYTE* pSrcData, BYTE* pDstData, int nWidth, int nHeight, int nSrcStride, int nDstStride, int nBlurSize )
{
if( !pSrcData || !pDstData || nWidth < 1 || nHeight < 1 || nSrcStride < 1 || nDstStride < 1 || nBlurSize < 1 )
{
return FALSE;
}
int nLength;
int nA, nR, nG, nB;
int nClr;
DWORD nFactor;
DWORD nClrFactor;
DWORD *pSrc, *pDst;
nA = (dwColor >> 24 & 0xff);
nR = (dwColor >> 16 & 0xff);
nG = (dwColor >> 8 & 0xff);
nB = (dwColor >> 0 & 0xff);
nFactor = (nBlurSize << 1) + 1;
nFactor *= nFactor;
for( ; nHeight > 0; --nHeight, pSrcData += nSrcStride, pDstData += nDstStride )
{
pSrc = (DWORD*)pSrcData;
pDst = (DWORD*)pDstData;
nClr = 0;
// ïîäñ÷¸ò íà÷àëüíîé ñóììû
nLength = nBlurSize < nWidth ? nBlurSize : nWidth - 1;
for( ; nLength >= 0; --nLength )
{
nClr += pSrc[nLength];
}
// ðàñ÷¸ò íîâûõ ïèêñåëîâ â ñòðîêå
nLength = nWidth;
for( ;; )
{
nClrFactor = nClr / nFactor + 1;
dwColor = (nB * nClrFactor >> 8) << 0;
dwColor |= (nG * nClrFactor >> 8) << 8;
dwColor |= (nR * nClrFactor >> 8) << 16;
dwColor |= (nA * nClrFactor >> 8) << 24;
*pDst = dwColor;
// ïðîâåðêà íà âûõîä èç öèêëà
if( nLength <= 1 )
{
break;
}
// âû÷èòàåì êðàéíåå ëåâîå çíà÷åíèå
if( nLength <= nWidth - nBlurSize )
{
nClr -= pSrc[-nBlurSize];
}
// ïåðåõîäèì íà ñëåäóþùèé ïèêñåë
--nLength;
++pSrc;
++pDst;
// ïðèáàâëÿåì ñëåäóþùåå êðàéíåå ïðàâîå çíà÷åíèå
if( nLength > nBlurSize )
{
nClr += pSrc[nBlurSize];
}
}
}
return TRUE;
}
static BOOL BGRA_ShadowBlurVertical(BYTE* pSrcData, BYTE* pDstData, int nWidth, int nHeight, int nSrcStride, int nDstStride, int nBlurSize )
{
if( !pSrcData || !pDstData || nWidth < 1 || nHeight < 1 || nSrcStride < 1 || nDstStride < 1 || nBlurSize < 1 )
{
return FALSE;
}
int nLength;
DWORD nClr;
BYTE *pSrc, *pDst;
pSrcData += nBlurSize*4;
pDstData += nBlurSize*4;
for( nWidth -= nBlurSize*2; nWidth > 0; --nWidth, pSrcData += 4, pDstData += 4 )
{
pSrc = pSrcData;
pDst = pDstData;
nClr = 0;
// ïîäñ÷¸ò íà÷àëüíîé ñóììû
nLength = nBlurSize < nHeight ? nBlurSize : nHeight - 1;
for( nLength *= nSrcStride; nLength >= 0; nLength -= nSrcStride )
{
nClr += *(DWORD*)(pSrc + nLength)&0xff;
}
// ðàñ÷¸ò íîâûõ ïèêñåëîâ â ñòðîêå
nLength = nHeight;
for( ;; )
{
*(DWORD*)(pDst) = nClr;
// ïðîâåðêà íà âûõîä èç öèêëà
if( nLength <= 1 )
{
break;
}
// âû÷èòàåì êðàéíåå âåðõíåå çíà÷åíèå
if( nLength <= nHeight - nBlurSize )
{
nClr -= *(DWORD*)(pSrc - nBlurSize*nSrcStride)&0xff;
}
// ïåðåõîäèì íà ñëåäóþùèé ïèêñåë
--nLength;
pSrc += nSrcStride;
pDst += nDstStride;
// ïðèáàâëÿåì ñëåäóþùåå êðàéíåå íèæíåå çíà÷åíèå
if( nLength > nBlurSize )
{
nClr += *(DWORD*)(pSrc + nBlurSize*nSrcStride)&0xff;
}
}
}
return TRUE;
}
static void FillZeroLeftRightEdge( BYTE* pARGBData, int nEdgeWidth, int nEdgeHeight, int nStride, int nOffset )
{
DWORD* pDst;
int nWidth;
for( ; nEdgeHeight > 0; --nEdgeHeight, pARGBData += nStride )
{
pDst = (DWORD*)pARGBData;
for( nWidth = nEdgeWidth; nWidth > 0; --nWidth, ++pDst )
{
pDst[0] = 0;
pDst[nOffset] = 0;
}
}
}
};
typedef Utils::ArrayReflection<CLetterEntity> CRefLetters;
class CTextEntity : public CEntity
{
public:
class CTextLine
{
public:
int m_nFirstLetter; // íîìåð ñèìâîëà â òåêñòå ñ êîòîðîãî íà÷èíàåòñÿ ëèíèÿ
int m_nLength; // êîëè÷åñòâî ñèìâîëîâ â ëèíèè
Gdiplus::RectF m_oPathBoundRect;
CTextLine()
{
m_nFirstLetter = 0;
m_nLength = 0;
}
};
public:
CAtlArray<CLetterEntity> m_arrLetters; // ñïèñîê áóêâ
CAtlArray<CTextEntity::CTextLine> m_arrLines;
public:
CTextEntity()
{
}
virtual void Clear()
{
CEntity::Clear();
m_arrLetters.RemoveAll();
m_arrLines.RemoveAll();
}
virtual void Update( double dTime, const Gdiplus::Matrix& oParentTransform, const float fParentAlphaFactor )
{
CEntity::Update( dTime, oParentTransform, fParentAlphaFactor );
// îáõîäèì ïîñëåäîâàòåëüíî âñå áóêâû
CRefLetters letters( m_arrLetters );
for( ; letters.count > 0; --letters.count, ++letters.ptr )
{
letters.ptr->Update( dTime, m_oTransform, m_dTransparency );
}
}
};
typedef Utils::ArrayReflection<CTextEntity::CTextLine> CRefLines;
class CTextPainter
{
public:
CTextPainter()
{
m_pText = NULL;
m_fLineSpacing = 0;
m_bUnderLine = FALSE;
pImage = NULL;
m_nFillMode = 0;
m_fAngle = 0;
}
~CTextPainter()
{
ReleaseDynamicData();
}
inline void Clear()
{
ReleaseDynamicData();
Utils::ClearRect( m_oLayoutRect );
m_fLineSpacing = 0;
m_bUnderLine = FALSE;
m_oFillBrush.Clear();
m_oEdge.Clear();
m_oShadow.Clear();
m_nFillMode = 0;
m_fAngle = 0;
m_PointLT = Gdiplus::PointF ( 0.0f, 0.0f );
}
inline BOOL CreateText( BSTR bstrText, const Gdiplus::Font& oFont, const Gdiplus::RectF& oClipLayoutRect, int nHorAlign, int nVerAlign, const Gdiplus::RectF& oImageRect, int nPlaceType = 0 )
{
Clear();
if( NULL == bstrText || FALSE == oFont.IsAvailable() || oImageRect.IsEmptyArea() )
return FALSE;
// ñîçäà¸ì ïàò÷ âñåãî òåêñòà
Gdiplus::FontFamily oFontFamily;
Gdiplus::StringFormat oStrFormat;
Gdiplus::StringAlignment eStrAlign;
Gdiplus::GraphicsPath oTextPath;
if( Gdiplus::Ok != oFont.GetFamily( &oFontFamily ) )
return FALSE;
Gdiplus::RectF oLayoutRect = oClipLayoutRect;
//oLayoutRect.X -= 0.5f; // ñìåùåíèå îòíîñèòåëüíî îáû÷íîãî òåêñòà ïî X
// íîðìàëèçàöèÿ ïðÿìîóãîëüíèêà âûâîäà
if( oLayoutRect.Width < 0 )
{
oLayoutRect.X += oLayoutRect.Width;
oLayoutRect.Width *= -1;
}
if( oLayoutRect.Height < 0 )
{
oLayoutRect.Y += oLayoutRect.Height;
oLayoutRect.Height *= -1;
}
// çàäà¸ì ïðÿìîóãîëüíèê îòñå÷åíèÿ âûâîäà
m_oLayoutRect = oLayoutRect;
switch( nHorAlign )
{
case Gdiplus::StringAlignmentCenter:
eStrAlign = Gdiplus::StringAlignmentCenter; break;
case Gdiplus::StringAlignmentFar:
eStrAlign = Gdiplus::StringAlignmentFar; break;
default:
eStrAlign = Gdiplus::StringAlignmentNear;
}
oStrFormat.SetAlignment( eStrAlign );
switch( nVerAlign )
{
case Gdiplus::StringAlignmentCenter:
oLayoutRect.Y += oLayoutRect.Height / 2;
break;
case Gdiplus::StringAlignmentFar:
oLayoutRect.Y += oLayoutRect.Height;
break;
}
oStrFormat.SetLineAlignment( Gdiplus::StringAlignmentNear );
float fViewHeight = oLayoutRect.Height;
oLayoutRect.Height = 0;
int nFontStyle = oFont.GetStyle();
float fFontSize = oFont.GetSize();
// ïîëó÷àåì îáùèé êîíòóð òåêñòà
if( Gdiplus::Ok != oTextPath.AddString( bstrText, -1, &oFontFamily, nFontStyle, fFontSize, oLayoutRect, &oStrFormat ))
return FALSE;
// çàäà¸ì ìåæñòðî÷íûé èíòåðâàë
float fFontAscent = oFontFamily.GetCellAscent( nFontStyle );
float fFontDescent = oFontFamily.GetCellDescent( nFontStyle );
float fFontHeight = oFontFamily.GetEmHeight( nFontStyle );
float fFontSpacing = oFontFamily.GetLineSpacing( nFontStyle );
m_fLineSpacing = fFontSpacing * fFontSize / fFontHeight;
m_bUnderLine = ((nFontStyle & Gdiplus::FontStyleStrikeout)|(nFontStyle & Gdiplus::FontStyleUnderline)) ? TRUE : FALSE;
BOOL bRetValue = CreateLetters ( oTextPath );
if( bRetValue )
{
float fHeight = (fFontAscent + fFontDescent) * fFontSize / fFontHeight;
float fFactor = (fFontSpacing - (fFontAscent + fFontDescent)) * fFontSize / fFontHeight;
CorrectionLettersPositions( nVerAlign, fHeight, m_fLineSpacing, fFactor, fViewHeight );
}
//m_oLayoutRect = oImageRect;
m_oClipRect = oImageRect;
return bRetValue;
}
inline void Draw ( double dTime, Gdiplus::Graphics& oRender )
{
if( m_pText )
{
Gdiplus::GraphicsState oRenderState;
Gdiplus::RectF oClipRect;
oRenderState = oRender.Save();
oRender.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias);
oRender.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
oRender.SetInterpolationMode(Gdiplus::InterpolationModeDefault );//InterpolationModeBilinear);
oRender.SetClip( m_oClipRect, Gdiplus::CombineModeIntersect );
oRender.GetClipBounds( &oClipRect );
oRender.ResetTransform();
oRender.SetTransform ( &m_BitmapTransform );
Gdiplus::Bitmap oBitmap ( (int)m_oLayoutRect.Width + 5, (int)m_oLayoutRect.Height + 5, PixelFormat32bppARGB );
// ATLTRACE ( _T("BitmapSize : %f, %f\t"), (float)oBitmap.GetWidth (), (float)oBitmap.GetHeight () );
Gdiplus::Graphics GraphicsText ( &oBitmap );
GraphicsText.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAlias);
GraphicsText.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
GraphicsText.SetInterpolationMode(Gdiplus::InterpolationModeBilinear);
#if _DEBUG
GraphicsText.Clear ( 0x1F0000FF );
#endif
GraphicsText.SetTransform ( &m_TextTransform );
CRefLetters letters( m_pText->m_arrLetters );
//if( m_oShadow.Visible )
//{
// int alpha = min(255, max(0, m_oShadow.GetAlpha()));
// int color = m_oShadow.GetColor();
// int nBlur = (int)m_oShadow.GetBlurSize();
// float dx = (float)m_oShadow.GetDistanceX();
// float dy = (float)m_oShadow.GetDistanceY();
// Gdiplus::Color oColor( alpha, GetRValue(color), GetGValue(color), GetBValue(color) );
// for( int i = 0; i < letters.count; i++ )
// {
// letters.ptr[i].DrawShadow( oRender, oClipRect, oColor, dx, dy, nBlur );
// }
//}
//if( m_oEdge.Visible )
//{
// for( int i = 0; i < letters.count; i++ )
// {
// if( letters.ptr[i].m_bNotLetter )
// continue;
// letters.ptr[i].DrawEdge( oRender, oClipRect, m_oEdge );
// }
//}
if( TRUE )
{
Gdiplus::RectF oBrushRect( m_pText->m_oPathBoundRect );
for( int i = 0; i < letters.count; i++ )
{
if( letters.ptr[i].m_bNotLetter && m_oEdge.Visible )
letters.ptr[i].DrawEdge( oRender, oClipRect, m_oEdge );
if( m_nFillMode != Constants::c_nTextFillMode_Normal )
{
switch( m_nFillMode )
{
case Constants::c_nTextFillMode_Letter:
oBrushRect = letters.ptr[i].m_oPathBoundRect;
break;
case Constants::c_nTextFillMode_HorCylinder:
break;
case Constants::c_nTextFillMode_VerCylinder:
break;
}
}
letters.ptr[i].Draw( GraphicsText, oClipRect, m_oFillBrush, &m_oTextureManager, &oBrushRect );
}
}
oRender.DrawImage ( &oBitmap, (int)m_oLayoutRect.X, (int)m_oLayoutRect.Y, (int)oBitmap.GetWidth (), (int)oBitmap.GetHeight () );
// ATLTRACE ( _T("DrawImage : %f, %f, %f, %f\n"), (float)m_oLayoutRect.X, (float)m_oLayoutRect.Y, (float)oBitmap.GetWidth (), (float)oBitmap.GetHeight () );
#if _DEBUG
oRender.DrawRectangle ( new Gdiplus::Pen ( Gdiplus::Color(192,255,255,0), 4 ), m_oLayoutRect );
#endif
oRender.Restore( oRenderState );
}
}
inline void SetFillBrush( const Painter::CBrush& oBrush )
{
m_oFillBrush = oBrush;
}
inline void SetShadow( const Painter::CShadow& oShadow )
{
m_oShadow = oShadow;
}
inline void SetEdge( const Painter::CEdgeText& oEdge )
{
m_oEdge = oEdge;
}
inline void UpdateRotate ( float Angle )
{
m_fAngle = Angle;
m_TextTransform.Reset ();
m_TextTransform.Translate ( -m_oLayoutRect.X, -m_oLayoutRect.Y );
Gdiplus::Matrix Transforms;
m_BitmapTransform.Reset ();
Gdiplus::Matrix mTransform;
mTransform.Translate ( m_oLayoutRect.X, m_oLayoutRect.Y );
m_BitmapTransform.Multiply ( &mTransform );
mTransform.Reset ();
mTransform.Rotate ( m_fAngle );
m_BitmapTransform.Multiply ( &mTransform );
mTransform.Reset ();
mTransform.Translate ( -m_oLayoutRect.X , -m_oLayoutRect.Y );
m_BitmapTransform.Multiply ( &mTransform );
}
inline void UpdateTranslate ( float X, float Y )
{
m_oLayoutRect.X += X;
m_oLayoutRect.Y += Y;
}
protected:
inline void ReleaseDynamicData()
{
m_oTextureManager.Clear();
if( pImage )
{
pImage->Release();
pImage = NULL;
}
m_arrPaths.RemoveAll();
if( m_pText )
{
delete m_pText;
m_pText = NULL;
}
}
static void GetBoundRectForLetters( const Gdiplus::RectF& oMaxLayoutRect, Gdiplus::RectF& oResultRect, CLetterEntity* arrLetters, int nCountLetters )
{
float fLeft = oMaxLayoutRect.GetRight();
float fRight = oMaxLayoutRect.GetLeft();
float fTop = oMaxLayoutRect.GetBottom();
float fBottom = oMaxLayoutRect.GetTop();
while( nCountLetters-- > 0 )
{
Gdiplus::RectF& oRect = arrLetters->m_oPathBoundRect;
if( oRect.GetLeft() < fLeft ) fLeft = oRect.GetLeft();
if( oRect.GetRight() > fRight ) fRight = oRect.GetRight();
if( oRect.GetTop() < fTop ) fTop = oRect.GetTop();
if( oRect.GetBottom() > fBottom ) fBottom = oRect.GetBottom();
++arrLetters;
}
oResultRect.X = fLeft;
oResultRect.Y = fTop;
oResultRect.Width = fRight - fLeft;
oResultRect.Height = fBottom - fTop;
}
inline BOOL CreateLetters( const Gdiplus::GraphicsPath& oTextPath )
{
Gdiplus::GraphicsPathIterator oPathIterator( &oTextPath );
// ðàñ÷èòûâàåì êîëè÷åñòâî ñèìâîëîâ â òåêñòå, âêëþ÷àÿ ëèíèíè ïåðå÷¸ðêèâàíèÿ
size_t nLetters = 0;
int nStartIndex, nEndIndex;
while( oPathIterator.NextMarker( &nStartIndex, &nEndIndex ) > 0 )
{
++nLetters;
}
if( nLetters < 1 )
return FALSE;
// ñîçäà¸ì íåîáõîäèìûå äèíàìè÷åñêèå äàííûå
if( !m_arrPaths.SetCount( nLetters, 0 ) )
return FALSE;
m_pText = new CTextEntity();
if( !m_pText )
return FALSE;
if( !m_pText->m_arrLetters.SetCount( nLetters, 0 ) )
return FALSE;
// çàäà¸ì ãåîìåòðè÷åñêèå ïàðàìåòðû äëÿ âñåãî òåêñòà
oTextPath.GetBounds( &m_pText->m_oPathBoundRect );
m_pText->m_oPathCenter = Utils::GetCenter( m_pText->m_oPathBoundRect );
m_pText->m_oOrigin = m_pText->m_oPathCenter;
// çàäà¸ì ãåìåòðè÷åñêèå ïàðàìåòðû äëÿ êàæäîãî ñèìâîëà â òåêñòå
oPathIterator.Rewind();
float fPrevLine = 0.5f;
CLetterEntity* pPrevLetter = NULL;
Gdiplus::GraphicsPath* pPath = m_arrPaths.GetData();
CLetterEntity* pLetter = m_pText->m_arrLetters.GetData();
float fBaseY = m_pText->m_oPathBoundRect.Y + (m_pText->m_oPathBoundRect.Height -
ceil(m_pText->m_oPathBoundRect.Height / m_fLineSpacing) * m_fLineSpacing) / 2;
int nCountLines = 0;
for( ; nLetters > 0; --nLetters, ++pLetter, ++pPath )
{
oPathIterator.NextMarker( pPath );
pLetter->m_pPath = pPath;
pPath->SetFillMode( Gdiplus::FillModeWinding ); // äà¸ò çíà÷èòåëüíîå óñêîðåíèå ïðè îòðèñîâêå
pPath->GetBounds( &pLetter->m_oPathBoundRect );
pLetter->m_oPathCenter = Utils::GetCenter( pLetter->m_oPathBoundRect );
float dy = (pLetter->m_oPathCenter.Y - fBaseY) / m_fLineSpacing;
float fLine = floor(dy);
dy = m_fLineSpacing * (fLine - dy + 0.5f);
pLetter->m_oPathCenter.Y += dy;
pLetter->m_oOrigin = pLetter->m_oPathCenter - m_pText->m_oOrigin;
pLetter->m_bNotLetter = TRUE;
++nCountLines;
if( fPrevLine != fLine )
{
fPrevLine = fLine;
}
else
{
if( pPrevLetter )
{
pPrevLetter->m_bNotLetter = FALSE;
--nCountLines;
}
}
pPrevLetter = pLetter;
}
// ñîçäà¸ì ìàññèâ òåêñòîâûõ ëèíèé
if( m_pText->m_arrLines.SetCount( nCountLines, 0 ) )
{
CTextEntity::CTextLine* pLine = m_pText->m_arrLines.GetData();
int nFirstIndex = 0;
CRefLetters letters( m_pText->m_arrLetters );
for( int nLetter = 0; nLetter < letters.count; ++nLetter )
{
if( letters[nLetter].m_bNotLetter )
{
pLine->m_nFirstLetter = nFirstIndex;
pLine->m_nLength = nLetter - nFirstIndex + 1;
GetBoundRectForLetters( m_pText->m_oPathBoundRect, pLine->m_oPathBoundRect, letters.ptr + nFirstIndex, pLine->m_nLength );
nFirstIndex = nLetter + 1;
++pLine;
}
}
}
// êîððåêòèðóåì ïîëîæåíèå ëèíèé ïåðå÷¸ðêèâàíèÿ
if( !m_bUnderLine )
{
CRefLetters letters( m_pText->m_arrLetters );
for( ; letters.count > 0; --letters.count, ++letters.ptr )
{
letters.ptr->m_bNotLetter = FALSE;
}
}
return TRUE;
}
inline void CorrectionLettersPositions( int nVerAlign, float fLineHeight, float fLineSpacing, float fLineFactor, float fViewHeight )
{
CRefLines lines( m_pText->m_arrLines );
float fHeight = (lines.count - 1) * fLineSpacing + fLineHeight;
float fTop = +1000000;
float fBottom = -1000000;
float fStart = 0;
switch( nVerAlign )
{
case Gdiplus::StringAlignmentFar:
fStart = fLineFactor * (lines.count - 1) - fHeight;
break;
case Gdiplus::StringAlignmentCenter:
fStart = (fLineFactor * lines.count - fHeight) / 2;
break;
default:
fStart = 0;
}
for( int nLine = 0; nLine < lines.count; nLine++ )
{
float offset = fStart - fLineFactor * nLine;
Gdiplus::Matrix matrix( 1, 0, 0, 1, 0, offset );
CRefLetters letters( m_pText->m_arrLetters, lines[nLine].m_nFirstLetter, lines[nLine].m_nLength );
for( int nLetter = 0; nLetter < letters.count; nLetter++ )
{
Text::CLetterEntity* pLetter = letters.ptr + nLetter;
pLetter->m_oOrigin.Y += offset;
pLetter->m_oPathBoundRect.Y += offset;
pLetter->m_oPathCenter.Y += offset;
pLetter->m_pPath->Transform( &matrix );
if( pLetter->m_oPathBoundRect.Y < fTop )
fTop = pLetter->m_oPathBoundRect.Y;
if( pLetter->m_oPathBoundRect.Y + pLetter->m_oPathBoundRect.Height > fBottom )
fBottom = pLetter->m_oPathBoundRect.Y + pLetter->m_oPathBoundRect.Height;
}
}
m_pText->m_oPathBoundRect.Y = fTop;
m_pText->m_oPathBoundRect.Height = fBottom - fTop;
}
protected:
CTextEntity* m_pText;
CAtlArray<Gdiplus::GraphicsPath> m_arrPaths;
Gdiplus::RectF m_oLayoutRect;
Gdiplus::RectF m_oClipRect;
float m_fLineSpacing;
BOOL m_bUnderLine;
Painter::CBrush m_oFillBrush;
Painter::CEdgeText m_oEdge;
Painter::CShadow m_oShadow;
Gdiplus::Matrix m_TextTransform; // ìàòðèöà ïîçèöèîíèðîâàíèÿ òåêñòà â âðåìåííîé êàðòèíêå
Gdiplus::Matrix m_BitmapTransform; // ìàòðèöà ïîçèöèîíèðîâàíèÿ êàðòèíêè ïðè îòðèñîâêå
PointF m_PointLT;
public:
IUnknown* pImage;
Painter::CTextureManager m_oTextureManager;
int m_nFillMode;
float m_fAngle;
PointF m_center;
};
}
}
}