492 lines
16 KiB
C++
492 lines
16 KiB
C++
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// ANIMATE.CPP
|
||
|
//
|
||
|
/*
|
||
|
This class is responsible for the animations in the busy dialogs such as the
|
||
|
merge index dialog.
|
||
|
|
||
|
REVIEW: Could we use the commctrl animation control instead?
|
||
|
|
||
|
*/
|
||
|
|
||
|
// needed for those pesky pre-compiled headers
|
||
|
#include "header.h"
|
||
|
|
||
|
#include <windows.h>
|
||
|
|
||
|
#include "animate.h" // Animate class
|
||
|
|
||
|
#include "resource.h"
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#undef THIS_FILE
|
||
|
static const char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
static const char txtAnimateClassName[] = "Help_Animation";
|
||
|
static const WCHAR wtxtAnimateClassName[] = L"Help_Animation";
|
||
|
|
||
|
typedef struct {
|
||
|
int left;
|
||
|
int top;
|
||
|
int cx;
|
||
|
int cy;
|
||
|
} WRECT;
|
||
|
|
||
|
void STDCALL GetWindowWRect(HWND hwnd, WRECT* prc)
|
||
|
{
|
||
|
ASSERT(IsValidWindow(hwnd));
|
||
|
GetWindowRect(hwnd, (PRECT) prc);
|
||
|
|
||
|
// Convert right and bottom into width and height
|
||
|
prc->cx -= prc->left;
|
||
|
prc->cy -= prc->top;
|
||
|
}
|
||
|
|
||
|
const int CX_DRAWAREA = 40;
|
||
|
const int CY_DRAWAREA = 40;
|
||
|
|
||
|
const int CX_BOOK = 36;
|
||
|
const int CY_BOOK = 36;
|
||
|
const int C_BOOKS = 5;
|
||
|
const int X_BOOK = 0;
|
||
|
const int Y_BOOK = (CY_DRAWAREA - CY_BOOK);
|
||
|
|
||
|
const int CX_PEN = 15;
|
||
|
const int CY_PEN = 20;
|
||
|
const int C_PENS = 3;
|
||
|
const int X_PEN = 18;
|
||
|
const int Y_PEN = 2;
|
||
|
|
||
|
const int CX_STROKE = 1;
|
||
|
const int CY_STROKE = 2;
|
||
|
|
||
|
const int C_HORZ_STROKES = 10;
|
||
|
const int C_VERT_STROKES = 4;
|
||
|
const int C_PEN_STROKES = (C_HORZ_STROKES * C_VERT_STROKES);
|
||
|
|
||
|
const int C_PAUSE_FRAMES = 0;
|
||
|
const int C_FRAMES = (C_PEN_STROKES + C_BOOKS + C_PAUSE_FRAMES);
|
||
|
|
||
|
const int ANIMATE_INCREMENTS = 100;
|
||
|
|
||
|
const COLORREF clrPenA = RGB(128, 128, 128);
|
||
|
const COLORREF clrPenB = RGB(128, 0, 128);
|
||
|
|
||
|
const int MAX_CNT_LINE = 1024;
|
||
|
const int VPAD = 62;
|
||
|
const int HPAD = 30;
|
||
|
|
||
|
static VOID _fastcall PointFromStroke(int xStroke, int yStroke, POINT* lppt);
|
||
|
|
||
|
#define SafeDeleteDC( hDC ) if( hDC ) { DeleteDC( hDC ); hDC = NULL; }
|
||
|
#define SafeDeleteObject( hObj ) if( hObj ) { DeleteObject( hObj ); hObj = NULL; }
|
||
|
|
||
|
LRESULT CALLBACK StatusWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
// remove escape keys
|
||
|
#if 0
|
||
|
MSG msg2;
|
||
|
if( PeekMessage( &msg2, hwnd, 0, 0, PM_NOREMOVE ) ) {
|
||
|
if( msg2.message == WM_KEYDOWN || msg2.message == WM_KEYUP ) {
|
||
|
if( msg2.wParam == VK_ESCAPE ) {
|
||
|
PeekMessage( &msg2, hwnd, 0, 0, PM_REMOVE );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
if(g_bWinNT5)
|
||
|
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
||
|
else
|
||
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
class Animate
|
||
|
{
|
||
|
public:
|
||
|
Animate();
|
||
|
~Animate(void);
|
||
|
void STDCALL NextFrame(void);
|
||
|
void SetPosition(int x, int y) { m_xPos = x; m_yPos = y; };
|
||
|
BOOL STDCALL CreateStatusWindow(HWND hwndParent, int idTitle);
|
||
|
HWND m_hWndParent;
|
||
|
HWND m_hWnd;
|
||
|
|
||
|
protected:
|
||
|
HBITMAP m_hBmpTemp;
|
||
|
HDC m_hDCBmp;
|
||
|
HBITMAP m_hBmpIml;
|
||
|
int m_iFrame;
|
||
|
int m_xPos;
|
||
|
int m_yPos;
|
||
|
DWORD m_oldTickCount;
|
||
|
DWORD m_originalTickCount;
|
||
|
BOOL m_fShown;
|
||
|
};
|
||
|
|
||
|
static Animate* g_pAnimationWindow = NULL;
|
||
|
|
||
|
BOOL STDCALL StartAnimation(int idTitle, HWND hWnd )
|
||
|
{
|
||
|
ASSERT(!g_pAnimationWindow);
|
||
|
if( g_pAnimationWindow )
|
||
|
return FALSE;
|
||
|
g_pAnimationWindow = new Animate();
|
||
|
if( !hWnd )
|
||
|
if( !(hWnd = GetActiveWindow()) )
|
||
|
hWnd = GetDesktopWindow();
|
||
|
if (!g_pAnimationWindow->CreateStatusWindow( hWnd, idTitle)) {
|
||
|
delete g_pAnimationWindow;
|
||
|
g_pAnimationWindow = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void STDCALL NextAnimation(void)
|
||
|
{
|
||
|
if (g_pAnimationWindow)
|
||
|
g_pAnimationWindow->NextFrame();
|
||
|
}
|
||
|
|
||
|
void STDCALL StopAnimation(void)
|
||
|
{
|
||
|
if (g_pAnimationWindow)
|
||
|
delete g_pAnimationWindow;
|
||
|
g_pAnimationWindow = NULL;
|
||
|
}
|
||
|
|
||
|
BOOL STDCALL Animate::CreateStatusWindow(HWND hWndParent, int idTitle)
|
||
|
{
|
||
|
int width;
|
||
|
|
||
|
WORD lang = PRIMARYLANGID(_Module.m_Language.GetUiLanguage());
|
||
|
|
||
|
if(g_bWinNT5)
|
||
|
{
|
||
|
WNDCLASSW wc;
|
||
|
|
||
|
SIZE sSize;
|
||
|
|
||
|
ZeroMemory(&wc, sizeof(wc));
|
||
|
|
||
|
// Register Main window class
|
||
|
|
||
|
wc.hInstance = _Module.GetModuleInstance();
|
||
|
wc.style = CS_BYTEALIGNWINDOW | CS_CLASSDC;
|
||
|
wc.lpfnWndProc = StatusWndProc;
|
||
|
wc.lpszClassName = wtxtAnimateClassName;
|
||
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
||
|
wc.hIcon = LoadIcon(_Module.GetResourceInstance(), "Icon!HTMLHelp");
|
||
|
|
||
|
if (!RegisterClassW(&wc))
|
||
|
return FALSE;
|
||
|
|
||
|
WRECT rc;
|
||
|
m_hWndParent = hWndParent;
|
||
|
GetWindowWRect(m_hWndParent, &rc);
|
||
|
|
||
|
const WCHAR *psz = GetStringResourceW(idTitle);
|
||
|
int len = wcslen(psz);
|
||
|
HDC hdc = GetDC(hWndParent);
|
||
|
GetTextExtentPoint32W(hdc, psz, len, (LPSIZE)&sSize);
|
||
|
ReleaseDC(m_hWndParent, hdc);
|
||
|
|
||
|
width = sSize.cx;
|
||
|
|
||
|
if(lang == LANG_JAPANESE || lang == LANG_CHINESE || lang == LANG_KOREAN)
|
||
|
width+=100;
|
||
|
|
||
|
if (width < CX_BOOK)
|
||
|
width = CX_BOOK;
|
||
|
if ( 0 /*!fIsThisNewShell4*/ )
|
||
|
width += HPAD;
|
||
|
|
||
|
#ifdef BIDI
|
||
|
m_hWnd = CreateWindowExW(WS_EX_WINDOWEDGE | /* WS_EX_TOPMOST | */
|
||
|
(fForceLtr ? 0 : WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR),
|
||
|
wtxtAnimateClassName, psz, WS_POPUP | WS_BORDER | WS_CAPTION,
|
||
|
rc.left + rc.cx / 2 - width / 2,
|
||
|
rc.top + rc.cy / 2 - CY_BOOK / 2 + HPAD,
|
||
|
width + GetSystemMetrics(SM_CXBORDER) * 2 + 2,
|
||
|
CY_BOOK + GetSystemMetrics(SM_CYBORDER) * 2 + VPAD +
|
||
|
GetSystemMetrics(SM_CYCAPTION),
|
||
|
(IsWindowVisible(m_hWndParent)) ? m_hWndParent : NULL,
|
||
|
NULL, hInsNow, NULL);
|
||
|
#else
|
||
|
m_hWnd = CreateWindowExW(WS_EX_WINDOWEDGE /* | WS_EX_TOPMOST*/,
|
||
|
wtxtAnimateClassName, psz, WS_POPUP | WS_BORDER | WS_CAPTION,
|
||
|
rc.left + rc.cx / 2 - width / 2,
|
||
|
rc.top + rc.cy / 2 - CY_BOOK / 2 + HPAD,
|
||
|
width + GetSystemMetrics(SM_CXBORDER) * 2 + 2,
|
||
|
CY_BOOK + GetSystemMetrics(SM_CYBORDER) * 2 + VPAD +
|
||
|
GetSystemMetrics(SM_CYCAPTION),
|
||
|
(IsWindowVisible(m_hWndParent)) ? m_hWndParent : NULL,
|
||
|
NULL, _Module.GetModuleInstance(), NULL);
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WNDCLASS wc;
|
||
|
SIZE sSize;
|
||
|
|
||
|
ZeroMemory(&wc, sizeof(wc));
|
||
|
|
||
|
// Register Main window class
|
||
|
|
||
|
wc.hInstance = _Module.GetModuleInstance();
|
||
|
wc.style = CS_BYTEALIGNWINDOW | CS_CLASSDC;
|
||
|
wc.lpfnWndProc = StatusWndProc;
|
||
|
wc.lpszClassName = (LPCSTR) txtAnimateClassName;
|
||
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
||
|
wc.hIcon = LoadIcon(_Module.GetResourceInstance(), "Icon!HTMLHelp");
|
||
|
|
||
|
if (!RegisterClass(&wc))
|
||
|
return FALSE;
|
||
|
|
||
|
WRECT rc;
|
||
|
m_hWndParent = hWndParent;
|
||
|
GetWindowWRect(m_hWndParent, &rc);
|
||
|
|
||
|
HDC hdc = GetDC(m_hWndParent);
|
||
|
PSTR psz = (PSTR) GetStringResource(idTitle);
|
||
|
GetTextExtentPoint32(hdc, psz, (int)strlen(psz), (LPSIZE)&sSize);
|
||
|
width = sSize.cx;
|
||
|
|
||
|
if(lang == LANG_JAPANESE || lang == LANG_CHINESE || lang == LANG_KOREAN)
|
||
|
width+=100;
|
||
|
|
||
|
if (width < CX_BOOK)
|
||
|
width = CX_BOOK;
|
||
|
if ( 0 /*!fIsThisNewShell4*/ )
|
||
|
width += HPAD;
|
||
|
ReleaseDC(m_hWndParent, hdc);
|
||
|
|
||
|
#ifdef BIDI
|
||
|
m_hWnd = CreateWindowEx(WS_EX_WINDOWEDGE | /* WS_EX_TOPMOST | */
|
||
|
(fForceLtr ? 0 : WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR),
|
||
|
(LPCSTR) txtAnimateClassName, psz, WS_POPUP | WS_BORDER | WS_CAPTION,
|
||
|
rc.left + rc.cx / 2 - width / 2,
|
||
|
rc.top + rc.cy / 2 - CY_BOOK / 2 + HPAD,
|
||
|
width + GetSystemMetrics(SM_CXBORDER) * 2 + 2,
|
||
|
CY_BOOK + GetSystemMetrics(SM_CYBORDER) * 2 + VPAD +
|
||
|
GetSystemMetrics(SM_CYCAPTION),
|
||
|
(IsWindowVisible(m_hWndParent)) ? m_hWndParent : NULL,
|
||
|
NULL, hInsNow, NULL);
|
||
|
#else
|
||
|
m_hWnd = CreateWindowEx(WS_EX_WINDOWEDGE /* | WS_EX_TOPMOST*/,
|
||
|
(LPCSTR) txtAnimateClassName, psz, WS_POPUP | WS_BORDER | WS_CAPTION,
|
||
|
rc.left + rc.cx / 2 - width / 2,
|
||
|
rc.top + rc.cy / 2 - CY_BOOK / 2 + HPAD,
|
||
|
width + GetSystemMetrics(SM_CXBORDER) * 2 + 2,
|
||
|
CY_BOOK + GetSystemMetrics(SM_CYBORDER) * 2 + VPAD +
|
||
|
GetSystemMetrics(SM_CYCAPTION),
|
||
|
(IsWindowVisible(m_hWndParent)) ? m_hWndParent : NULL,
|
||
|
NULL, _Module.GetModuleInstance(), NULL);
|
||
|
#endif
|
||
|
}
|
||
|
ASSERT(m_hWnd);
|
||
|
SetPosition((width - CX_BOOK) / 2, VPAD / 4);
|
||
|
|
||
|
EnableWindow( m_hWndParent, FALSE );
|
||
|
|
||
|
if (!m_hWnd) {
|
||
|
UnregisterClass((LPCSTR)txtAnimateClassName, _Module.GetModuleInstance());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
m_fShown = FALSE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
///////////////////////////// ANIMATE CLASS ///////////////////////////////
|
||
|
|
||
|
Animate::Animate()
|
||
|
{
|
||
|
m_hDCBmp = NULL;
|
||
|
m_hWnd = NULL;
|
||
|
m_originalTickCount = GetTickCount();
|
||
|
m_hWndParent = NULL;
|
||
|
m_hBmpTemp = NULL;
|
||
|
m_hBmpIml = NULL;
|
||
|
}
|
||
|
|
||
|
Animate::~Animate(void)
|
||
|
{
|
||
|
SafeDeleteDC( m_hDCBmp )
|
||
|
SafeDeleteObject( m_hBmpTemp );
|
||
|
SafeDeleteObject( m_hBmpIml );
|
||
|
|
||
|
EnableWindow( m_hWndParent, TRUE );
|
||
|
|
||
|
if (m_hWnd) {
|
||
|
DestroyWindow(m_hWnd);
|
||
|
m_hWnd = NULL;
|
||
|
UnregisterClass((LPCSTR) txtAnimateClassName, _Module.GetModuleInstance());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
|
||
|
FUNCTION: AnimFrame
|
||
|
|
||
|
PURPOSE: Displays one frame of the "build index" animation
|
||
|
in the specified device context.
|
||
|
|
||
|
PARAMETERS:
|
||
|
hdc
|
||
|
x
|
||
|
y
|
||
|
|
||
|
RETURNS:
|
||
|
|
||
|
COMMENTS:
|
||
|
|
||
|
MODIFICATION DATES:
|
||
|
04-Nov-1993 [niklasb]
|
||
|
|
||
|
***************************************************************************/
|
||
|
|
||
|
void PASCAL Animate::NextFrame(void)
|
||
|
{
|
||
|
DWORD curTickCount = GetTickCount();
|
||
|
if (curTickCount - m_oldTickCount < ANIMATE_INCREMENTS)
|
||
|
return;
|
||
|
m_oldTickCount = curTickCount;
|
||
|
|
||
|
// Delay showing the window for one second. If we get done before then,
|
||
|
// then there's no need to have gone to all the trouble.
|
||
|
|
||
|
if (!m_fShown) {
|
||
|
if (curTickCount - m_originalTickCount < 1000)
|
||
|
return;
|
||
|
|
||
|
HBITMAP hbmBooks = LoadBitmap(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDBMP_BOOK));
|
||
|
HBITMAP hbmPens = LoadBitmap(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDBMP_PENS));
|
||
|
HDC hdcTemp = CreateCompatibleDC(NULL);
|
||
|
m_hDCBmp = CreateCompatibleDC(NULL);
|
||
|
|
||
|
HBITMAP hbmpOldBook = NULL;
|
||
|
m_hBmpTemp = NULL;
|
||
|
if (m_hDCBmp) {
|
||
|
hbmpOldBook = (HBITMAP)SelectObject(m_hDCBmp, hbmBooks);
|
||
|
m_hBmpTemp = CreateCompatibleBitmap(m_hDCBmp, CX_DRAWAREA, CY_DRAWAREA);
|
||
|
}
|
||
|
|
||
|
if (!hbmBooks || !hbmPens || !m_hBmpTemp || !m_hDCBmp || !hdcTemp) {
|
||
|
if (hbmpOldBook)
|
||
|
SelectObject(m_hDCBmp, hbmpOldBook);
|
||
|
SafeDeleteObject( hbmpOldBook );
|
||
|
SafeDeleteObject( hbmBooks );
|
||
|
SafeDeleteObject( hbmPens );
|
||
|
SafeDeleteDC( hdcTemp );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m_hBmpIml = CreateCompatibleBitmap(m_hDCBmp, CX_DRAWAREA * C_FRAMES, CY_DRAWAREA);
|
||
|
|
||
|
HBITMAP hbmpOldTemp = (HBITMAP) SelectObject(hdcTemp, m_hBmpTemp);
|
||
|
HBITMAP hbmpOldBmp = (HBITMAP) SelectObject(m_hDCBmp, hbmBooks);
|
||
|
|
||
|
// Create the frames in which the pen scribbles on the open book.
|
||
|
|
||
|
m_iFrame = 0;
|
||
|
for (int y = 0; y < C_VERT_STROKES; y++) {
|
||
|
for (int x = 0; x < C_HORZ_STROKES; x++) {
|
||
|
|
||
|
// Show the book on a white background.
|
||
|
|
||
|
PatBlt(hdcTemp, 0, 0, CX_DRAWAREA, CY_DRAWAREA, WHITENESS);
|
||
|
SelectObject(m_hDCBmp, hbmBooks);
|
||
|
|
||
|
BitBlt(hdcTemp, X_BOOK, Y_BOOK, CX_BOOK, CY_BOOK, m_hDCBmp,
|
||
|
(C_BOOKS - 1) * CX_BOOK, 0, SRCCOPY);
|
||
|
|
||
|
// Add in the scribbled "text".
|
||
|
|
||
|
POINT pt;
|
||
|
for (int yDraw = 0; yDraw < y; yDraw++) {
|
||
|
for (int xDraw = 0; xDraw < C_HORZ_STROKES; xDraw++) {
|
||
|
PointFromStroke(xDraw, yDraw, &pt);
|
||
|
SetPixel(hdcTemp, pt.x, pt.y + CY_PEN - 1,
|
||
|
(xDraw & 1) ? clrPenA : clrPenB);
|
||
|
}
|
||
|
}
|
||
|
for (int xDraw = 0; xDraw <= x; xDraw++) {
|
||
|
PointFromStroke(xDraw, y, &pt);
|
||
|
SetPixel(hdcTemp, pt.x, pt.y + CY_PEN - 1,
|
||
|
(xDraw & 1) ? clrPenA : clrPenB);
|
||
|
}
|
||
|
|
||
|
// Add in the pen using the SRCAND operation.
|
||
|
|
||
|
SelectObject(m_hDCBmp, hbmPens);
|
||
|
BitBlt(hdcTemp, pt.x, pt.y, CX_PEN, CY_PEN, m_hDCBmp,
|
||
|
(m_iFrame & 1) ? CX_PEN : (m_iFrame & 2) * CX_PEN, 0, SRCAND);
|
||
|
|
||
|
SelectObject(m_hDCBmp, m_hBmpIml);
|
||
|
BitBlt(m_hDCBmp, m_iFrame * CX_DRAWAREA, 0, CX_DRAWAREA, CY_DRAWAREA,
|
||
|
hdcTemp, 0, 0, SRCCOPY);
|
||
|
|
||
|
m_iFrame++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Blast a white background into the temporary bitmap, and
|
||
|
// select the books bitmap.
|
||
|
|
||
|
PatBlt(hdcTemp, 0, 0, CX_DRAWAREA, CY_DRAWAREA, WHITENESS);
|
||
|
|
||
|
// Add the frames for the page turning (from the books bitmap).
|
||
|
|
||
|
for (int iBook = 0; iBook < C_BOOKS; iBook++) {
|
||
|
SelectObject(m_hDCBmp, hbmBooks);
|
||
|
BitBlt(hdcTemp, X_BOOK, Y_BOOK, CX_BOOK, CY_BOOK,
|
||
|
m_hDCBmp, iBook * CX_BOOK, 0, SRCCOPY);
|
||
|
SelectObject(m_hDCBmp, m_hBmpIml);
|
||
|
BitBlt(m_hDCBmp, m_iFrame * CX_DRAWAREA, 0, CX_DRAWAREA, CY_DRAWAREA,
|
||
|
hdcTemp, 0, 0, SRCCOPY);
|
||
|
m_iFrame++;
|
||
|
}
|
||
|
|
||
|
SelectObject(hdcTemp, hbmpOldTemp);
|
||
|
|
||
|
m_iFrame = 0;
|
||
|
|
||
|
if (hbmpOldBook)
|
||
|
SelectObject(m_hDCBmp, hbmpOldBook);
|
||
|
SafeDeleteObject(hbmpOldBook);
|
||
|
SafeDeleteObject(hbmBooks);
|
||
|
SafeDeleteObject(hbmPens);
|
||
|
SafeDeleteDC( hdcTemp );
|
||
|
|
||
|
m_fShown = TRUE;
|
||
|
ShowWindow(m_hWnd, SW_NORMAL);
|
||
|
|
||
|
}
|
||
|
|
||
|
ASSERT(IsValidWindow(m_hWnd));
|
||
|
HDC hdc = GetDC(m_hWnd);
|
||
|
HBITMAP hbmpOld = (HBITMAP) SelectObject(m_hDCBmp, m_hBmpIml);
|
||
|
BitBlt(hdc, m_xPos, m_yPos, CX_DRAWAREA, CY_DRAWAREA, m_hDCBmp,
|
||
|
m_iFrame * CX_DRAWAREA, 0, SRCCOPY);
|
||
|
SelectObject(m_hDCBmp, hbmpOld);
|
||
|
|
||
|
ReleaseDC(m_hWnd, hdc);
|
||
|
|
||
|
// Next time draw the next frame.
|
||
|
|
||
|
if (++m_iFrame > C_FRAMES) {
|
||
|
m_iFrame = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static VOID FASTCALL PointFromStroke(int xStroke, int yStroke, POINT* lppt)
|
||
|
{
|
||
|
int cx = (C_HORZ_STROKES / 2) - xStroke;
|
||
|
|
||
|
lppt->x = X_PEN + xStroke * CX_STROKE;
|
||
|
lppt->y = Y_PEN + yStroke * CY_STROKE + cx * cx / 10;
|
||
|
}
|