
OpenGLで矩形選択
画像編集するアプリケーションでは、画像の特定の部分を選んで何かしたい場合がある。たとえば文章の画像があって、2行目と3行目を黒塗りにしたい、など。この場合には、マウスで2行目と3行目全体を含めるように矩形(長方形)を描いてその中を黒く塗りつぶせばいい。この記事には、OpenGLを使ってその矩形選択した領域を画面上に示すコードのメモを残す。
矩形領域は、ウィンドウ内の2点をマウスで指示することで決める。その2点を結ぶ線分を対角線として、辺は画面水平垂直方向とするように矩形を決める。
マウスで指示した点の位置はMouseEventArgs.Locationで取れる。
// C++/CLI
ref class CustomControl : System::Windows::Forms::UserControl
{
protected:
virtual void OnMouseMove(
System::Windows::Forms::MouseEventArgs^ e ) override
{
System::Drawing::Point p = e->Location;
}
};
MouseEventArgs.Locationは、ウィンドウの左上隅を原点として画面の水平右方向を$${x}$$軸、画面の垂直下方向を$${y}$$軸とする2次元座標系上の点として得られる。マウスで選んだ2点を$${P_1}$$、$${P_2}$$として下の図のようになる。

一方でOpenGLは最終的に、
$$
-1 \leqq x \leqq 1, -1 \leqq y \leqq 1, -1 \leqq z \leqq 1
$$
の領域内にあるものを、視線方向を$${z}$$軸に合わせた状態で画面に描き出す。
ウィンドウの幅を$${w}$$、高さを$${h}$$とすると、左上隅を原点とする$${w \times h}$$の長方形を、左下隅を原点とする$${2 \times 2}$$の正方形にぴったり重なるように変形させれば、マウスでウィンドウ上に指定した点$${P_1}$$、$${P_2}$$もOpenGLの世界に投影できる。あとはこの2点を頂点とする四角形(GL_LINE_LOOPまたはGL_QUADS)をOpenGLで描けば、矩形選択した領域が画面上に表示される。
この変換は、
$$
\begin{pmatrix}
2.0/w 0 0 -1 \\
0 -2.0/h 0 1 \\
0 0 1 0 \\
0 0 0 1
\end{pmatrix}
$$
という行列で表現できるので、変換行列の設定処理は以下のようになる。OpenGL の行列は転置して設定する必要があることに注意。
void SetupProjection()
{
int viewport[4];
memset( viewport, 0x00, sizeof( int ) * 4 );
glGetIntegerv( GL_VIEWPORT, viewport );
int w = viewport[2] - viewport[0];
int h = viewport[3] - viewport[1];
float m11 = 2.0f / (float)w;
float m22 = -2.0f / (float)h;
float prj[] = {
m11, 0, 0, 0,
0, m22, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1 };
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glMultMatrixf( prj );
}
矩形領域を描く処理は以下のコード。引数で与えている $${P_1}$$、$${P_2}$$がマウスで指定した位置。MouseEventArgs.Location。
void DrawSelectedRect(
System::Drawing::Point P1, System::Drawing::Point P2 )
{
glDisable( GL_LIGHTING );
int lx = System::Math::Min( P1.X, P2.X );
int ux = System::Math::Max( P1.X, P2.X );
int ly = System::Math::Min( P1.Y, P2.Y );
int uy = System::Math::Max( P1.Y, P2.Y );
glColor4ub( (byte)128, (byte)128, (byte)128, (byte)32 );
glBegin( GL_QUADS );
glVertex2i( lx, ly );
glVertex2i( ux, ly );
glVertex2i( ux, uy );
glVertex2i( lx, uy );
glEnd();
glLineWidth( 3 );
glColor4ub( 0, 0, 0, (byte)255 );
glBegin( GL_LINE_LOOP );
glVertex2i( lx, ly );
glVertex2i( ux, ly );
glVertex2i( ux, uy );
glVertex2i( lx, uy );
glEnd();
glEnable( GL_LIGHTING );
}
下の図のように、矩形選択した領域が、枠線付きで半透明の長方形で表示される。
