AFsoft WebSite(エーエフソフト・ウェブサイト)
 

オペレーティング・システムについて

プログラミングについて
ホームページについて
キャドについて
電子カタログについて
書籍・雑誌
イベント
リンク集
CADを考える:画像(ラスタ)(1)
前回は、独自拡張のハッチング(画像塗り)について考えました。今回は、画像(ラスタ)要素データについて考えます。
 
SXF仕様では、Ver.2.0でモノクロTIFF(G4)1枚のみの画像を貼り付ける事が出来るようになりましたが、Ver.3.1では、カラーJPEGファイルも指定出来るようになり、複数枚の画像を貼り付ける事が出来るようになっています。
SXF Ver.3.1仕様書・同解説附属書共通属性セット編.pdf」(p.9〜)では、下記のように記載されています。
3-4 属性付加機構の適用
(1)属性を付与するフィーチャ
 1)画像フィーチャを定義する場合
  以下の条件で自動的に生成した1つの折線フィーチャに属性を付与する。
  ・画像データの存在範囲を示す長方形を、「用紙上」に時計回りで生成する。
  ・画像データの配置基点は「左下」固定とし、存在範囲を示す長方形(折線フィーチャ)を以下の通りに解釈して画像データが表示されることに留意して折線フィーチャの各点を指定する。
   画像データの配置点:折線の第1点目
   配置角度:折線の第4点目と、第5点目を結ぶ線の、
        水平に対する角度(反時計回り)。
   横方向倍率:画像データの水平方向サイズと、折線の第4点面
        と第5点目を結ぶ直線の長さの比率
   縦方向倍率:画像データの垂直方向サイズと、折線の第1点面
        と第2点目を結ぶ直線の長さの比率
という訳で、画像は用紙上に作図する、つまり、部分図には作図されない、と解釈して構わないでしょう。また、Ver.2.0の頃は、用紙上に作図グループを用意し、そこに折線1つだけを定義し、階層化しない、とありますので、Ver.3.1でも、階層化しない=作図グループや作図部品には作図されない、と解釈して構わないと思われます。つまり何がいいたいのかというと、部分図や作図部品によって歪んだ折線枠内に画像を描画するというような事は有り得ない、長方形(縦横倍)→回転 だけを考えれば良い、という事です。
 
GDIでの画像描画は、BMP・ICON・WMF・EMFの読み込みと、それをそのまま描画する手法(Draw)と縦横倍率変形をして描画する手法(StretchDraw)はありますが、回転描画を行う手法はありません。例えば、ドット単位で読み取ってドットを塗り潰し四角として描画させる事で描画する事は出来ますが、描画速度はかなり遅くなってしまいます。画像サイズにも寄りますが、最低でも数十秒以上掛かるでしょう。それでは使い物になりません。また、他画像ライブラリを利用すれば可能かもしれません。けれどもここではなるべく「素」の状態で作る事を主眼としていますので、そういったミドルウェアは使用しない事にします。
 
という訳で、塗り潰し同様、画像描画についても、GDIを指定して描画する際もGDI+を利用して画像描画を行う事にします。GDI+、Direct2Dでは比較的容易に画像の回転描画を実現する事が出来ます。
 
まずは単純に、GDI+での画像描画の方法は以下のようになります。
var
 im1 : TGPImage;
begin
 im1 := TGPImage.Create(fn);  // fn:ファイル名
 gra.DrawImage(im1,0,0);
 im1.Free ;
これを拡大縮小描画をしたい場合は
var
 im1 : TGPImage;
 dst : TGPRectF ;
 px,py : Integer ;
begin
 im1 := TGPImage.Create(fn);  // fn:ファイル名
 dst := MakeRect(0,0, px*2,py*2);
 gra.DrawImage(im1,dst);
 im1.Free ;
のようにすれば、画像左上が(0,0)位置に、縦横2倍に描画されます。次に回転ですが、GDI+には画像の3点指定による描画で回転も変形も実現出来るのですが、おそらくDirect2Dには無いと思いますので、Direct2Dと同様に変換行列的に行うとすると、以下のようになります。変換行列を使えば出力先の領域指定による拡大縮小の指定も一緒にしてしまえば不要になるので省けます。
var
 im1 : TGPImage;
 mt1 : TGPMatrix ;
begin
 im1 := TGPImage.Create(fn);
 mt1 := TGPMatrix.Create ;
 mt1.Translate(100.0,100.0);
 mt1.Rotate(-30.0);
 mt1.Scale(2.0,1.5);
 gra.SetTransform(mt1);
 mt1.Free ;
 gra.DrawImage(im1,0.0,0.0);
 im1.Free ;
 gra.ResetTransform ;
これで、画像左上が(100,100)位置に、横2倍・縦1.5倍、回転30°(時計回り指定のためマイナス値にしています)の画像描画が出来るようになります。
 
次に、Direct2D です。
var
 im2 : TPicture ;
begin
 im2 := TPicture.Create ;
 im2.LoadFromFile(fn) ;
 D2DCanvas.Draw(0,0,im2.Graphic);
 im2.Free ;
という感じです。RenderTargetにも DrawBitmapメソッドはありますが、引数渡しでBitmap経由等をしないといけないみたいで手間ですからパスします。これを踏まえた上で次に拡大・縮小・回転を考慮すると下記のような感じです。
var
 im2 : TPicture ;
 mt2 : D2D_MATRIX_3X2_F ;
begin
 im2 := TPicture.Create ;
 im2.LoadFromFile(fn) ;
 mt2._11 := 2.0*Cos(-30.0/180.0*Pi) ;
 mt2._12 := 2.0*Sin(-30.0/180.0*Pi) ;
 mt2._21 :=-1.5*Sin(-30.0/180.0*Pi) ;
 mt2._22 := 1.5*Cos(-30.0/180.0*Pi) ;
 mt2._31 := 100.0 ;
 mt2._32 := 100.0 ;
 D2DCanvas.RenderTarget.SetTransform(mt2);
 D2DCanvas.Draw(0,0,im2.Graphic);
 im2.Free ;
 mt2._11 := 1.0 ;
 mt2._12 := 0.0 ;
 mt2._21 := 0.0 ;
 mt2._22 := 1.0 ;
 mt2._31 := 0.0 ;
 mt2._32 := 0.0 ;
 D2DCanvas.RenderTarget.SetTransform(mt2);
これで、画像左上が(100,100)位置に、横2倍・縦1.5倍、回転30°(時計回り指定のためマイナス値にしています)の画像描画が出来るようになります。TMatrixで変換行列を作ってから mt2 に受け渡してもいいのですが(その方がわかりやすいですが)この程度なら直接書いても分かるでしょう。
 
これで画像の描画が出来る事は分かりましたが、上記では、画像左上点が基準となっていますので、画像左下点を基準としたいので、計算をして補正を行うようにします。
 
var
 dx,dy : double ;
 im1 : TGPImage;
 mt1 : TGPMatrix ;
begin
 im1 := TGPImage.Create(fn);
 dx := 1.5*im1.GetHeight*Cos(-(30.0+90.0)/180.0*Pi);
 dy := 1.5*im1.GetHeight*Sin(-(30.0+90.0)/180.0*Pi);
 mt1 := TGPMatrix.Create ;
 mt1.Translate(100.0+dx,100.0+dy);
 mt1.Rotate(-30.0);
 mt1.Scale(2.0,1.5);
 gra.SetTransform(mt1);
 mt1.Free ;
 gra.DrawImage(im1,0.0,0.0);
 im1.Free ;
 gra.ResetTransform ;
var
 dx,dy : double ;
 im2 : TPicture ;
 mt2 : D2D_MATRIX_3X2_F ;
begin
 im2 := TPicture.Create ;
 im2.LoadFromFile(fn) ;
 dx := 1.5*im2.Height*Cos(-(30.0+90.0)/180.0*Pi);
 dy := 1.5*im2.Height*Sin(-(30.0+90.0)/180.0*Pi);
 mt2._11 := 2.0*Cos(-30.0/180.0*Pi) ;
 mt2._12 := 2.0*Sin(-30.0/180.0*Pi) ;
 mt2._21 :=-1.5*Sin(-30.0/180.0*Pi) ;
 mt2._22 := 1.5*Cos(-30.0/180.0*Pi) ;
 mt2._31 := 100.0+dx ;
 mt2._32 := 100.0+dy ;
 D2DCanvas.RenderTarget.SetTransform(mt2);
 D2DCanvas.Draw(0,0,im2.Graphic);
 im2.Free ;
 mt2._11 := 1.0 ;
 mt2._12 := 0.0 ;
 mt2._21 := 0.0 ;
 mt2._22 := 1.0 ;
 mt2._31 := 0.0 ;
 mt2._32 := 0.0 ;
 D2DCanvas.RenderTarget.SetTransform(mt2);
という感じになります。
 
これをまとめて、例外処理を付けて、引数を付けそれに対応するようにした描画用手続き G_Image を UnitGraph.pas に用意しておきます。
 
 
それでは、画像データ構造は下記のようにします。
UnitData.pas
type
 ・・・
 TDataImage = record     // 独自|表記要素|画像
  exf : Boolean ;      // 存在フラグ(True:有り False:無し)
  Layer : Integer ;     // レイヤ(1〜256)
  start_x : double ;     // 配置点X座標(画像左下点)
  start_y : double ;     // 配置点Y座標
  size_x : double ;     // X方向サイズ[mm]
  size_y : double ;     // Y方向サイズ[mm]
  Angle  : double ;     // 回転角[°]
  ImgFileName : string ;   // 画像ファイル名
 end;
 ・・・
X方向サイズ・Y方向サイズの指定で、それにちょうど納まるように画像をリサイズする事とします。そうなると、画像縦横比率を1:1にしたい場合に困る事になりますので、画像の縦横サイズを取得する手続き G_ImageSize をUnitGraph.pasに作成しておく事にします。
 
これは表記要素で、幾何要素にはなりませんから、それを考慮して変数宣言しておきます。シンボル図形にも複合図形(部分図や作図部品)にもなりません。データ追加用関数は下記のようにします。 
UnitData.pas
function AddDataImage(fn:string;lay:integer;
 px,py,sx,sy,an:double) : Boolean;
 
この関数内では、画像ファイル名のチェックは、空でないかどうかだけにしておきます。実際の描画の際に、画像ファイルがあるかどうか、拡張子が正しいかどうか、のチェックを行って、正しければ画像を描画、を行うようにしています。なお、画像ファイル形式は、ハッチング(画像塗り)と同様
  ICO、WMF、EMF、BMP、GIF、PNG、TIFF、JPEG
です。
SXF仕様では、モノクロG4-TIFF、又は、JPEGのみ、となっていますが、SXFでの保存を行うときに画像変換を行えばいいだろうという程度に考えておきます。
 
画像データの描画は、恒例のように UnitDataGraph.pasにて行います。部分図での描画はありませんので変換行列での座標変換はありません。
UnitDataGraph.pas
// 画像の表示
procedure DisplayImage(lay:integer;px,py,sx,sy,an:double;
 fn:string);
var
 ex : string ;
 x1,y1,x2,y2,x3,y3,x4,y4 : double ;
 h,w: integer ;
begin
 ex := AnsiLowerCase(ExtractFileExt(fn));
 if not(FileExists(fn))
  or((ex <> '.ico')and(ex <> '.bmp')and(ex <> '.wmf')
  and(ex <> '.emf')and(ex <> '.jpg')and(ex <> '.jpeg')
  and(ex <> '.png')and(ex <> '.gif')
  and(ex <> '.tif')and(ex <> '.tiff')) then
  exit;
 
 // クリッピングチェック
 x1 := px;
 y1 := py;
 x2 := px + sx*Cos(an/180.0*Pi);
 y2 := py + sx*Sin(an/180.0*Pi);
 x3 := x2 + sy*Cos((an+90.0)/180.0*Pi);
 y3 := y2 + sy*Sin((an+90.0)/180.0*Pi);
 x4 := px + sy*Cos((an+90.0)/180.0*Pi);
 y4 := py + sy*Sin((an+90.0)/180.0*Pi);
 if (Clip4Point(x1,y1,x2,y2,x3,y3,x4,y4, WndX1,WndY1,WndX2,WndY2) = 2) then exit;
 
 x1 := (x1 - WndX1)*mm_dot ;
 y1 := -(y1 - WndY2)*mm_dot ;
 gp.G_ImageSize(h,w,fn);
 if (h = 0)or(w = 0) then exit ;
 x2 := sx*mm_dot / w ;
 y2 := sy*mm_dot / h ;
 gp.G_Image(x1,y1,x2,y2,an,fn);
end;
 
 
それでは、画像の登録・描画のテストです。
Unit1.pas
// 画像
fn := AppPath+'test.jpg' ;
CData.AddDataImage(fn, 1, 20.0,200.0, 50.0,50.0, 30.0);
// 縦横比率を1:1にしたい場合
gp.G_ImageSize(y1,x1,fn);
CData.AddDataImage(fn, 1, 60.0,200.0, 50.0,50.0*y1/x1, 15.0);


 
それでは、ここまでのテストプログラムです。実行ファイル、gdiplus.dll、gdipフォルダは入っていません。ソースのみです。
 
CAD装置(1)
CAD装置(2)
メディア
AutoCADの
DIESELマクロ
CSV
DXF
PCES
IGES
STEP
数学とCAD
CAD作ろ!
CADを考える
 ▲PREV
 ▼NEXT
M7
Jw_cad
 
お問い合わせ 
本サイトはリンクフリーです
リンクバナー
(C)Copyright 1999-2015 By AFsoft All Rights Reserved.