ブレゼンハムアルゴリズム
前回では、カーソルの位置に点を描画するだけだったので、描画結果は直線には見えませんでした。
これを繋がった直線にするためには、点と点の間を、直線で描画する必要があります。
直線を描画するアルゴリズムには、有名なものでは「ブレゼンハム」のアルゴリズムがあります。
(参考) wikipedia:ブレゼンハムのアルゴリズム
他にも固定小数点演算を使った方法もありますが、これは後述します。
まずは、ブレゼンハムアルゴリズムを使って直線描画を実装し、ちゃんとした自由線を描画させます。
これを繋がった直線にするためには、点と点の間を、直線で描画する必要があります。
直線を描画するアルゴリズムには、有名なものでは「ブレゼンハム」のアルゴリズムがあります。
(参考) wikipedia:ブレゼンハムのアルゴリズム
他にも固定小数点演算を使った方法もありますが、これは後述します。
まずは、ブレゼンハムアルゴリズムを使って直線描画を実装し、ちゃんとした自由線を描画させます。
スクリーンショット
ソースコード
003_freeline.c
#include "sptk.h" #define WIDTH 300 #define HEIGHT 300 #define PIXCOL 0x0000ff int lastx,lasty; void drawline(SPTK_IMAGE *img,int x1,int y1,int x2,int y2,uint32_t col) { int dx,dy,sx,sy,a,a1,e; if(x1 <= x2) dx = x2 - x1, sx = 1; else dx = x1 - x2, sx = -1; if(y1 <= y2) dy = y2 - y1, sy = 1; else dy = y1 - y2, sy = -1; if(dx >= dy) { a = 2 * dy; a1 = a - 2 * dx; e = a - dx; while(1) { sptk_image_setpixel(img, x1, y1, col); if(x1 == x2) break; if(e >= 0) y1 += sy, e += a1; else e += a; x1 += sx; } } else { a = 2 * dx; a1 = a - 2 * dy; e = a - dy; while(1) { sptk_image_setpixel(img, x1, y1, col); if(y1 == y2) break; if(e >= 0) x1 += sx, e += a1; else e += a; y1 += sy; } } } void winhandle(SPTK_EVENT *ev) { SPTK_RECT rc; switch(ev->type) { case SPTK_EVENT_BTTDOWN: if(ev->mouse.btt == SPTK_MOUSEBTT_LEFT) { lastx = ev->mouse.x; lasty = ev->mouse.y; sptk_grab(NULL); } break; case SPTK_EVENT_BTTUP: if(ev->mouse.btt == SPTK_MOUSEBTT_LEFT && sptk_isgrab()) sptk_ungrab(); break; case SPTK_EVENT_MOUSEMOVE: if(sptk_isgrab()) { rc.x1 = lastx; rc.y1 = lasty; rc.x2 = ev->mouse.x; rc.y2 = ev->mouse.y; drawline(sptk_window_get_image(), rc.x1, rc.y1, rc.x2, rc.y2, PIXCOL); sptk_update_rect(NULL, &rc, 0); lastx = ev->mouse.x; lasty = ev->mouse.y; } break; case SPTK_EVENT_WINDOW_CLOSE: sptk_quit(); break; } } int main() { sptk_init("test", WIDTH, HEIGHT); sptk_window_set_handle(winhandle); sptk_run(); return 0; }
解説
drawline() 関数が、ブレゼンハムによる直線描画の処理です。
動作
直線を描画するためには、始点と終点の2点が必要です。
まず、ボタンが押された時は、その位置が直線の「始点」になるので、変数にその位置を記録しておきます。
次に、カーソルが移動された時は、「始点」はボタンが押された時の位置、「終点」は現在位置となるので、[押し時に記録しておいた位置〜現在位置] までを直線で引きます。
次に引く直線では、「始点」は現在の位置になるので、次の始点として現在位置を記録しておきます。
以降は、[記録しておいた位置〜現在位置] までを直線で引きます。
これを繰り返せば、直線での自由線が描画できます。
ここでは、始点を記録しておくための変数として lastx,lasty が用意されています。
この変数に常に始点位置を記録しておきます。
まず、ボタンが押された時は、その位置が直線の「始点」になるので、変数にその位置を記録しておきます。
次に、カーソルが移動された時は、「始点」はボタンが押された時の位置、「終点」は現在位置となるので、[押し時に記録しておいた位置〜現在位置] までを直線で引きます。
次に引く直線では、「始点」は現在の位置になるので、次の始点として現在位置を記録しておきます。
以降は、[記録しておいた位置〜現在位置] までを直線で引きます。
これを繰り返せば、直線での自由線が描画できます。
ここでは、始点を記録しておくための変数として lastx,lasty が用意されています。
この変数に常に始点位置を記録しておきます。
更新
今回は更新に sptk_update_rect() を使っています。
点 (x1,y1) と 点 (x2,y2) からなる矩形範囲を更新させます。
この関数は、値の小さい方が左上、値の大きい方が右下の位置になるように処理されるので、左上、右下を意識する必要はありません。
点 (x1,y1) と 点 (x2,y2) からなる矩形範囲を更新させます。
この関数は、値の小さい方が左上、値の大きい方が右下の位置になるように処理されるので、左上、右下を意識する必要はありません。