2006年03月09日
川俣晶の縁側ソフトウェア技術雑記total 3834 count

描画システムの座標変換機能と、ビットマップ、文字描画の制約の思い出

Written By: 川俣 晶連絡先

 99%ある種のノスタルジーの話題です。

座標系変形の昔話 §

 マイコン/パソコン系の最も最初のグラフィック描画システムは、おそらくクリッピングの機能すらろくに持たない極めて貧弱なものだったと思います。それこそ、「画面を消す」「点を打つ」「点を消す」ぐらいの機能しかないものです。

 これが、徐々に進化を始め、16bit版Windowsの描画システムであるGDIでは、クリッピングができるだけでなく、座標系のスケールを自由に取れるようになりました。つまり画面上の1ピクセルが指定座標数値のどれぐらいに対応するかが自由に指定できるようになったわけです。これによって、描画データが持つ本来の数値を渡しつつ、画面上では適切なピクセル数で描くということが可能になりました。(いやまて、N88-BASICぐらいで、既にウィンドウやビューポートの機能を持っていて可能だったか?)

 これはX座標とY座標について独立したスケーリングを指定できるために、わざと扁平にひしゃげるように描かせることも可能でした。

 更にこれが16bit版のOS/2(当時はWindowsの後継OS)になると、座標変換行列が指定可能になり、それこそ好きなように座標系を変形させることができたわけです。

 ちなみに、この「座標変換行列が指定可能」という機能を知ったときには痺れましたねぇ。本当にまともなスピードで処理できるのか疑問に思いましたが、機能としては夢があって魅力があると思いました。

昔の制約 §

 もう確かな記憶ではありませんが。

 これらの座標系の変形の機能は、直線などベクトル系の描画では機能したものの、文字の描画とビットマップの描画では動作しなかったという記憶があります。

 ベクトルデータは座標値を演算するだけで変形に対応できますが、高速描画のために特殊な構造になっていて、デバイスドライバレベルで描かれる文字と、データ量が大きく、これまた特殊な構造で処理されるビットマップは、座標系の変換を指定しても、機能してくれなかったように思います。

 それゆえに、実は「座標変換行列が指定可能」というような機能はあまり使い勝手が良くなかったという印象があります。

そして制約からは解放された §

 .NET Framework(あるいはGDI+?)は、このような制約をスカッと飛ばして、変換行列によって文字もビットマップも変形させることができます。

 というわけで、テストプログラムを作ってみました。

 以下は画面のスナップショットです。

画面スナップショット

 プログラム本体は、ノータッチデプロイメントですぐ実行して試せるようになっています。

 以下のリンクをクリックしてみてください。

回転サンプルプログラム .NET Framework 2.0要 ノータッチデプロイメント

 いや~、ほんとに。回転する画像と文字を見るのは、ある種のトラウマから解放された快感ですね。

 とはいえ、本当にこれで良いのは分かりませんが。

例外という問題 §

 実は実行させると、しばしば例外が発生します。

 この例外はテキストボックスに記録するように作成しました。

 ArgumentExceptionは、どのようなケースで出るのかドキュメントに記述が無く、詳細不明です。何となく不都合があるパラメータがあるのだろうということは推測できますが、どこからどこまでの値に不都合が出るのか、詳細不明です。

 OutOfMemoryExceptionの方は、そもそも出る理由が不明です。メモリ不足ということはありませんし、そのまま継続して次のフレームの描画に成功していることから考えて、何らかのシステムリソース不足とも思えません。

テストプログラム主要部 §

 Visual Studio 2005のC#によるWindowsアプリケーションのプロジェクトです。

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

namespace RotateTransform001

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

        private float param1 = 0.0f;

        private float param2 = 0.0f;

        private float param3 = 0.0f;

        private float param4 = 0.0f;

        private float param5 = 0.0f;

        private float param6 = 0.0f;

        private void pictureBox1_Paint(object sender, PaintEventArgs e)

        {

            try

            {

                int centerX = this.pictureBox1.Width / 2;

                int centerY = this.pictureBox1.Height / 2;

                using (System.Drawing.Drawing2D.Matrix transformMatrix = 

                                        new System.Drawing.Drawing2D.Matrix(

                    (float)Math.Cos(param3 / 360.0f * Math.PI),

                    (float)Math.Sin(param4 / 360.0f * Math.PI),

                    -(float)Math.Sin(param5 / 360.0f * Math.PI),

                    (float)Math.Cos(param6 / 360.0f * Math.PI), 0, 0

                    ))

                {

                    e.Graphics.TranslateTransform(centerX, centerY);

                    e.Graphics.MultiplyTransform(transformMatrix);

                    e.Graphics.DrawRectangle(Pens.Blue, 0, 0, 10, 10);

                    e.Graphics.DrawImage(this.pictureBox2.Image, 

                        -this.pictureBox2.Image.Width / 2,

                        -this.pictureBox2.Image.Height / 2);

                    e.Graphics.ResetTransform();

                    e.Graphics.TranslateTransform(centerX, centerY);

                    e.Graphics.MultiplyTransform(transformMatrix);

                    const string message = "ROTATING";

                    e.Graphics.DrawString(message, new Font("Arial", 20.0f), 

                                     Brushes.Black, new PointF(0.0f, 0.0f));

                    e.Graphics.DrawString(message, new Font("Arial", 20.0f), 

                                   Brushes.Magenta, new PointF(1.0f, 1.0f));

                }

            }

            catch (System.Exception ex)

            {

                this.label2.Text = (int.Parse(this.label2.Text) + 1).ToString();

                this.textBox1.Text = DateTime.Now.ToString() + ":\r\n" 

                            + ex.ToString() + "\r\n\r\n" + this.textBox1.Text;

            }

        }

        private void timer1_Tick(object sender, EventArgs e)

        {

            param1 = (param1 + 5.0f);

            param2 = (param2 - 4.0f);

            param3 = (param3 + 3.5f);

            param4 = (param4 + 3.0f);

            param5 = (param5 + 2.5f);

            param6 = (param6 + 2.0f);

            this.pictureBox1.Invalidate();

        }

    }

}

あっ! §

 サンプルソース、最後にいろいろいじったら、凄く無駄だらけのコードになってる。

 取っても良いコードや、何の機能も持っていない行が……。

 そこは見なかったことにしてください (汗。

Facebook

キーワード【 川俣晶の縁側ソフトウェア技術雑記
【技術雑記】の次のコンテンツ
2006年
03月
10日
実は昔から持っていたメイヤー先生の『オブジェクト指向入門』
3days 0 count
total 7423 count
【技術雑記】の前のコンテンツ
2006年
02月
22日
Nullableに対抗するNeverNullable構造体はC# 2.0で作れるか?
3days 0 count
total 2786 count

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

http://mag.autumn.org/tb.aspx/20060309184357
サイトの表紙【技術雑記】の表紙【技術雑記】のコンテンツ全リスト 【技術雑記】の入手全リスト 【技術雑記】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.34 (Alpha-Test) Copyright (c) 2004-2018 Pie Dey.Co.,Ltd.