九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

简单图形编程的学习(2)---点 (Windows GDI实现)


简单图形编程的学习(2--- (Windows GDI实现)

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

一、   又一顿牢骚

虽然知道不应该老是说些与技术无关的话。。。。但是有的时候又总是想说。。。。难怪有同事说我最近已经有点像唐僧了-_-!总而言之,因为相对来说书看的太快,(现在租的房子离公司太远,老是坐地铁,导致有了非常固定的看书时间),而因为工作一直太忙,一直加班回家太晚的原因,所以实际的实践太慢(基本上现在就是以写博客的形式),所以QtAndroid的部分要是同步跟上我DirectX的书都要看好几本了,这样的方式好像不太好,所以目前暂时还是以Windows的为主了。。。。这也体现了一点理想与现实的差距-_-!虽然初期项目目标过大,项目中即时调整起码还能保证项目完成吧。。。。。(扯的远了)。

 

二、   画点

很简单的一个Windows GDI函数,SetPixel,原型如下:

COLORREF SetPixel(
  HDC hdc,           // handle to DC
  int X,             // x-coordinate of pixel
  int Y,             // y-coordinate of pixel
  COLORREF crColor   // pixel color
);

 

也许属于最最简单的Windows GDI 之一了,但是如同在Small Basic画点中所讲解的一样,画点这样简单的函数可以实现无限的效果。还是那句话,能够设置一个像素点,就能够描绘一个世界。。。。。。唯一限制程序实现的就是编写程序人的思维。

因为Windows代码本身的复杂性,不能如Small Basic那样直击要害,要是每次给出完整代码太占用空间,这里给出《Tricks Of the Windows Game Programming GURUS》一书中的框架代码,方便以后代码的添加。

// T3D Game Console, creates a game console application

 

// INCLUDES ///////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN  // just say no to MFC

 

#include <tchar.h>

#include <windows.h>   // include important windows stuff

#include <windowsx.h>

#include <mmsystem.h>

#include <iostream> // include important C/C++ stuff

#include <conio.h>

#include <stdlib.h>

#include <malloc.h>

#include <memory.h>

#include <string.h>

#include <stdarg.h>

#include <stdio.h>

#include <math.h>

#include <io.h>

#include <fcntl.h>

 

// DEFINES ////////////////////////////////////////////////

 

// defines for windows

#define WINDOW_CLASS_NAME _T("WINCLASS1")

 

// MACROS /////////////////////////////////////////////////

 

#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)

#define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

 

// GLOBALS ////////////////////////////////////////////////

HWND      main_window_handle = NULL; // globally track main window

HINSTANCE hinstance_app      = NULL; // globally track hinstance

 

char buffer[80];                     // general printing buffer

 

#define FRAME_PER_SECOND (20)

#define TIME_IN_FRAME (1000/FRAME_PER_SECOND)

#define WIDTH 800

#define HEIGHT 600

 

 

// FUNCTIONS //////////////////////////////////////////////

LRESULT CALLBACK WindowProc(HWND hwnd,

                    UINT msg,

                    WPARAM wparam,

                    LPARAM lparam)

{

   // this is the main message handler of the system

   PAINTSTRUCT   ps;     // used in WM_PAINT

   HDC           hdc;  // handle to a device context

   char buffer[80] = {0};        // used to print strings

 

   // what is the message

   switch(msg)

   { 

   case WM_CREATE:

      {

        // do initialization stuff here

        // return success

        return(0);

      } break;

 

   case WM_PAINT:

      {

        // simply validate the window

        hdc = BeginPaint(hwnd,&ps);

 

        // end painting

        EndPaint(hwnd,&ps);

 

        // return success

        return(0);

      } break;

 

   case WM_DESTROY:

      {

 

        // kill the application, this sends a WM_QUIT message

        PostQuitMessage(0);

 

        // return success

        return(0);

      } break;

 

   default:break;

 

   } // end switch

 

   // process any messages that we didn't take care of

   return (DefWindowProc(hwnd, msg, wparam, lparam));

 

} // end WinProc

 

///////////////////////////////////////////////////////////

 

int Game_Main(void *parms = NULL, int num_parms = 0)

{

   DWORD dwStartTime;

 

   dwStartTime = GetTickCount();

   // this is the main loop of the game, do all your processing

   // here

 

   // for now test if user is hitting ESC and send WM_CLOSE

   if (KEYDOWN(VK_ESCAPE))

      SendMessage(main_window_handle,WM_CLOSE,0,0);

 

   while(GetTickCount() - dwStartTime < TIME_IN_FRAME)

   {

      Sleep(1);

   }

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Main

 

////////////////////////////////////////////////////////////

 

int Game_Init(void *parms = NULL, int num_parms = 0)

{

   // this is called once after the initial window is created and

   // before the main event loop is entered, do all your initialization

   // here

 

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Init

 

/////////////////////////////////////////////////////////////

 

int Game_Shutdown(void *parms = NULL, int num_parms = 0)

{

   // this is called after the game is exited and the main event

   // loop while is exited, do all you cleanup and shutdown here

 

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Shutdown

 

// WINMAIN ////////////////////////////////////////////////

int WINAPI WinMain( HINSTANCE hinstance,

              HINSTANCE hprevinstance,

              LPSTR lpcmdline,

              int ncmdshow)

{

 

   WNDCLASSEX winclass; // this will hold the class we create

   HWND     hwnd; // generic window handle

   MSG        msg;    // generic message

   HDC        hdc;      // graphics device context

 

   // first fill in the window class stucture

   winclass.cbSize         = sizeof(WNDCLASSEX);

   winclass.style        = CS_DBLCLKS | CS_OWNDC |

      CS_HREDRAW | CS_VREDRAW;

   winclass.lpfnWndProc  = WindowProc;

   winclass.cbClsExtra   = 0;

   winclass.cbWndExtra   = 0;

   winclass.hInstance    = hinstance;

   winclass.hIcon        = LoadIcon(NULL, IDI_APPLICATION);

   winclass.hCursor    = LoadCursor(NULL, IDC_ARROW);

   winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);

   winclass.lpszMenuName = NULL;

   winclass.lpszClassName = WINDOW_CLASS_NAME;

   winclass.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);

 

   // save hinstance in global

   hinstance_app = hinstance;

 

   // register the window class

   if (!RegisterClassEx(&winclass))

      return(0);

 

   // create the window

   if (!(hwnd = CreateWindowEx(NULL,                  // extended style

      WINDOW_CLASS_NAME,     // class

      _T("Show Point 0.1"), // title

      WS_OVERLAPPEDWINDOW | WS_VISIBLE,

      0,0,    // initial x,y

      WIDTH,HEIGHT,  // initial width, height

      NULL,   // handle to parent

      NULL,   // handle to menu

      hinstance,// instance of this application

      NULL))) // extra creation parms

      return(0);

 

   // save main window handle

   main_window_handle = hwnd;

 

   // initialize game here

   Game_Init();

 

   // enter main event loop

   while(TRUE)

   {

      // test if there is a message in queue, if so get it

      if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))

      {

        // test if this is a quit

        if (msg.message == WM_QUIT)

           break;

 

        // translate any accelerator keys

        TranslateMessage(&msg);

 

        // send the message to the window proc

        DispatchMessage(&msg);

      } // end if

 

      // main game processing goes here

      Game_Main();

 

   } // end while

 

   // closedown game here

   Game_Shutdown();

 

   // return to Windows like this

   return(msg.wParam);

 

} // end WinMain

 

///////////////////////////////////////////////////////////

 

 

 

1.      随机在屏幕上画随机颜色点

int Game_Main(void *parms = NULL, int num_parms = 0)

{

   DWORD dwStartTime;

   HDC hdc;

 

   dwStartTime = GetTickCount();

   // this is the main loop of the game, do all your processing

   // here

 

   // for now test if user is hitting ESC and send WM_CLOSE

   if (KEYDOWN(VK_ESCAPE))

      SendMessage(main_window_handle,WM_CLOSE,0,0);

 

  

   hdc = GetDC(main_window_handle);

 

   // draw 1000 pixels

   for (int index=0; index < 1000; index++)

   {

      // get random position

      int x = rand()%WIDTH;

      int y = rand()%HEIGHT;

 

      COLORREF color = RGB(rand()%255,rand()%255,rand()%255);

      SetPixel(hdc, x,y, color);

 

   } // end for index

 

   // release the dc

   ReleaseDC(main_window_handle, hdc);

 

   while(GetTickCount() - dwStartTime < TIME_IN_FRAME)

   {

      Sleep(1);

   }

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Main

 

同样还是简单,当然,前提是框架存在并且熟悉了一大堆Windows的特性以后,说实话,Windows的死消息机制也不是一天两天就熟悉的了的,我当年学MFC的时候那是一愣一愣的,用了较旧以后才能大概知道怎么回事儿。知道这些以后,剩下的也就是一个Rand函数+SetPixel函数的理解量了。这里不放截图了,这么简单的东西放个截图我都觉得没有意思。

 

2.      老电视机雪花点的效果:

int Game_Main(void *parms = NULL, int num_parms = 0)

{

   DWORD dwStartTime;

   HDC hdc;

 

   dwStartTime = GetTickCount();

   // this is the main loop of the game, do all your processing

   // here

 

   // for now test if user is hitting ESC and send WM_CLOSE

   if (KEYDOWN(VK_ESCAPE))

      SendMessage(main_window_handle,WM_CLOSE,0,0);

 

  

   hdc = GetDC(main_window_handle);

 

   // draw 1000 pixels

   for (int index=0; index < 500; index++)

   {

      // get random position

      int x = rand()%WIDTH;

      int y = rand()%HEIGHT;

 

      COLORREF color = RGB(255,255,255);

      SetPixel(hdc, x,y, color);

 

   } // end for index

 

   // release the dc

   ReleaseDC(main_window_handle, hdc);

 

   Sleep(10);

   InvalidateRect(main_window_handle, NULL, TRUE);

   //while(GetTickCount() - dwStartTime < TIME_IN_FRAME)

   //{

   // Sleep(1);

   //}

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Main

 

这样基本能够达到效果,但是实际上还有点问题,为了统一程序的架构方便讲解才这样写代码,这里因为利用Windows原有消息去擦除原有点,放在WM_DRAW中去做画点效果会更好一点。

 

3.      闪烁的星空:

#define POSITION_COUNT (500)

int gaWidth[POSITION_COUNT];

int gaHeight[POSITION_COUNT];

 

///////////////////////////////////////////////////////////

 

int Game_Main(void *parms = NULL, int num_parms = 0)

{

   DWORD dwStartTime;

   HDC hdc;

 

   dwStartTime = GetTickCount();

   // this is the main loop of the game, do all your processing

   // here

 

   // for now test if user is hitting ESC and send WM_CLOSE

   if (KEYDOWN(VK_ESCAPE))

      SendMessage(main_window_handle,WM_CLOSE,0,0);

 

  

   hdc = GetDC(main_window_handle);

   for (int index=0; index < POSITION_COUNT; index++)

   {

      SetPixel(hdc, gaWidth[index], gaHeight[index], RGB(255,255,255));

      gaWidth[index] += 1;

      gaHeight[index] += 1;

   } // end for index

 

   Sleep(1000);

 

 

   Sleep(10);

   // release the dc

   ReleaseDC(main_window_handle, hdc);

   //while(GetTickCount() - dwStartTime < TIME_IN_FRAME)

   //{

   // Sleep(1);

   //}

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Main

 

////////////////////////////////////////////////////////////

 

int Game_Init(void *parms = NULL, int num_parms = 0)

{

   // this is called once after the initial window is created and

   // before the main event loop is entered, do all your initialization

   // here

 

   // 一次初始化一个随机POSITION_COUNT大小的数组用于固定位置

   for (int index=0; index < POSITION_COUNT; index++)

   {

      // get random position

      gaWidth[index] = rand()%WIDTH;

      gaHeight[index] = rand()%HEIGHT;

 

   } // end for index

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Init

 

Small Basic实现的方式我发现闪烁效果没有Small Basic好,原因可能是CWin32程序速度比Small Basic快太多了,以至于几乎看不到闪烁,于是,按上面这样将闪烁实际的用Sleep停留一下才能看到较好的效果。也算是不同语言实现同一个算法(大概这么称呼实现这个效果的方法吧)需要注意的不同之处。

 

 

4.      屏幕刮花效果

///////////////////////////////////////////////////////////

 

int Game_Main(void *parms = NULL, int num_parms = 0)

{

   DWORD dwStartTime;

   HDC hdc;

 

   dwStartTime = GetTickCount();

   // this is the main loop of the game, do all your processing

   // here

 

   // for now test if user is hitting ESC and send WM_CLOSE

   if (KEYDOWN(VK_ESCAPE))

      SendMessage(main_window_handle,WM_CLOSE,0,0);

 

  

   hdc = GetDC(main_window_handle);

   for (int index=0; index < POSITION_COUNT; index++)

   {

      SetPixel(hdc, gaWidth[index], gaHeight[index], RGB(255,255,255));

 

      // 这算是实现的一种,横向刮花

      gaWidth[index] += 1;

 

      // 这算是实现的另一种,斜向刮花

      //gaWidth[index] += 1;

      //gaHeight[index] += 1;

 

      // 这算是实现的又一种,纵向刮花,纵向刮花建议配合血红色颜色观看效果。。。。。。

      //gaHeight[index] += 1;

 

   } // end for index

 

   // release the dc

   ReleaseDC(main_window_handle, hdc);

   while(GetTickCount() - dwStartTime < TIME_IN_FRAME)

   {

      Sleep(1);

   }

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Main

 

////////////////////////////////////////////////////////////

 

int Game_Init(void *parms = NULL, int num_parms = 0)

{

   // this is called once after the initial window is created and

   // before the main event loop is entered, do all your initialization

   // here

 

   // 一次初始化一个随机POSITION_COUNT大小的数组用于固定位置

   for (int index=0; index < POSITION_COUNT; index++)

   {

      // get random position

      gaWidth[index] = rand()%WIDTH;

      gaHeight[index] = rand()%HEIGHT;

 

   } // end for index

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Init

 

一个屏幕刮花效果,不同的方向给人感受完全不同,最有意思的是纵向向下的红色效果,如插图1.让我想起某游戏死亡时的结束画面,满眼都是向下流动的血液。。。。。。。。

 

5.      移动的星空:

本来用我在Small Basic中的方法实现也可以出现移动的星空的,但是我发现《Tricks of the Windows Game Programming GURUS》一书中的例子更加有趣,这里引用一下了。

///////////////////////////////////////////////////////////

 

void Init_Stars(void)

{

// this function initializes all the stars

 

for (int index=0; index < NUM_STARS; index++)

    {

    // select random position

    stars[index].x = rand()%WINDOW_WIDTH;

    stars[index].y = rand()%WINDOW_HEIGHT;

 

    // set random velocity  

    stars[index].vel = 1 + rand()%16;

 

    // set intensity which is inversely prop to velocity for 3D effect

    // note, I am mixing equal amounts of RGB to make black -> bright white   

    int intensity = 15*(17 - stars[index].vel);

    stars[index].col = RGB(intensity, intensity, intensity);

 

    } // end for index

 

} // end Init_Stars

 

////////////////////////////////////////////////////////////

 

void Erase_Stars(void)

{

// this function erases all the stars

for (int index=0; index < NUM_STARS; index++)

    SetPixel(global_dc, stars[index].x, stars[index].y, RGB(0,0,0));

 

} // end Erase_Stars

 

////////////////////////////////////////////////////////////

 

void Draw_Stars()

{

// this function draws all the stars

for (int index=0; index < NUM_STARS; index++)

    SetPixel(global_dc, stars[index].x, stars[index].y, stars[index].col);

 

 

} // end Draw_Stars

 

////////////////////////////////////////////////////////////

 

void Move_Stars(void)

{

// this function moves all the stars and wraps them around the

// screen boundaries

for (int index=0; index < NUM_STARS; index++)

    {

    // move the star and test for edge

    stars[index].x+=stars[index].vel;

 

    if (stars[index].x >= WINDOW_WIDTH)

        stars[index].x -= WINDOW_WIDTH;

   

    } // end for index

 

} // end Move_Stars

 

////////////////////////////////////////////////////////////

 

int Game_Main(void *parms = NULL, int num_parms = 0)

{

// this is the main loop of the game, do all your processing

// here

 

// get the time

DWORD start_time = GetTickCount();

 

// erase the stars

Erase_Stars();

 

// move the stars

Move_Stars();

 

// draw the stars

Draw_Stars();

 

// lock to 30 fps

while((start_time - GetTickCount() < 33));

 

// for now test if user is hitting ESC and send WM_CLOSE

if (KEYDOWN(VK_ESCAPE))

   SendMessage(main_window_handle,WM_CLOSE,0,0);

 

// return success or failure or your own return code here

return(1);

 

} // end Game_Main

 

////////////////////////////////////////////////////////////

 

int Game_Init(void *parms = NULL, int num_parms = 0)

{

// this is called once after the initial window is created and

// before the main event loop is entered, do all your initialization

// here

 

// first get the dc to the window

global_dc = GetDC(main_window_handle);

 

// initialize the star field here

Init_Stars();

 

// return success or failure or your own return code here

return(1);

 

} // end Game_Init

 

/////////////////////////////////////////////////////////////

 

int Game_Shutdown(void *parms = NULL, int num_parms = 0)

{

// this is called after the game is exited and the main event

// loop while is exited, do all you cleanup and shutdown here

 

// release the global dc

ReleaseDC(main_window_handle, global_dc);

 

// return success or failure or your own return code here

return(1);

 

} // end Game_Shutdown

 

书中例子利用不同的亮度,及不同的移动速度来模拟一种类3D的效果。。。。。。。。。。。。估计不是一般的人能想到的,呵呵,要知道,仅仅利用了一个SetPixel函数。。。。。。。

 

Have Funaha?在有了一个大概的思路以后,其实Window下面的GDI编程也没有那么难吧,的确是没有那么难吧?毕竟我们专注的也就一个SetPixel函数而已了 ,越专注反而能让我们有更多新的想法。

这里用Windows GDI来实现我从Small Basic学到的一招显示文字的华丽技巧,你以前要是从来没有看过类似例子(也不是个在图形编程领域混过很多年的骨灰级程序员)你别说看了没有感觉惊艳,要知道其实仅仅是利用了画点和文字输出两个如此平常而简单的特性。

6.      星空中的文字

///////////////////////////////////////////////////////////

 

int Game_Main(void *parms = NULL, int num_parms = 0)

{

   DWORD dwStartTime;

 

   dwStartTime = GetTickCount();

   // this is the main loop of the game, do all your processing

   // here

 

   // for now test if user is hitting ESC and send WM_CLOSE

   if (KEYDOWN(VK_ESCAPE))

      SendMessage(main_window_handle,WM_CLOSE,0,0);

 

  

   for (int index=0; index < 1000; index++)

   {

      // get random position

      int x = rand()%WIDTH;

      int y = rand()%HEIGHT;

 

      COLORREF color = RGB(rand()%255,rand()%255,rand()%255);

      SetPixel(ghDC, x,y, color);

 

   } // end for index

 

   TextOut(ghDC, 100, 100, CHAR_OUT, _tcslen(CHAR_OUT));

 

   // release the dc

   while(GetTickCount() - dwStartTime < TIME_IN_FRAME)

   {

      Sleep(1);

   }

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Main

 

////////////////////////////////////////////////////////////

 

int Game_Init(void *parms = NULL, int num_parms = 0)

{

   // this is called once after the initial window is created and

   // before the main event loop is entered, do all your initialization

   // here

   HFONT hfont;

 

   ghDC = GetDC(main_window_handle);

 

   hfont = CreateFont( 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _T("新宋体"));

 

   SelectObject(ghDC, hfont);

 

   SetTextColor(ghDC, RGB(0,0,0));

   SetBkColor(ghDC, RGB(0,0,0));

 

   DeleteObject(hfont);

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Init

 

/////////////////////////////////////////////////////////////

 

int Game_Shutdown(void *parms = NULL, int num_parms = 0)

{

   // this is called after the game is exited and the main event

   // loop while is exited, do all you cleanup and shutdown here

 

   ReleaseDC(main_window_handle, ghDC);

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Shutdown

按照以前的实现思想,我很快写出了上述程序,结果效果和我想象的不太一样,反而有点像个面具。。。。。留出了眼睛的观察窗口。。。-_-!见插图2。实际的原因想了一下才知道,背景前景都是黑的,当时的第一想法是不能让星空给盖了,但是这样不都黑了啊?。。。。呵呵,改成下面这个样子,效果就出来了,多的行用红色标明,看了就明白了,让背景透明,能够被星空给盖了,这样才能显示出文字。

///////////////////////////////////////////////////////////

 

int Game_Main(void *parms = NULL, int num_parms = 0)

{

   DWORD dwStartTime;

 

   dwStartTime = GetTickCount();

   // this is the main loop of the game, do all your processing

   // here

 

   // for now test if user is hitting ESC and send WM_CLOSE

   if (KEYDOWN(VK_ESCAPE))

      SendMessage(main_window_handle,WM_CLOSE,0,0);

 

  

   for (int index=0; index < 300; index++)

   {

      // get random position

      int x = rand()%WIDTH;

      int y = rand()%HEIGHT;

 

      COLORREF color = RGB(rand()%255,rand()%255,rand()%255);

      SetPixel(ghDC, x,y, color);

 

   } // end for index

 

   TextOut(ghDC, 100, 100, CHAR_OUT, _tcslen(CHAR_OUT));

 

   // release the dc

   while(GetTickCount() - dwStartTime < TIME_IN_FRAME)

   {

      Sleep(1);

   }

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Main

 

////////////////////////////////////////////////////////////

 

int Game_Init(void *parms = NULL, int num_parms = 0)

{

   // this is called once after the initial window is created and

   // before the main event loop is entered, do all your initialization

   // here

   HFONT hfont;

 

   ghDC = GetDC(main_window_handle);

 

   hfont = CreateFont( 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _T("新宋体"));

 

   SelectObject(ghDC, hfont);

 

   SetTextColor(ghDC, RGB(0,0,0));

   SetBkMode (ghDC, TRANSPARENT);

 

   DeleteObject(hfont);

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Init

 

/////////////////////////////////////////////////////////////

 

int Game_Shutdown(void *parms = NULL, int num_parms = 0)

{

   // this is called after the game is exited and the main event

   // loop while is exited, do all you cleanup and shutdown here

 

   ReleaseDC(main_window_handle, ghDC);

 

 

   // return success or failure or your own return code here

   return(1);

 

} // end Game_Shutdown

 

我第一次看到这个例子的时候真的感叹作者是个天才-_-!也许是自己太笨了所以想不到用这样的方式去显示文字吧。这次来个系列效果,可以参考《简单图形编程的学习(2--- (small basic实现)》文中的插图效果,或者简单的自己运行一下就好了。

 

三、   小结

一个个简单的点就能够构成如此繁多的效果,简直有点不可思议,但是其实,能够绘制一个点,就能够绘制整个世界,要知道,整个屏幕不过也就是一个一个像素构成的,呵呵。其实,从另外的角度来说,一连串连续的点就能构成一条直线,一排排直线就能构成一个面,有了点,线,面,还有什么不够构成的?你可以表达整个世界。另外,就我看的老的游戏编程书籍介绍,DirectDraw的原始接口也仅仅是能画点/位图而已,游戏的开发的先驱们还不是用这样简单的接口实现了那么多画面丰富,效果华丽的2D游戏啊?

 

插图

插图一:

插图2:

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

简单图形编程的学习(2)---点 (small basic实现)


简单图形编程的学习(2--- (small basic实现)

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

一、   又一顿牢骚

虽然知道不应该老是说些与技术无关的话。。。。但是有的时候又总是想说。。。。难怪有同事说我最近已经有点像唐僧了-_-!总而言之,因为相对来说书看的太快,(现在租的房子离公司太远,老是坐地铁,导致有了非常固定的看书时间),而因为工作一直太忙,一直加班回家太晚的原因,所以实际的实践太慢(基本上现在就是以写博客的形式),所以QtAndroid的部分要是同步跟上我DirectX的书都要看好几本了,这样的方式好像不太好,所以目前暂时还是以Windows的为主了。。。。这也体现了一点理想与现实的差距-_-!虽然初期项目目标过大,项目中即时调整起码还能保证项目完成吧。。。。。(扯的远了)。

 

二、   画点

画点,在Small Basic中属于太基础的东西,当然,其实在Small Basic中什么都基础。。。呵呵

GraphicsWindow.SetPixel

函数用于画点,这里的点仅仅只有一个像素,所以叫SetPixel(设置像素),和Windows GDI的命名一致(其实Small Basic中的画图函数很多都与Windows GDI一致),参数的解释如下:

SetPixel
Draws the pixel specified by the x and y co-ordinates using the specified color. 

GraphicsWindow.
SetPixel(x, y, color)

x
The x co-ordinate of the pixel.
y
The y co-ordinate of the pixel.
color
The color of the pixel to set.
 
Returns
Nothing

1.      随机在屏幕上画随机颜色点(HHT897

GraphicsWindow.BackgroundColor = "White"

GraphicsWindow.PenColor = "LightBlue"

gw = GraphicsWindow.Width

gh = GraphicsWindow.Height

 

While ("True")

  GraphicsWindow.SetPixel(Math.GetRandomNumber(gw), Math.GetRandomNumber(gh), GraphicsWindow.GetRandomColor())

EndWhile

 

So简单。。。不是吗?效果如插图1

但是,不要小看点的作用,点可以用于模拟星空。。。。。。。。。这里展示几个效果,真的觉得small basic用于演示什么叫简单的技术惊艳的效果非常合适。。。。。。在图形领域,感觉技术固然重要,但是思维强大也能利用简单的技术实现惊艳的效果。

2.      老电视机雪花点的效果:(PXB396)

GraphicsWindow.BackgroundColor = "DarkNight"

GraphicsWindow.PenColor = "LightBlue"

gw = GraphicsWindow.Width

gh = GraphicsWindow.Height

 

While ("True")

  For i = 1 To 1000

    GraphicsWindow.SetPixel(Math.GetRandomNumber(gw), Math.GetRandomNumber(gh), "White")

  EndFor

  Program.Delay(10)

  GraphicsWindow.Clear()

EndWhile

 

3.      闪烁的星空:(TPK996)

GraphicsWindow.BackgroundColor = "DarkNight"

GraphicsWindow.PenColor = "LightBlue"

gw = GraphicsWindow.Width

gh = GraphicsWindow.Height

 

' 以数组记录下随机出来的点,这样才能保证星空是在闪烁而不是移动

For i = 1 To 500

  width[i] = Math.GetRandomNumber(gw)

  height[i] = Math.GetRandomNumber(gh)

EndFor

 

While ("True")

  For i = 1 To 500

    GraphicsWindow.SetPixel(width[i], height[i], "White")

  EndFor

  Program.Delay(1000)

  GraphicsWindow.Clear()

EndWhile

 

4.      屏幕刮花效果(RMP025

 

GraphicsWindow.BackgroundColor = "DarkNight"

GraphicsWindow.PenColor = "LightBlue"

gw = GraphicsWindow.Width

gh = GraphicsWindow.Height

 

 

For i = 1 To 500

  width[i] = Math.GetRandomNumber(gw)

  height[i] = Math.GetRandomNumber(gh)

EndFor

 

While ("True")

  For i = 1 To 500

    GraphicsWindow.SetPixel(width[i], height[i], "White")

    width[i] = width[i] + 1

  EndFor

 

  Program.Delay(10)

EndWhile

 

 

5.      移动的星空:(ZGB224

GraphicsWindow.BackgroundColor = "DarkNight"

GraphicsWindow.PenColor = "LightBlue"

gw = GraphicsWindow.Width

gh = GraphicsWindow.Height

 

 

For i = 1 To 50

  width[i] = Math.GetRandomNumber(gw)

  height[i] = Math.GetRandomNumber(gh)

EndFor

 

While ("True")

  Program.Delay(1)

  For i = 1 To 50

    GraphicsWindow.SetPixel(width[i], height[i], "White")

    width[i] = width[i] + 1

   

    ' 保证星空不是直接消失了-_-!

    If(width[i] > gw) Then

      width[i] = 0

    EndIf

  EndFor

 

  GraphicsWindow.Clear()

EndWhile

 

Have Funaha?呵呵,的确是,很久没有这样爽的写程序了,有了思维,很简单的就能体现在Small Basic上,让人愉快。后面的字母都是可以直接在Small Basicimport的,现在Small Basic 0.6出来了,我用的都是Small Basic 0.6。另外,发现没有,不像在讲其他语言/程序的时候一样,对各个参数一通饱讲,10分钟还没有看到一个函数的参数,对于Small Basic的程序我感觉仅仅需要展示效果和源代码就好了,展示的直接就是编程的思想,而不是语言,因为语言本身如此的简单。完成上面所有的示例都没有花掉我一个小时。。。。。很难想象用GDI或者DX我要用多久。。。。。。。呵呵,虽然我用C/C++出身的(现在也靠这个吃饭),也稍微学习过一下汇编,但是我怎么感觉我对越简单的语言越有好感啊?LUA, Python, Bash ,JAVA都稍微学过一点,但是实在是没有如Small Basic这样让人愉快的语言了^^,也许最最重要的一点在于,现有的大部分语言(上面提及的都是),逻辑表达能力虽然很强,库很丰富,但是为了适应足够广阔的领域并达到工业强度,GUI编程方面都是复杂的让人吐血,MFC就不说了,TK号称简单,其实我感觉也好不到哪去,我没有尝试过用Bash没写GUIQt已经算是非常好的GUI库了,但是上百个类足够让你头晕目眩。Small Basic这样的语言虽然是玩具,也就因为其是玩具才敢这么简单。。。。。。。。。呵呵,欣赏它,起码作为一种简单的演示也不错。

 

上面的例子都是自己随便想的,下面看一个偷师来的例子,以前在讲small Basic的时候其实已经展示过了,但是因为这个例子给了我太多惊喜,我决定反复提起,告诉你们什么叫编程思维利用简单的技术,你别说看了没有感觉惊艳,要知道其实仅仅是利用了画点和文字输出两个如此平常而简单的特性。

6.      星空中的文字(HQG707

GraphicsWindow.BackgroundColor = "midnight"

gw = GraphicsWindow.Width

gh = GraphicsWindow.Height

GraphicsWindow.FontSize = 100

Turtle.Move (100)

Turtle.Turn (1*1)

While ("True")

  For i = 1 To 50

   GraphicsWindow.SetPixel(Math.GetRandomNumber(gw),Math.GetRandomNumber(gh),GraphicsWindow.GetRandomColor())

 EndFor

 Turtle.Move(1)

  GraphicsWindow.BrushColor = "Black"

  GraphicsWindow.DrawBoundText(30,110,gw-20,"Small Basic")

 

EndWhile

 

第一次看到这个例子的时候我真的感叹作者是个天才-_-!也许是自己太笨了所以想不到用这样的方式去显示文字吧。这次来个系列效果,如星空中的文字-插图1-4。怎么样?效果惊艳吧?呵呵,直接运行一下程序吧,将文字改成你想要的,你会有更好的感觉。

 

三、   小结

一个个简单的点就能够构成如此繁多的效果,简直有点不可思议,但是其实,能够绘制一个点,就能够绘制整个世界,要知道,整个屏幕不过也就是一个一个像素构成的,呵呵。其实,从另外的角度来说,一连串连续的点就能构成一条直线,一排排直线就能构成一个面,有了点,线,面,还有什么不够构成的?你可以表达整个世界。

 


 

 

 

 

 

 

插图1:

插图2:

 

星空中的文字-插图1:

星空中的文字-插图2:

星空中的文字-插图3:

星空中的文字-插图4:

 

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

Small Basic V0.6 出来了,快报

Small Basic V0.6 出来了,快报

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

今天在Small Basic的论坛上查看有趣的信息,无意看到

微软官方消息:Small Basic V0.6 is Now Available for Download。。。。。。。

Small Basic 的开发速度明显要快于微软一般的产品,几个一个月前我刚开始看到Small Basic这个有意思的东西的时候还是0.4,现在已经0.6了。。。。。阿门,继续有意思下去吧。。。。。。。。。。继续bring fun back to programming 吧。。。。。。

 

以下是官方博客发表的内容:

 

We have a new version of Small Basic for download at http://smallbasic.comThis release adds German localization on top of existing French, Spanish and Russian.

Also, this release sports a lot of small bug-fixes and some most requested features:

Updated the manual to include a chapter on Arrays 

Added Zoom capabilities to Shape

Added Arc Trigonometric operations in the Math object

Removed Arguments object and moved the functionality into Program object

GetAllIndices operation on Array allows iteration on top of all array values

Added ElapsedMilliseconds to help measure time

Added TextInput events, to help write text input features on GraphicsWindow

Fixed crashes related to event subscription

Fixed cursor visibility issues

When you have downloaded and installed, import STARGATES and try out the fun game.

 

粗略翻译如下:

特性:

1.升级文档以包含数组的章节(数组是在0.5版本新添的内容)

2.加入图形缩放的能力(强大啊)

3.Math对象中加入三角函数操作(方便图形计算)

4.移除Arguments对象,并将相关功能移到Program对象中

5.对数组的GetAllIndices操作允许遍历所有的数组值

6.增加ElapsedMilliseconds以帮助计算时间

7.添加TextInput时间来帮助实现GraphicsWindow的文字输入特性

8.修复event subscription相关的崩溃问题

9.修复鼠标光标的显示问题。

 

最后,导入STARGATES可以尝试玩玩Small Basic编写的一个游戏:)

你不会还不会用Small Basic导入吧:)

 

我不太喜欢MS的大部分东西(包括Windows),但是,我毫不掩饰对Small Basic的喜爱。。。。。

 

STARGATES的游戏截图如下:(游戏还是一般)

StarGate截图

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

 

阅读全文....

简单图形编程的学习(1)---文字 (Windows GDI实现)

简单图形编程的学习(1)---文字 (Windows GDI实现)

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

 

一、   全部简单图形编程的学习说在前面的话

      此系列文章均假设读者已经具备一定的对应的程序编写知识,无论是最简单的small basic,还是因为常用而被人熟知的Windows GDI,或者是Linux下用的更多的Qt(一般我用PyQt),甚至是现在国内知道的人并不多的Android,我都不准备讲太多基础的语法,或者与平台相关的太多背景知识,这些靠读者先行学习,我仅仅准备在自己学习的过程中找点乐子:)看看我用一些简单的接口都能想出干什么事情,然后展示给大家看看,图形程序实在是最好展示的一类程序了,不像其他程序一样,哪怕我讲了一堆的boost,真正见识到boost强大的又有几个呢?-_-!要知道,今天起,所有程序都是窗口程序,不再是命令行!!!!人类用了多久才走到这一步我不知道。。。。我用了25.......(从我出生算起)或者1年(从工作开始)

       另外,想要看怎么编写窗口应用程序的就不要走错地方了,这里不是想怎么描述怎么使用一个又一个的控件,这里都是讲绘图的-_-!

 

二、   谈谈Windows GDI

由于今天是第一篇,所以谈谈Windows GDI

虽然因为兴趣和工作需要,对Linux也有所了解,但是Windows到目前为止绝对是本人最熟悉的平台。。。也许也是绝大部分程序员最熟悉的平台吧,但是GDI用的实在是并不多。。。。也许又要说了。。我本质上是个服务器端的程序员-_-!呵呵,服务器端程序员每天面对的只能是控制台,与图形化界面无关,更加与GDI无关。。。。呵呵,但是假如要做客户端的话,是有图形界面了,但是其实游戏还是不需要用到GDI的。。。Windows下不都是用DirectX嘛。但是我对GDI还是比较有兴趣,主要来源于一个资深同事描述用Windows GDI去描述IM软件界面的往事(公司以前是做IM软件的),呵呵,我听着都觉得出神入化。自己好歹也了解一下,虽然说其实用到的机会不多。

文字好像都不像是图形编程中应该学习的东西,但是别忘了,文字可都是由象形文字发展过来的。。。中文至今还是象形文字呢,文字在远古的时代可本来就是图形啊,为啥学习图形编程的时候不要学习怎么显示文字啊?呵呵,前面的都是废话,其实你编点啥程序都会碰到需要在图形中显示文字的情况,所以我们先来看看文字的显示。另外,其实在显示文字的时候,假如需要对文字的显示进行设置,也能学到很多普通图形的设置方式,这点以后就能看到。

 

三、   Windows GDI的文字显示

事实上因为一个完整的Windows程序已经较为复杂,所以以后的程序得有个模板可以套才行,不然老是纠缠在窗口注册,创建和消息循环上了,那样效率太低。这里就用VS2005创建Win32程序本身的那一套了,不用MFC是不想拦住不了解也不像熟悉MFC的兄弟,也许另外弄个MFC+GDI+ (注:切分格式是(MFC+(GDI+))-_-!)篇吧。

基本程序如下:

// Win32GraphicEx1.cpp : 定义应用程序的入口点。

//

 

#include "stdafx.h"

#include "Win32GraphicEx1.h"

 

#define MAX_LOADSTRING 100

 

// 全局变量:

HINSTANCE hInst;                    // 当前实例

TCHAR szTitle[MAX_LOADSTRING];            // 标题栏文本

TCHAR szWindowClass[MAX_LOADSTRING];      // 主窗口类名

 

// 此代码模块中包含的函数的前向声明:

ATOM          MyRegisterClass(HINSTANCE hInstance);

BOOL          InitInstance(HINSTANCE, int);

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

 

int APIENTRY _tWinMain(HINSTANCE hInstance,

                     HINSTANCE hPrevInstance,

                     LPTSTR    lpCmdLine,

                     int       nCmdShow)

{

   UNREFERENCED_PARAMETER(hPrevInstance);

   UNREFERENCED_PARAMETER(lpCmdLine);

 

   // TODO: 在此放置代码。

   MSG msg;

   HACCEL hAccelTable;

 

   // 初始化全局字符串

   LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);

   LoadString(hInstance, IDC_WIN32GRAPHICEX1, szWindowClass, MAX_LOADSTRING);

   MyRegisterClass(hInstance);

 

   // 执行应用程序初始化:

   if (!InitInstance (hInstance, nCmdShow))

   {

      return FALSE;

   }

 

   hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32GRAPHICEX1));

 

   // 主消息循环:

   while (GetMessage(&msg, NULL, 0, 0))

   {

      if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

      {

         TranslateMessage(&msg);

        DispatchMessage(&msg);

      }

   }

 

   return (int) msg.wParam;

}

 

 

 

//

//  函数: MyRegisterClass()

//

//  目的: 注册窗口类。

//

//  注释:

//

//    仅当希望

//    此代码与添加到Windows 95 中的“RegisterClassEx”

//    函数之前的Win32 系统兼容时,才需要此函数及其用法。调用此函数十分重要,

//    这样应用程序就可以获得关联的

//    格式正确的小图标。

//

ATOM MyRegisterClass(HINSTANCE hInstance)

{

   WNDCLASSEX wcex;

 

   wcex.cbSize = sizeof(WNDCLASSEX);

 

   wcex.style       = CS_HREDRAW | CS_VREDRAW;

   wcex.lpfnWndProc = WndProc;

   wcex.cbClsExtra     = 0;

   wcex.cbWndExtra     = 0;

   wcex.hInstance      = hInstance;

   wcex.hIcon       = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32GRAPHICEX1));

   wcex.hCursor     = LoadCursor(NULL, IDC_ARROW);

   wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);

   wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_WIN32GRAPHICEX1);

   wcex.lpszClassName  = szWindowClass;

   wcex.hIconSm     = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

 

   return RegisterClassEx(&wcex);

}

 

//

//   函数: InitInstance(HINSTANCE, int)

//

//   目的: 保存实例句柄并创建主窗口

//

//   注释:

//

//        在此函数中,我们在全局变量中保存实例句柄并

//        创建和显示主程序窗口。

//

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

{

   HWND hWnd;

 

   hInst = hInstance; // 将实例句柄存储在全局变量中

 

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,

      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

 

   if (!hWnd)

   {

      return FALSE;

   }

 

   ShowWindow(hWnd, nCmdShow);

   UpdateWindow(hWnd);

 

   return TRUE;

}

 

//

//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)

//

//  目的: 处理主窗口的消息。

//

//  WM_COMMAND   - 处理应用程序菜单

//  WM_PAINT  - 绘制主窗口

//  WM_DESTROY   - 发送退出消息并返回

//

//

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

   int wmId, wmEvent;

   PAINTSTRUCT ps;

   HDC hdc;

 

   switch (message)

   {

   case WM_COMMAND:

      wmId    = LOWORD(wParam);

      wmEvent = HIWORD(wParam);

      // 分析菜单选择:

      switch (wmId)

      {

      case IDM_ABOUT:

        DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);

        break;

      case IDM_EXIT:

        DestroyWindow(hWnd);

        break;

      default:

        return DefWindowProc(hWnd, message, wParam, lParam);

      }

      break;

   case WM_PAINT:

      hdc = BeginPaint(hWnd, &ps);

      // TODO: 在此添加任意绘图代码...

      EndPaint(hWnd, &ps);

      break;

   case WM_DESTROY:

      PostQuitMessage(0);

      break;

   default:

      return DefWindowProc(hWnd, message, wParam, lParam);

   }

   return 0;

}

 

// “关于框的消息处理程序。

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

{

   UNREFERENCED_PARAMETER(lParam);

   switch (message)

   {

   case WM_INITDIALOG:

      return (INT_PTR)TRUE;

 

   case WM_COMMAND:

      if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)

      {

        EndDialog(hDlg, LOWORD(wParam));

        return (INT_PTR)TRUE;

      }

      break;

   }

   return (INT_PTR)FALSE;

}

 

以后讲解的时候可能就不列出完整的源代码了,因为用C++编写Win32程序,可能动辄几页的代码,完整列出影响阅读,为了完整,就如前面所言,我已经假定读者都具有相关领域一定的编程经验,将描述的代码放进恰当的位置应该不成问题。

文字的显示是个复杂的问题,(自有历史以来就复杂)不仅仅是调用几个类似于TextOutDrawText DrawTextEx API那么简单,看过Windows 图形编程》(参考1)的人应该会有同感,此书是我见过关于Windows下字体,文字显示最深入的一本书,(其实是其他的专著太深看不懂也没有看)。

至于Windows下普通的文字显示APIProgramming Windows》一书按照惯例还是最好的一本。

 

       Windows下最常用的文字输出API就是前面提到的3个,见下面的例子:

   case WM_PAINT:

      {

        hdc = BeginPaint(hWnd, &ps);

        // TextOut

        TextOut(hdc, 0, 0, szHelloWorld, (int)_tcslen(szHelloWorld));

 

        // DrawText

        RECT rtText = {0, 50, 100, 100};

        DrawText(hdc, szHelloWorld, -1, &rtText, DT_LEFT);

 

        // DrawTextEx

        rtText.top += 50;

        rtText.bottom += 50;

        DrawTextEx(hdc, szHelloWorld, -1, &rtText, DT_LEFT, NULL);

        EndPaint(hWnd, &ps);

      }

 

显示效果如插图1

 其实还有几个使用复杂度稍微高一点,但是控制力更强一些的文本输出函数,比如TabbedTextOut,ExtTextOut。(八卦一下:ExtTextOut是少有的将Ext放在函数名前表示扩展而不是按照微软惯例将Ex表示扩展放在函数名后的函数,估计是设计此函数的人刚到微软工作-_-!

 

四、   字体

其实字体是个更加复杂的问题。。。。有多少人知道在字体的现实问题上MS,Apple,Adobe的研究人员投入了多少精力啊。。。。今天的TrueType可不是一开始就存在的。。。。

当然,其实今天我们已经没有必要再去重复研究了,Windows下用MS提供的API就好了,一般有两个接口:

 

HFONT CreateFont(

  int nHeight,               // height of font

  int nWidth,                // average character width

  int nEscapement,           // angle of escapement

  int nOrientation,          // base-line orientation angle

  int fnWeight,              // font weight

  DWORD fdwItalic,           // italic attribute option

  DWORD fdwUnderline,        // underline attribute option

  DWORD fdwStrikeOut,        // strikeout attribute option

  DWORD fdwCharSet,          // character set identifier

  DWORD fdwOutputPrecision,  // output precision

  DWORD fdwClipPrecision,    // clipping precision

  DWORD fdwQuality,          // output quality

  DWORD fdwPitchAndFamily,   // pitch and family

  LPCTSTR lpszFace           // typeface name

);

这个接口直接通过超多的参数创建字体,看看参数的数量。。。就知道我说过的字体显示是个复杂问题没有错了。。。。

另外有个更加人性化的接口,那就是利用结构,本质上没有太大区别,只是从软件接口设计上来说,当参数超过67个的时候提供结构传递参数会更加人性化一点,CreateWindow之类的也就是遵循了这样的方式。这也是BSC++中不提供关键词参数,关键词参数没有那么重要的主要理由之一。(不明白我说的是什么那就忽略此句。。。见D&E6.5

所谓的用结构创建字体的接口如下:

HFONT CreateFontIndirect(

  CONST LOGFONT* lplf   // characteristics

);

LOGFONT结构就是前面一个直接创建的接口的参数的堆叠:

typedef struct tagLOGFONTW

{

    LONG      lfHeight;

    LONG      lfWidth;

    LONG      lfEscapement;

    LONG      lfOrientation;

    LONG      lfWeight;

    BYTE      lfItalic;

    BYTE      lfUnderline;

    BYTE      lfStrikeOut;

    BYTE      lfCharSet;

    BYTE      lfOutPrecision;

    BYTE      lfClipPrecision;

    BYTE      lfQuality;

    BYTE      lfPitchAndFamily;

    WCHAR     lfFaceName[LF_FACESIZE];

} LOGFONTW, *PLOGFONTW, NEAR *NPLOGFONTW, FAR *LPLOGFONTW;

 

这里列出来的是宽字节版本。参数如此之多,一方面体现了复杂度,一方面也是自由度,其实个人认为有点点设计的累赘了。。。。其实完全没有必要用一个一个整数来表示一个bool值的内容,MS习惯的位标志竟然在此处看不到痕迹。。。估计。。。写字体模块的哥们光研究怎么更好的显示字体了,没有关注接口的设计。。。。。或者,和写TabbedTextOut,ExtTextOut就是同一个人。。。。这个人很显然刚刚来微软。。。。

参数的含义不一个一个解释了,看MSDN或者《Programming Windows》,见下面一个例子,来自于《Programming Windows》。

 

/*---------------------------------------

   EZFONT.C -- Easy Font Creation

               (c) Charles Petzold, 1998

  ---------------------------------------*/

 

#include <windows.h>

#include <math.h>

#include "ezfont.h"

 

HFONT EzCreateFont (HDC hdc, TCHAR * szFaceName, int iDeciPtHeight,

                    int iDeciPtWidth, int iAttributes, BOOL fLogRes)

{

     FLOAT      cxDpi, cyDpi ;

     HFONT      hFont ;

     LOGFONT    lf ;

     POINT      pt ;

     TEXTMETRIC tm ;

    

     SaveDC (hdc) ;

    

     SetGraphicsMode (hdc, GM_ADVANCED) ;

     ModifyWorldTransform (hdc, NULL, MWT_IDENTITY) ;

     SetViewportOrgEx (hdc, 0, 0, NULL) ;

     SetWindowOrgEx   (hdc, 0, 0, NULL) ;

    

     if (fLogRes)

     {

          cxDpi = (FLOAT) GetDeviceCaps (hdc, LOGPIXELSX) ;

          cyDpi = (FLOAT) GetDeviceCaps (hdc, LOGPIXELSY) ;

     }

     else

     {

          cxDpi = (FLOAT) (25.4 * GetDeviceCaps (hdc, HORZRES) /

                                        GetDeviceCaps (hdc, HORZSIZE)) ;

         

          cyDpi = (FLOAT) (25.4 * GetDeviceCaps (hdc, VERTRES) /

                                        GetDeviceCaps (hdc, VERTSIZE)) ;

     }

    

     pt.x = (int) (iDeciPtWidth  * cxDpi / 72) ;

     pt.y = (int) (iDeciPtHeight * cyDpi / 72) ;

    

     DPtoLP (hdc, &pt, 1) ;

    

     lf.lfHeight         = - (int) (fabs (pt.y) / 10.0 + 0.5) ;

     lf.lfWidth          = 0 ;

     lf.lfEscapement     = 0 ;

     lf.lfOrientation    = 0 ;

     lf.lfWeight         = iAttributes & EZ_ATTR_BOLD      ? 700 : 0 ;

     lf.lfItalic         = iAttributes & EZ_ATTR_ITALIC    ?   1 : 0 ;

     lf.lfUnderline      = iAttributes & EZ_ATTR_UNDERLINE ?   1 : 0 ;

     lf.lfStrikeOut      = iAttributes & EZ_ATTR_STRIKEOUT ?   1 : 0 ;

     lf.lfCharSet        = DEFAULT_CHARSET ;

     lf.lfOutPrecision   = 0 ;

     lf.lfClipPrecision  = 0 ;

     lf.lfQuality        = 0 ;

     lf.lfPitchAndFamily = 0 ;

    

     lstrcpy (lf.lfFaceName, szFaceName) ;

    

     hFont = CreateFontIndirect (&lf) ;

    

     if (iDeciPtWidth != 0)

     {

          hFont = (HFONT) SelectObject (hdc, hFont) ;

         

          GetTextMetrics (hdc, &tm) ;

         

          DeleteObject (SelectObject (hdc, hFont)) ;

         

          lf.lfWidth = (int) (tm.tmAveCharWidth *

                                        fabs (pt.x) / fabs (pt.y) + 0.5) ;

         

          hFont = CreateFontIndirect (&lf) ;

     }

    

     RestoreDC (hdc, -1) ;

     return hFont ;

}

 

/*----------------------------------------

   FONTROT.C -- Rotated Fonts

                (c) Charles Petzold, 1998

  ----------------------------------------*/

 

#include <windows.h>

#include "ezfont.h"

 

TCHAR szAppName [] = TEXT ("FontRot") ;

TCHAR szTitle   [] = TEXT ("FontRot: Rotated Fonts") ;

 

void PaintRoutine (HWND hwnd, HDC hdc, int cxArea, int cyArea)

{

     static TCHAR szString [] = TEXT ("   Rotation") ;

     HFONT        hFont ;

     int          i ;

     LOGFONT      lf ;

 

     hFont = EzCreateFont (hdc, TEXT ("Times New Roman"), 540, 0, 0, TRUE) ;

     GetObject (hFont, sizeof (LOGFONT), &lf) ;

     DeleteObject (hFont) ;

 

     SetBkMode (hdc, TRANSPARENT) ;

     SetTextAlign (hdc, TA_BASELINE) ;

     SetViewportOrgEx (hdc, cxArea / 2, cyArea / 2, NULL) ;

 

     for (i = 0 ; i < 12 ; i ++)

     {

          lf.lfEscapement = lf.lfOrientation = i * 300 ;

          SelectObject (hdc, CreateFontIndirect (&lf)) ;

 

          TextOut (hdc, 0, 0, szString, lstrlen (szString)) ;

 

          DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;

     }

}

 

/*------------------------------------------------

   FONTDEMO.C -- Font Demonstration Shell Program

                 (c) Charles Petzold, 1998

  ------------------------------------------------*/

 

#include <windows.h>

#include "EzFont.h"

#include "resource.h"

 

extern  void     PaintRoutine (HWND, HDC, int, int) ;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

 

HINSTANCE hInst ;

 

extern TCHAR szAppName [] ;

extern TCHAR szTitle [] ;

 

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

{

     TCHAR    szResource [] = TEXT ("FontDemo") ;

     HWND     hwnd ;

     MSG      msg ;

     WNDCLASS wndclass ;

    

     hInst = hInstance ;

    

     wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

     wndclass.lpfnWndProc   = WndProc ;

     wndclass.cbClsExtra    = 0 ;

     wndclass.cbWndExtra    = 0 ;

     wndclass.hInstance     = hInstance ;

     wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

     wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

     wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

     wndclass.lpszMenuName  = szResource ;

     wndclass.lpszClassName = szAppName ;

    

     if (!RegisterClass (&wndclass))

     {

          MessageBox (NULL, TEXT ("This program requires Windows NT!"),

                      szAppName, MB_ICONERROR) ;

          return 0 ;

     }

    

     hwnd = CreateWindow (szAppName, szTitle,

                          WS_OVERLAPPEDWINDOW,

                          CW_USEDEFAULT, CW_USEDEFAULT,

                          CW_USEDEFAULT, CW_USEDEFAULT,

                          NULL, NULL, hInstance, NULL) ;

    

     ShowWindow (hwnd, iCmdShow) ;

     UpdateWindow (hwnd) ;

    

     while (GetMessage (&msg, NULL, 0, 0))

     {

          TranslateMessage (&msg) ;

          DispatchMessage (&msg) ;

     }

     return msg.wParam ;

}

 

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

{

     static DOCINFO  di = { sizeof (DOCINFO), TEXT ("Font Demo: Printing") } ;

     static int      cxClient, cyClient ;

     static PRINTDLG pd = { sizeof (PRINTDLG) } ;

     BOOL            fSuccess ;

     HDC             hdc, hdcPrn ;

     int             cxPage, cyPage ;

     PAINTSTRUCT     ps ;

    

     switch (message)

     {

     case WM_COMMAND:

          switch (wParam)

          {

          case IDM_PRINT:

 

                    // Get printer DC

 

               pd.hwndOwner = hwnd ;

          pd.Flags     = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION ;

 

             if (!PrintDlg (&pd))

                    return 0 ;

 

               if (NULL == (hdcPrn = pd.hDC))

               {

                    MessageBox (hwnd, TEXT ("Cannot obtain Printer DC"),

                                szAppName, MB_ICONEXCLAMATION | MB_OK) ;

                    return 0 ;

               }

                    // Get size of printable area of page

 

               cxPage = GetDeviceCaps (hdcPrn, HORZRES) ;

               cyPage = GetDeviceCaps (hdcPrn, VERTRES) ;

 

               fSuccess = FALSE ;

 

                    // Do the printer page

 

               SetCursor (LoadCursor (NULL, IDC_WAIT)) ;

               ShowCursor (TRUE) ;

 

               if ((StartDoc (hdcPrn, &di) > 0) && (StartPage (hdcPrn) > 0))

               {

                    PaintRoutine (hwnd, hdcPrn, cxPage, cyPage) ;

                   

                    if (EndPage (hdcPrn) > 0)

                    {

                         fSuccess = TRUE ;

                         EndDoc (hdcPrn) ;

                    }

               }

               DeleteDC (hdcPrn) ;

 

               ShowCursor (FALSE) ;

               SetCursor (LoadCursor (NULL, IDC_ARROW)) ;

 

               if (!fSuccess)

                    MessageBox (hwnd,

                                TEXT ("Error encountered during printing"),

                                szAppName, MB_ICONEXCLAMATION | MB_OK) ;

               return 0 ;

 

          case IDM_ABOUT:

               MessageBox (hwnd, TEXT ("Font Demonstration Program/n")

                                 TEXT ("(c) Charles Petzold, 1998"),

                           szAppName, MB_ICONINFORMATION | MB_OK);

               return 0 ;

          }

          break ;

         

     case WM_SIZE:

          cxClient = LOWORD (lParam) ;

          cyClient = HIWORD (lParam) ;

          return 0 ;

         

     case WM_PAINT:

          hdc = BeginPaint (hwnd, &ps) ;

         

          PaintRoutine (hwnd, hdc, cxClient, cyClient) ;

         

          EndPaint (hwnd, &ps) ;

          return 0 ;

          

     case WM_DESTROY :

          PostQuitMessage (0) ;

          return 0 ;

     }

     return DefWindowProc (hwnd, message, wParam, lParam) ;

}

 

显示效果如插图2,一个显示成一圈的Rotation,用最最简单的手段实现绚烂的效果,以Small Basic中的一个星空显示文字的程序为最,这也算是比较突出的例子了。。。。

这里也不能老是抄袭Petzold。。。。。。。我将其转起来。实现旋转的动画:)

 

/*------------------------------------------------

   FONTDEMO.C -- Font Demonstration Shell Program

                 (c) 改自Charles Petzold, 1998,因为不知道其原来是啥版权,这里也不声明自己的版权了

  ------------------------------------------------*/

 

#include <windows.h>

#include "EzFont.h"

#include "resource.h"

 

extern void PaintRoutine (HWND, HDC, int, int) ;

extern void PaintAnimateOrientation(HWND hwnd, HDC hdc, int cxArea, int cyArea);

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

 

HINSTANCE hInst ;

 

 

extern TCHAR szAppName [] ;

extern TCHAR szTitle [] ;

 

#define WND_WIDTH (800)

#define WND_HEIGHT (800)

 

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

{

   TCHAR    szResource [] = TEXT ("FontDemo") ;

   HWND     hwnd ;

   MSG      msg ;

   WNDCLASS wndclass ;

   HDC hdc ;

 

   hInst = hInstance ;

 

   wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

   wndclass.lpfnWndProc   = WndProc ;

   wndclass.cbClsExtra    = 0 ;

   wndclass.cbWndExtra    = 0 ;

   wndclass.hInstance     = hInstance ;

   wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

   wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

   wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

   wndclass.lpszMenuName  = szResource ;

   wndclass.lpszClassName = szAppName ;

 

   if (!RegisterClass (&wndclass))

   {

      MessageBox (NULL, TEXT ("This program requires Windows NT!"),

        szAppName, MB_ICONERROR) ;

      return 0 ;

   }

 

   hwnd = CreateWindow (szAppName, szTitle,

      WS_OVERLAPPEDWINDOW,

      CW_USEDEFAULT, CW_USEDEFAULT,

      WND_WIDTH, WND_HEIGHT,

      NULL, NULL, hInstance, NULL) ;

 

   ShowWindow (hwnd, iCmdShow) ;

   UpdateWindow (hwnd) ;

 

 

 

 

   while (TRUE)

   {

      // 就像游戏中一贯的做法

      if(PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))

      {

        if(msg.message == WM_QUIT)

        {

           break;

        }

 

        TranslateMessage (&msg);

        DispatchMessage (&msg);

      }

 

      hdc = GetDC(hwnd);

 

      PaintAnimateOrientation(hwnd, hdc, WND_WIDTH, WND_HEIGHT);

 

      ReleaseDC(hwnd, hdc);

   }

   return msg.wParam ;

}

 

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

{

     static DOCINFO  di = { sizeof (DOCINFO), TEXT ("Font Demo: Printing") } ;

     static int      cxClient, cyClient ;

     static PRINTDLG pd = { sizeof (PRINTDLG) } ;

     BOOL            fSuccess ;

     HDC             hdc, hdcPrn ;

     int             cxPage, cyPage ;

     PAINTSTRUCT     ps ;

    

     switch (message)

     {

     case WM_COMMAND:

          switch (wParam)

          {

          case IDM_PRINT:

 

                    // Get printer DC

 

               pd.hwndOwner = hwnd ;

          pd.Flags     = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION ;

 

             if (!PrintDlg (&pd))

                    return 0 ;

 

               if (NULL == (hdcPrn = pd.hDC))

               {

                    MessageBox (hwnd, TEXT ("Cannot obtain Printer DC"),

                                szAppName, MB_ICONEXCLAMATION | MB_OK) ;

                    return 0 ;

               }

                    // Get size of printable area of page

 

               cxPage = GetDeviceCaps (hdcPrn, HORZRES) ;

               cyPage = GetDeviceCaps (hdcPrn, VERTRES) ;

 

               fSuccess = FALSE ;

 

                    // Do the printer page

 

               SetCursor (LoadCursor (NULL, IDC_WAIT)) ;

               ShowCursor (TRUE) ;

 

               if ((StartDoc (hdcPrn, &di) > 0) && (StartPage (hdcPrn) > 0))

               {

                    PaintRoutine (hwnd, hdcPrn, cxPage, cyPage) ;

                   

                    if (EndPage (hdcPrn) > 0)

                    {

                         fSuccess = TRUE ;

                         EndDoc (hdcPrn) ;

                    }

               }

               DeleteDC (hdcPrn) ;

 

               ShowCursor (FALSE) ;

               SetCursor (LoadCursor (NULL, IDC_ARROW)) ;

 

               if (!fSuccess)

                    MessageBox (hwnd,

                                TEXT ("Error encountered during printing"),

                                szAppName, MB_ICONEXCLAMATION | MB_OK) ;

               return 0 ;

 

          case IDM_ABOUT:

               MessageBox (hwnd, TEXT ("Font Demonstration Program/n")

                                 TEXT ("(c) Charles Petzold, 1998"),

                           szAppName, MB_ICONINFORMATION | MB_OK);

               return 0 ;

          }

          break ;

         

     case WM_SIZE:

          cxClient = LOWORD (lParam) ;

          cyClient = HIWORD (lParam) ;

          return 0 ;

          

     case WM_PAINT:

          hdc = BeginPaint (hwnd, &ps) ;

         

         

          EndPaint (hwnd, &ps) ;

          return 0 ;

     case WM_DESTROY :

          PostQuitMessage (0) ;

          return 0 ;

     }

     return DefWindowProc (hwnd, message, wParam, lParam) ;

}

 

/*----------------------------------------

   FONTROT.C -- Rotated Fonts

                (c) 改自Charles Petzold, 1998

  ----------------------------------------*/

 

#include <windows.h>

#include <tchar.h>

#include "ezfont.h"

 

TCHAR szAppName [] = TEXT ("FontRot") ;

TCHAR szTitle   [] = TEXT ("FontRot: Rotated Fonts") ;

#define FRAME_PER_SECOND (20)

#define TIME_IN_FRAME (1000/FRAME_PER_SECOND)

 

#define WHITE_COLOR (RGB(255,255,255))

#define BLACK_COLOR (RGB(0,0,0))

void PaintRoutine (HWND hwnd, HDC hdc, int cxArea, int cyArea, int iAnimateOrientation)

{

   static TCHAR szString [] = TEXT ("   Rotation") ;

   HFONT        hFont ;

   int          i ;

   LOGFONT      lf ;

 

 

   hFont = EzCreateFont (hdc, TEXT ("Times New Roman"), 540, 0, 0, TRUE) ;

   GetObject (hFont, sizeof (LOGFONT), &lf) ;

   DeleteObject (hFont) ;

 

   SetBkMode (hdc, TRANSPARENT) ;

   SetTextAlign (hdc, TA_BASELINE) ;

   SetViewportOrgEx (hdc, cxArea / 2, cyArea / 2, NULL) ;

 

  

 

   SetTextColor(hdc, WHITE_COLOR);

   for (i = 0 ; i < 12 ; i++)

   {

      lf.lfEscapement = lf.lfOrientation = i * 300 + iAnimateOrientation-1;

      SelectObject (hdc, CreateFontIndirect (&lf)) ;

 

      TextOut (hdc, 0, 0, szString, lstrlen (szString)) ;

 

      DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;

   }

 

 

   SetTextColor(hdc, BLACK_COLOR);

   for (i = 0 ; i < 12 ; i++)

   {

      lf.lfEscapement = lf.lfOrientation = i * 300 + iAnimateOrientation;

      SelectObject (hdc, CreateFontIndirect (&lf)) ;

 

      TextOut (hdc, 0, 0, szString, lstrlen (szString)) ;

 

      DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;

   }

 

 

}

 

// By 九天雁翎:

// 主要的新添函数

void PaintAnimateOrientation(HWND hwnd, HDC hdc, int cxArea, int cyArea)

{

   DWORD dwStartTime;

   DWORD dwEndTime;

   DWORD dwInTime;

   int iIntervalTimeNeed ;

   static int iAnimateOrientation = 0;

 

   dwStartTime = GetTickCount();

 

 

   iAnimateOrientation += 1;

 

   PaintRoutine(hwnd, hdc, cxArea, cyArea, iAnimateOrientation);

// 帧数控制

   while(GetTickCount() - dwStartTime < TIME_IN_FRAME)

   {

      Sleep(1);

   }

 

}

 

其实目前的源代码中有个瑕疵,因为是用透明文字背景画图,用白色的文字覆盖原有文字的时候会出现一些覆盖不了的情况,这个情况导致文字转动后会留有一些背景的尾巴。。。。效果如插图3了,另外。。。之所以使用此种循环画图方式纯粹因为最近看了《Window游戏编程大师级技巧》一书,其实用纯timer的方式实现如此简单的动画应该也是可行的,并且因为可以依赖消息机制的背景刷新,就不会有此问题。见下面的改良版旋转文字动画:

SetBkMode (hdc, TRANSPARENT) ;一句改为SetBkMode (hdc, OPAQUE) ;倒是可以解决这个问题,但是因为背景的不透明,中间R重合的部分会导致互相的遮掩,这点没有深入研究了,希望有人能给出更好的解决方案。另外,因为是使用GDI,所以在每秒20帧重画的时候都会有闪烁。。。。。

 

改良版旋转文字动画:

/*------------------------------------------------

   FONTDEMO.C -- Font Demonstration Shell Program

                 (c) 改自Charles Petzold, 1998

  ------------------------------------------------*/

 

#include <windows.h>

#include "EzFont.h"

#include "resource.h"

 

void PaintRoutine (HWND hwnd, HDC hdc, int cxArea, int cyArea, int iAnimateOrientation);

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

 

HINSTANCE hInst ;

 

 

extern TCHAR szAppName [] ;

extern TCHAR szTitle [] ;

 

#define WND_WIDTH (800)

#define WND_HEIGHT (800)

 

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

                    PSTR szCmdLine, int iCmdShow)

{

   TCHAR    szResource [] = TEXT ("FontDemo") ;

   HWND     hwnd ;

   MSG      msg ;

   WNDCLASS wndclass ;

   HDC hdc ;

 

   hInst = hInstance ;

 

   wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

   wndclass.lpfnWndProc   = WndProc ;

   wndclass.cbClsExtra    = 0 ;

   wndclass.cbWndExtra    = 0 ;

   wndclass.hInstance     = hInstance ;

   wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

   wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

   wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

   wndclass.lpszMenuName  = szResource ;

   wndclass.lpszClassName = szAppName ;

 

   if (!RegisterClass (&wndclass))

   {

      MessageBox (NULL, TEXT ("This program requires Windows NT!"),

        szAppName, MB_ICONERROR) ;

      return 0 ;

   }

 

   hwnd = CreateWindow (szAppName, szTitle,

      WS_OVERLAPPEDWINDOW,

      CW_USEDEFAULT, CW_USEDEFAULT,

      WND_WIDTH, WND_HEIGHT,

      NULL, NULL, hInstance, NULL) ;

 

   ShowWindow (hwnd, iCmdShow) ;

   UpdateWindow (hwnd) ;

 

   SetTimer(hwnd, 1, 50, NULL);

   // 主消息循环:

   while (GetMessage(&msg, NULL, 0, 0))

   {

      TranslateMessage(&msg);

      DispatchMessage(&msg);

   }

 

   return msg.wParam ;

}

 

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

{

   static DOCINFO  di = { sizeof (DOCINFO), TEXT ("Font Demo: Printing") } ;

   static int      cxClient, cyClient ;

   static PRINTDLG pd = { sizeof (PRINTDLG) } ;

   BOOL            fSuccess ;

   HDC             hdc, hdcPrn ;

   int             cxPage, cyPage ;

   PAINTSTRUCT     ps ;

   static int iAnimateOrientation = 0;

 

   switch (message)

   {

   case WM_COMMAND:

      switch (wParam)

      {

      case IDM_PRINT:

 

        // Get printer DC

 

        pd.hwndOwner = hwnd ;

        pd.Flags     = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION ;

 

        if (!PrintDlg (&pd))

           return 0 ;

 

        if (NULL == (hdcPrn = pd.hDC))

        {

           MessageBox (hwnd, TEXT ("Cannot obtain Printer DC"),

              szAppName, MB_ICONEXCLAMATION | MB_OK) ;

           return 0 ;

        }

        // Get size of printable area of page

 

        cxPage = GetDeviceCaps (hdcPrn, HORZRES) ;

        cyPage = GetDeviceCaps (hdcPrn, VERTRES) ;

 

         fSuccess = FALSE ;

 

        // Do the printer page

 

        SetCursor (LoadCursor (NULL, IDC_WAIT)) ;

        ShowCursor (TRUE) ;

 

        if ((StartDoc (hdcPrn, &di) > 0) && (StartPage (hdcPrn) > 0))

        {

           PaintRoutine (hwnd, hdcPrn, cxPage, cyPage, iAnimateOrientation) ;

 

           if (EndPage (hdcPrn) > 0)

           {

              fSuccess = TRUE ;

              EndDoc (hdcPrn) ;

           }

        }

        DeleteDC (hdcPrn) ;

 

        ShowCursor (FALSE) ;

        SetCursor (LoadCursor (NULL, IDC_ARROW)) ;

 

        if (!fSuccess)

           MessageBox (hwnd,

           TEXT ("Error encountered during printing"),

           szAppName, MB_ICONEXCLAMATION | MB_OK) ;

        return 0 ;

 

      case IDM_ABOUT:

        MessageBox (hwnd, TEXT ("Font Demonstration Program/n")

           TEXT ("(c) Charles Petzold, 1998"),

           szAppName, MB_ICONINFORMATION | MB_OK);

        return 0 ;

      }

      break ;

 

   case WM_SIZE:

      cxClient = LOWORD (lParam) ;

      cyClient = HIWORD (lParam) ;

      return 0 ;

 

   case WM_PAINT:

      hdc = BeginPaint (hwnd, &ps) ;

 

      PaintRoutine(hwnd, hdc, WND_WIDTH, WND_HEIGHT, iAnimateOrientation);

 

      EndPaint (hwnd, &ps) ;

      return 0 ;

   case WM_TIMER:

      {

        iAnimateOrientation += 1;

        InvalidateRect(hwnd, NULL, TRUE);

        return 0;

      }

 

   case WM_DESTROY :

      PostQuitMessage (0) ;

      return 0 ;

   }

   return DefWindowProc (hwnd, message, wParam, lParam) ;

}

 

#define BLACK_COLOR (RGB(0,0,0))

TCHAR szAppName [] = TEXT ("FontRot") ;

TCHAR szTitle   [] = TEXT ("FontRot: Rotated Fonts") ;

void PaintRoutine (HWND hwnd, HDC hdc, int cxArea, int cyArea, int iAnimateOrientation)

{

   static TCHAR szString [] = TEXT ("   Rotation") ;

   HFONT        hFont ;

   int          i ;

   LOGFONT      lf ;

 

 

   hFont = EzCreateFont (hdc, TEXT ("Times New Roman"), 540, 0, 0, TRUE) ;

   GetObject (hFont, sizeof (LOGFONT), &lf) ;

   DeleteObject (hFont) ;

 

   SetBkMode (hdc, TRANSPARENT) ;

   SetTextAlign (hdc, TA_BASELINE) ;

   SetViewportOrgEx (hdc, cxArea / 2, cyArea / 2, NULL) ;

 

   SetTextColor(hdc, BLACK_COLOR);

   for (i = 0 ; i < 12 ; i++)

   {

      lf.lfEscapement = lf.lfOrientation = i * 300 + iAnimateOrientation;

      SelectObject (hdc, CreateFontIndirect (&lf)) ;

 

      TextOut (hdc, 0, 0, szString, lstrlen (szString)) ;

 

      DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;

   }

 

 

}

注意几个改动的地方,其实这个版本应该是最先的思路。。。。。。及利用TimerWindows消息机制来完成动画绘制,这样可以利用背景擦除消息来擦除背景,不需要重新自己用白色文字覆盖上一帧的文字,也不会有拖尾现象,但是,因为总是重复擦除然后重绘,所以会有闪烁。

 

另外,从理论上来说,直接将绘制放在擦除背景消息中,如下:

   case WM_PAINT:

      hdc = BeginPaint (hwnd, &ps) ;

 

      EndPaint (hwnd, &ps) ;

      return 0 ;

   case WM_ERASEBKGND:

      hdc = GetDC(hwnd);

      Rectangle(hdc, 0, 0, WND_WIDTH, WND_HEIGHT);

      PaintRoutine(hwnd, hdc, WND_WIDTH, WND_HEIGHT, iAnimateOrientation);

      ReleaseDC(hwnd, hdc);

      return 0;

闪烁会小一点,因为擦除背景到重新绘制的间隔更短了(放在WM_PAINT中还有个重新分配消息的过程),但是事实上看不怎么出来。

随机文字的那个例子比较简单,这里不再实现了。

 

五、   参考:

1. Windows 图形编程》第1415章(原版名《Windows Graphics Programming Win32 GDI and DirectDraw》,Feng Yuan著,机械工业出版社

2.Programming Windows Fifth EditionChapter4,17 Charles Petzold著,Microsoft Press

 

 

 

插图1:

 

 

插图3:

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

ubuntu 安装视频解码控件及文档

最近老在折腾ubuntu。。。。。呵呵,学习辛苦了,偶尔放松一下,对工作的环境(其实我主要工作还是在windows的VS中,Linux属于假想工作环境-_0!)

 

ubuntu由于有apt,deb包管理相当轻松,大部分任务就是sudo apt-get install XXXX,呵呵,实在是奇迹。。。。。

 

 

realplayer装了没有用。。。。原因不明。

 

国外的这个文章很牛:(http://www.ubuntugeek.com/install-mplayer-and-multimedia-codecs-libdvdcss2w32codecsw64codecs-in-ubuntu-904-jaunty.html

Install libdvdcss2 and w32 video codecs in Ubuntu 9.04 (Jaunty)

Support for WMV, RealMedia and other formats has been bundled into the w32codecs package. This package is not available from the Ubuntu repositories due to licensing and legal restrictions.To play encrypted DVDs, the libdvdcss2 package is essential.

For Ubuntu 9.04 (Jaunty) Users use the following procedures

sudo wget http://www.medibuntu.org/sources.list.d/jaunty.list --output-document=/etc/apt/sources.list.d/medibuntu.list

Then, add the GPG Key using the following commands

sudo apt-get update

For i386 Users install Codecs using the following command

sudo apt-get install medibuntu-keyring

sudo apt-get update

sudo apt-get install w32codecs libdvdcss2

For amd64 Users install Codecs using the following command

sudo apt-get install w64codecs libdvdcss2

Using above download locations you can install most of the mutimedia codecs for ubuntu.

Mplayer Plugin for Firefox

If you want to install Mplayer with plug-in for Mozilla Firefox run the following command

sudo apt-get install mozilla-mplayer

 

 

解决了很多问题

 

 

有个问题很有意思,现在我在windows下用kmplayer,感觉很好,很习惯,比windows下的mplayer还好用,还美观,现在改用了KDE(主要是因为学了QT的原因,其实我以前都是用Gnome),想都没有想,直接上在ubuntu上装kmplayer用,结果完全不好用,再查了查,还有个kplayer,还是不好用,都删掉,换成mplayer..............一切如以前般强大,接近无语。。。。。。

 

 

 

接着,开发库文档的问题:【转】

 

添加库函数手册
ubuntu默认是没有安装c语言的库函数man手册的,所以你在man perror 和sendto之类的函数时会显示没有相关文档的问题,这个问题让我郁闷了我好久。解决方法:
sudo apt-get install manpages-dev

 

事实上,也有中文版的文档了,只是看起来比较别扭(可能因为ubuntu的控制台下中文字不够美观)

 

选择man页面的方式是man 页面号 文档,比如我要查sleep的库函数调用,那就是man 3 sleep

阅读全文....

Ubuntu 9.04禁用触摸板的办法

现在将笔记本带到公司去了(好寒酸啊。。。。带着女朋友的HP520本本),装了Ubuntu9.04,这个本子很多地方设计不合理,其中USB的位置有问题,触摸板在打字的时候很容易误碰(对于敲程序来说太致命了),所以找了半天禁用的办法,流传最广的8.04下可用的例子已经不能用了,真的可用办法如下:

 

用 

sudo rmmod psmouse

禁用

要恢复也简单:
sudo modprobe psmouse 

 

 

 

来自

http://blog.3gcomet.com/trackback.asp?tbID=343&action=addtb&tbKey=627fe24937da2cebf94fe2f5a0b2022ebb428cbc

 

 

一切好了,哈雷路亚。。。。。。在Linux下没有不能做到的事情,只是看你知不知道怎么做。。。。。其实这个事情我折腾了很久。

阅读全文....

【转载】ubuntu搭建subversion(svn)服务器

关于普通的配置http://wiki.ubuntu.org.cn/SubVersion
中讲解的很详细

 

但是因为是做服务器,我希望开机即启动,这样排除手工干预,才能将服务器的显示器,鼠标,键盘省下来-_-!。。。

 

 

这里从别的网站转来的文章:(因为原来那个网站不厚道,没有出处,我也没有办法了,不是我不注明)

 

Ubuntu Add commentsubuntu预设是跑runleve2,也就是/etc/rc2.d内的软连结档,真正的服务设定档都在/etc/init.d当中。

 

我在/etc/rc2.d执行ls可以看得到firestarter,代表在开机时他的确有执行,事实上/etc/rc2.d到/etc/rc5.c应该都是一样的。

 

你也可以自行设定开机要跑哪一个runlevel。

 

可以试试这个套件:

 

$ sudo apt-get install sysvconfig

$ sudo sysvconfig

 

就可以设定开机服务。

 

 

 

其实上面的说法很简单,有点看不懂,基本思路是将服务器启动的脚本卸载/etc/init.d中,然后建立软连接(用ln -s)到/etc/rc2.d中去,这样就能那里面的启动脚本会在开机时自动运行,也就达到了我们要的开机运行效果。

 

阅读全文....

【转载】ubuntu搭建ssh服务器

昨天的文章里,我记录了搭建一个小型 Ubuntu 服务器的过程,相信如果各位按照我写的做了,您的 Ubuntu Server 已经可以跑起来了。不过,现在这个系统只是个基本的系统,算不上服务器,因为什么都没法做。如果需要让它行使服务器的职能,还需要给它装一些软件。

因为我需要的服务器最终会被“抛弃”在实验室的某个角落,无论是后期设置还是维护的需要,都必须安装一个远程管理的工具。在 Linux 系统中,不二的选择就是 openssh 了。在 Ubuntu 中安装 openssh 实在是再简单不过的一件事情了,下面的内容也只是纯记录,给我这个菜鸟备个份。如果是高手直接绕过就好。

安装前的准备

Ubuntu 之所以好用,就是因为它继承了 debian 的 apt 系统,这一点相信您在昨天装系统的教程中已经感受到了。但是 apt 需要依赖网络,昨天我们装好的系统是暂时上不了网的,我们需要先设置一下。

首先,激活服务器的网卡,命令如下:

sudo nano /etc/network/interfaces

在 interfaces 中添加以下内容:

auto eth0
iface eth0 inet static
address 202.113.235.181
netmask 255.255.255.0
gateway 202.113.235.1

这其中,斜体部分标注的 IP 地址是我服务器的设置,您需要根据您的具体情况修改。当然,如果您的服务器使用的是 DHCP 来分配 IP 地址,只需要写上 iface eth0 inet dhcp 就可以了,无需设置 address/netmask/gateway。

然后,修改 resolv.conf 配置 DNS 服务器:

sudo nano /etc/resolv.conf

添加您的 DNS 服务器地址:

nameserver 202.113.16.10
nameserver 202.113.16.11

完成后,重新启动 networking 服务:

sudo /etc/init.d/networking restart

这样应该就可以连通网络了。如果您使用的是 ADSL,可能还需要装上 pppoe 之类的东西,考虑到服务器很少用这样的配置,这里就不讨论了,需要的话可以在网上查找。

安装和设置 OpenSSH Server

Ubuntu 下安装 OpenSSH Server 是无比轻松的一件事情,需要的命令只有一条:

sudo apt-get install openssh-server

随后,Ubuntu 会自动下载并安装 openssh server,并一并解决所有的依赖关系。当您完成这一操作后,您可以找另一台计算机,然后使用一个 SSH 客户端软件(强烈推荐 PuTTy),输入您服务器的 IP 地址。如果一切正常的话,等一会儿就可以连接上了。并且使用现有的用户名和密码应该就可以登录了。

事实上如果没什么特别需求,到这里 OpenSSH Server 就算安装好了。但是进一步设置一下,可以让 OpenSSH 登录时间更短,并且更加安全。这一切都是通过修改 openssh 的配置文件 sshd_config 实现的。

首先,您刚才实验远程登录的时候可能会发现,在输入完用户名后需要等很长一段时间才会提示输入密码。其实这是由于 sshd 需要反查客户端的 dns 信息导致的。我们可以通过禁用这个特性来大幅提高登录的速度。首先,打开 sshd_config 文件:

sudo nano /etc/ssh/sshd_config

找到 GSSAPI options 这一节,将下面两行注释掉:

#GSSAPIAuthentication yes
#GSSAPIDelegateCredentials no

然后重新启动 ssh 服务即可:

sudo /etc/init.d/ssh restart

再登录试试,应该非常快了吧 :)

利用 PuTTy 通过证书认证登录服务器

SSH 服务中,所有的内容都是加密传输的,安全性基本有保证。但是如果能使用证书认证的话,安全性将会更上一层楼,而且经过一定的设置,还能实现证书认证自动登录的效果。

首先修改 sshd_config 文件,开启证书认证选项:

RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile %h/.ssh/authorized_keys

修改完成后重新启动 ssh 服务。

下一步我们需要为 SSH 用户建立私钥和公钥。首先要登录到需要建立密钥的账户下,这里注意退出 root 用户,需要的话用 su 命令切换到其它用户下。然后运行:

ssh-keygen

这里,我们将生成的 key 存放在默认目录下即可。建立的过程中会提示输入 passphrase,这相当于给证书加个密码,也是提高安全性的措施,这样即使证书不小心被人拷走也不怕了。当然如果这个留空的话,后面即可实现 PuTTy 通过证书认证的自动登录。

ssh-keygen 命令会生成两个密钥,首先我们需要将公钥改名留在服务器上:

cd ~/.ssh
mv id_rsa.pub authorized_keys

然后将私钥 id_rsa 从服务器上复制出来,并删除掉服务器上的 id_rsa 文件。

服务器上的设置就做完了,下面的步骤需要在客户端电脑上来做。首先,我们需要将 id_rsa 文件转化为 PuTTy 支持的格式。这里我们需要利用 PuTTyGEN 这个工具:

点击 PuTTyGen 界面中的 Load 按钮,选择 id_rsa 文件,输入 passphrase(如果有的话),然后再点击 Save PrivateKey 按钮,这样 PuTTy 接受的私钥就做好了。

打开 PuTTy,在 Session 中输入服务器的 IP 地址,在 Connection->SSH->Auth 下点击 Browse 按钮,选择刚才生成好的私钥。然后回到 Connection 选项,在 Auto-login username 中输入证书所属的用户名。回到 Session 选项卡,输入个名字点 Save 保存下这个 Session。点击底部的 Open 应该就可以通过证书认证登录到服务器了。如果有 passphrase 的话,登录过程中会要求输入 passphrase,否则将会直接登录到服务器上,非常的方便。

好了,今天就写到这,以后逐步再写 AMP,Proftpd 和 Squid 的安装和设置。

阅读全文....

【转载】linux下的samba安装及配置

以前配置samba一直是我的噩梦,甚至一度放弃samba,直接使用ssh+winscp来管理文件,今天找到一篇好文,顺利配置成功,感谢原作者。

在Ubuntu中设置samba共享可读写文件夹 收藏
首先当然是要安装samba了,呵呵: 代码:
sudo apt-get install samba sudo apt-get install smbfs
下面我们来共享群组可读写文件夹,假设你要共享的文件夹为: /home/ray/share 首先创建这个文件夹 代码:
mkdir /home/ray/share chmod 777 /home/ray/share
备份并编辑smb.conf允许网络用户访问 代码:
sudo cp /etc/samba/smb.conf /etc/samba/smb.conf_backup sudo gedit /etc/samba/smb.conf
搜寻这一行文字 代码:
; security = user
用下面这几行取代 代码:
security = user username map = /etc/samba/smbusers
将下列几行新增到文件的最后面,假设允许访问的用户为:newsfan。而文件夹的共享名为 Share 代码:
[Share] comment = Shared Folder with username and password path = /home/ray/share public = yes writable = yes valid users = newsfan create mask = 0700 directory mask = 0700 force user = nobody force group = nogroup available = yes browseable = yes
然后顺便把这里改一下,找到[global]把 workgroup = MSHOME 改成 代码:
workgroup = WORKGROUP display charset = UTF-8 unix charset = UTF-8 dos charset = cp936
后面的三行是为了防止出现中文目录乱码的情况。其中根据你的local,UTF-8 有可能需要改成 cp936。自己看着办吧。 现在要添加newsfan这个网络访问帐户。如果系统中当前没有这个帐户,那么 代码:
sudo useradd newsfan
要注意,上面只是增加了newsfan这个用户,却没有给用户赋予本机登录密码。所以这个用户将只能从远程访问,不能从本机登录。而且samba的登录密码可以和本机登录密码不一样。 现在要新增网络使用者的帐号: 代码:
sudo smbpasswd -a newsfan sudo gedit /etc/samba/smbusers
在新建立的文件内加入下面这一行并保存 代码:
newsfan = "network username"
如果将来需要更改newsfan的网络访问密码,也用这个命令更改 代码:
sudo smbpasswd -a newsfan
删除网络使用者的帐号的命令把上面的 -a 改成 -x 代码:
sudo testparm sudo /etc/init.d/samba restart
最后退出重新登录或者重新启动一下机器。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/joliny/archive/2008/07/13/2646420.aspx

 

另外还有两篇备用的:

第一篇:

root根用户进行操作,如果您不是,请在每条命令前加上sudo

步骤1:安装samba
#apt-get install samba
#apt-get install smbfs

步骤2:添加linux用户
#useradd user1
     //添加用户名user1
#passwd user1 //
给用户名user1添加密码
#mkdir /home/user1 //
建立user1home目录,如果不用这个用户名来登陆linux,此步骤不是必需
#chown -R user1:user1 /home/user1
   //user1home目录设置好权限

步骤3:给samba服务器添加用户
说明:登陆samba的用户必须已经是linux中的用户
#smbpasswd -a user1 //
添加并给user1设置samba密码

步骤4smb.conf设置
#cd /etc/samb //
进入设置目录
#mv smb.conf
 smb.conf.bak //做好备份,直接将系统默认配置文件改名
#vim smb.conf //
建立和配置smb.conf文件

[global]
workgroup=x1 //X1为你局域网中的工作组名
server string=x2 //x2
为你linux主机描述性文字,比如:samba server
security=user //samba
的安全等级,user代表需要输入用户名和密码,改成share则不需要输入用户名和密码

[x3] //方框号中的x3这个名字可以随便取,只是在win的网上邻居中显示的共享文件夹名
path=/home/x4 //x4
为你要共享的文件夹名,在共享前还要建立这个文件夹,并设好权限以便访问,下面会说明。
valid users=user1 //
这个x4共享目录只允许user1这个用户进入
public=no
     //no表示除了user1这个用户,其它用户在进入samba服务器后看不见x4这个目录,如果为yes,虽然能看见x4这个目录,但除了user1这个用户能进入这个目录,其它人进不了。
writable=yes
   //允许user1x4目录中进行读和写操作,反之no

//存盘退出
#testparm //
检查一下语法错误,比如拼错

步骤5:建立共享目录
#mkdir /home/x4
#chown -R user1:user1 /home/x4 //
因为是root建立的目录,其它用户只有读的权限,所还得把权限改一下。当然也可以简单的用#chmod 777 /home/x4。还有个问题就是共享里目录的文件如果有些能访问有些不能访问,那肯定也是权限的问题,进入/home/x4,直接#chmod 777 *来解决。

步骤6:重启samba服务
#/etc/init.d/samba restart

设置samba服务要注意以下两点(即两个两次):
1.
添加两次用户:一次添加系统用户#useradd user1;再一次是添加samba用户#smbpasswd -a user1;
2.
设置两次权限:一次是在smb.conf中设置共享文件夹的权限:再一次是在系统中设置共享文件夹的权限#chmod 777 文件夹名。

 

按照楼主的做法设置, WINDOWS登陆到samba的时候报错, 显示如//10.x.x.x 无法访问, 你可能没有权限使用网络资源.... 这样的错误. 请问这是怎么回师?

如果设置passdb backend = tdbsam 则可以访问

 

passdb backend = tdbsam
tdbsam的时候,密码文件是放在 /var/lib/samba/passdb.tdb

而没有这句,当然也就找不到SMB用户和密码,即提示没用权限了。

 

第二篇:

#rpm -q samba     //查看SAMBA是否安装,如未安装,则执行下列安装

#rpm -ivh  samba-3.0.25b-0.4E.6.i386.rpm

#vi /etc/services    //查看以netbios开头的是否可用,必须要全部可用

 

 

  //linux防火墙要关闭

#ls /etc/samba //无smbpasswd文件

#smbpasswd -a tom //创建tom用户

#ls /var/log/samba //smb服务器的日志文件

#vi /etc/samba/smb.conf //编辑smb服务器的主配置文件

 

  Workgroup = WORKGROUP //windows工作组名

  server string = samba server //samba服务器简要说明

  hosts allow = 192.168.1. 192.168.0. 127. //设置允许访问的子网,默认都允许,可不设置

  hosts allwo = client1,alarm.com,192.168.16. EXCEPT 192.168.16.4 //举例说明

  security = user //设置安全级,默认为user。五个级别分别为"share,user,server,domain,ads"

  interfaces = eth0 //多网卡SAMBA服务器设置监听的网卡

  interfaces = 192.168.16.177/24 //举例说明

  wins support = yes //设置将samba服务器作为wins服务器,默认不使用

 

  //wins服务器由微软开发,功能是将NetBIOS名称转换为对应的ip地址

  username map = /etc/samba/smbusers //去掉前面的;号,用于用户映射

  然后编辑文件/etc/samba/smbusers,将需要映射的用户添加进去,格式为

  单独的linux用户 = 要映射的windows用户列表

  例: test = alarm back //test用户对应windows下的alarm和back两个用户

  encrypt password = yes 或 no //yes表示采用加密方式发送密码,no为不采用

  若此项为no,则windows系统必须响应的修改注册表项,注册表文件存放在/usr/share/doc/samba-2.2.7a/docs/Registry下

 

  [homes] //设置共享目录

  comment = Home Directories //简要说明

  browseable = no //是否允许用户浏览所有人的主目录

  writable = yes //是否允许用户写入自己的主目录

 

  [share] //设置一个共享目录

  comment = Samba's share Directory //简要说明

  read list = test //只读用户或组

  write list = @share //可写用户或组

  path = /home/share //共享文件夹目录路径

 

  //设置共享目录后需要做以下操作

  1.root登录,使用命令groupadd share 建立share组,并用usermod -g share abc命令将abc用户添加到share组

  2.mkdir /home/share在/home下建立目录share

  3.chown :share /home/share设置share目录所属组为share组,chmod 777 /home/share 设置share组对该目录有最大权限

  :wq //保存退出

 

 

  #testparm //测试smb.conf文件是否有语法错误

  #su - //切换root用户

  #service smb start restart stop //启动 重启 停止samba服务器

  #ntsysv //设置samba服务器开机启动

阅读全文....

【转载】jdk-6-doc安装失败的解决办法

去这个地址

http://java.sun.com/javase/downloads/

下载 jdk-6-doc.zip(英文) 或者 jdk-6-doc-ja.zip(日文) 包(二者选一即可)

sudo chown root:root jdk-6-doc.zip 改变文件所有者为root

cp jdk-6-doc.zip /tmp 下 然后再执行安装程序

/tmp 目录下面的东西会在系统关闭的时候被删除,所以如果想保存jdk-6-doc.zip请把它备份到其他位置

 

http://forum.ubuntu.org.cn转载过来的

阅读全文....