HSPで画像の切り取り表示と同時に変形も

HSPで画像の一部だけを表示したいときは基本的には`gcopy`命令ですが、もっと高度な切り取りをするなら**`gsquare`**命令です。長方形以外の切り方ができる上に、同時に様々な変形を施すことができます。ただ、ちょっと使い方がややこしかったのでメモ。

##リファレンスにはどう書いてるの
冒頭部分では

>任意の四角形を描画

>**gsquare p1,p2,p3,p4,p5**

>p1=0～(0) : コピー元のウィンドウID (マイナス値ならば塗りつぶし)
>p2=変数名 : コピー先X座標が格納される数値型配列変数名
>p3=変数名 : コピー先Y座標が格納される数値型配列変数名
>p4=変数名 : コピー元X座標が格納される数値型配列変数名
>p5=変数名 : コピー元Y座標が格納される数値型配列変数名

>解説
>任意の４点を持つ四角形をコピーまたは塗りつぶします。
>p1で、画像コピー元のウィンドウIDを指定します。

>p2,p3にはコピー先の座標４つを配列に格納した変数を指定します。
>p4,p5にはコピー元の座標４つを配列に格納した変数を指定します。
>配列変数には、左上(0),右上(1),右下(2),左下(3) の順番に座標を入れておく必要があります。

とあります。

###どういうことか

この命令では、  

* まずp4, p5の説明より 画像を**四角形であればいかなる形にも切り取ることができます**。
* さらに、p2, p3の説明より 切り取ってできた図形を**変形して描画する**機能まで備わっています。

**かなりいろいろできそうです。**

##つかいかた

1. 切り取り対象の**画像を読み込む**
2. コピー先(描画範囲)の**座標を決める**
3. その座標を**X, Yごとに配列化**しておく
4. コピー元(切り取り範囲)についても同様にする
5. `gSquare`命令で描画する


---

#### 切り取り対象の画像を読み込む

使われる画像は一度別のバッファに読みこまれていなければなりません。

**`celload`**命令  
もしくは  
**`buffer`**命令からの**`picload`**命令  
を使って、画像を読み込みましょう。

```hsp
celload "Origin.png", 1 //ウィンドウIDは適当でおk
```

OR

```hsp
buffer 1 //ウィンドウIDは適当でおk
picload "Origin.png"
```

---

#### コピー先の座標を決める・その座標をX, Yごとに配列化しておく

ややこしいのはここです。  
コピー先の長方形の頂点４つのX座標、Y座標ごとに４要素の配列(座標配列と呼びましょう)を用意して引数として渡さなければならないのですが、

リファレンスには

>配列変数には、左上(0),右上(1),右下(2),左下(3) の順番に座標を入れておく必要があります。

とあります。座標配列の中身は**この順番でないと意図した動きをしません**(違う順番にした時の動きは実例を参照してください)。左上から、時計回りに格納しましょう[^1]。(図を参照)

![指定方法.png](https://qiita-image-store.s3.amazonaws.com/0/67672/6292bfaf-1485-6c37-4e16-81ee00b230e2.png)


[^1]: 特に、コピー先やコピー元が回転のない長方形のときに、(回転のない長方形は対角上の２点を与えれば確定するため)X座標(Y座標)の配列には左右(上下)の位置２個だけを格納していれば良いと考えがちですが、それを実行すると妙なものが描画されてしまいます。この命令はかなり自由度が高く、コピー元もコピー先も四角形なら**どんな図形でも**切り取り変形して描画できるという仕様になっているため、長方形であれ、４つの頂点すべてについて格納しなければなりません*(だから回転のない長方形を切り取るときには***`gcopy`***を使ったほうがいいのです)*。もし各々の座標配列に２つの数値を入れるという仕様なら、*コピー先やコピー元が正方形や長方形・ひし形以外の四角形になることができません*。


また、配列は整数のみです。`ddim`で宣言した配列では落ちます。  


上の図の例でいけば、コードは以下のようになります。

```hsp
dim copyToX, 4 
dim copyToY, 4
//ddim copyToX, 4 //ERROR: 実数不可！アニメーションさせたい等の理由で実数計算をした後はint()で囲うべし

//X座標について設定
copyToX(0) = a //copyToX.0 = aも可
copyToX(1) = c
copyToX(2) = e
copyToX(3) = g

//Y座標について設定
copyToY(0) = b
copyToY(1) = d
copyToY(2) = f
copyToY(3) = h
```

---

#### コピー元も同様にする

座標の与え方はコピー先と同様です。X座標、Y座標ともに配列を用意し格納します。

```
dim copyFromX, 4
dim copyFromY, 4

//後はコピー先の設定と同様
```

ちなみに**コピー元の指定は省略できません**。もしウィンドウ全体を描画の対象にしたい(つまり変形のみ行いたい)ときは、画像を読み込む際に以下のように**`gsel`**命令で描画対象を移動して、

```
gsel 1 //描画対象の画像の入っているウィンドウIDに切り替え

copyFromX(0) = 0
copyFromX(1) = ginfo_winX
copyFromX(2) = ginfo_winX
copyFromX(3) = 0

copyFromY(0) = 0
copyFromY(1) = 0
copyFromY(2) = ginfo_winY
copyFromY(3) = ginfo_winY

gsel 0 //戻すのを忘れずに
```

とやればいいでしょう。

---

#### `gsquare`命令を実行

あとは**`gSquare`**命令を打つだけです。順番を間違えないようにしましょう(`buffer`命令を使用して読み込んだ場合は`gsel 0`を忘れずに)。

```hsp

gsquare 1, copyToX, copyToY, copyFromX, copyFromY

```
ウィンドウ**ID**, コピー**先*****Ｘ***座標、コピー**先*****Ｙ***座標、コピー**元*****Ｘ***座標、コピー**元*****Ｙ***座標 の順番です。  
これで切り取り・変形の加えられた画像が描画されます。描画は、**`gmode`命令の影響を受けます**[^2][^3]。
[^2]: ただしアルファブレンド(`gmode 7`)は効きません。
[^3]: コピー元とコピー先で画面モードが違う(パレットとフルカラー)と落ちるので注意しましょう。


##実例

### 画像を切り取って表示したい

**コピー元の座標を切り取る範囲にしましょう**。コピー先は、その切り取った範囲と全く同じ形の四角形になるように設定します[^4][^5]。
[^4]: 長方形以外の形に切った時でも難しくありません。各頂点を時計回りする際に、前の頂点との座標の差が同じになるようにすればいいのです。
[^5]: 回転のない長方形に切るなら**`gcopy`**命令を使いましょう。その方が楽です。

以下の例では、コピー元から台形状に画像を切り出して描画しています。画像は次のものを使います。名前は`Origin.png`です。

![Origin.png](https://qiita-image-store.s3.amazonaws.com/0/67672/6ec44f71-628f-8d6b-353f-d753c8816ee8.png)


```hsp:imgCrop.hsp
screen 0, 640, 480

cls 4

celload "Origin.png", 1

dim copyToX, 4 
dim copyToY, 4
dim copyFromX, 4
dim copyFromY, 4

//コピー元
//X座標について設定
copyFromX(0) = 20
copyFromX(1) = 130
copyFromX(2) = 130
copyFromX(3) = 20

//Y座標について設定
copyFromY(0) = 60
copyFromY(1) = 40
copyFromY(2) = 110
copyFromY(3) = 90

//コピー先(形が同じになるように注意して)
//X座標について設定
copyToX(0) = 0
copyToX(1) = 110
copyToX(2) = 110
copyToX(3) = 0

//Y座標について設定
copyToY(0) = 20
copyToY(1) = 0
copyToY(2) = 70
copyToY(3) = 50

gsquare 1, copyToX, copyToY, copyFromX, copyFromY
```

![imgCrop.png](https://qiita-image-store.s3.amazonaws.com/0/67672/17e79380-9837-a66c-b989-84248b36221d.png)

---

### 画像を変形したい

**コピー先の座標を変えましょう**。拡大・縮小もできます。  
以下の例では、画像の切り取りは行わず、輪郭が先ほどの例の出力と同じ形になるように画像を変形させています。

```hsp:imgWarp.hsp
screen 0, 640, 480

celload "Origin.png", 1

cls 4

dim copyToX, 4 
dim copyToY, 4
dim copyFromX, 4
dim copyFromY, 4

//コピー元
gsel 1 //描画対象のウィンドウIDを指定する

copyFromX(0) = 0
copyFromX(1) = ginfo_winX
copyFromX(2) = ginfo_winX
copyFromX(3) = 0

copyFromY(0) = 0
copyFromY(1) = 0
copyFromY(2) = ginfo_winY
copyFromY(3) = ginfo_winY

gsel 0 //戻すのを忘れずに

//コピー先
//X座標について設定
copyToX(0) = 0
copyToX(1) = 110
copyToX(2) = 110
copyToX(3) = 0

//Y座標について設定
copyToY(0) = 20
copyToY(1) = 0
copyToY(2) = 70
copyToY(3) = 50

gsquare 1, copyToX, copyToY, copyFromX, copyFromY
```

![imgWarp.png](https://qiita-image-store.s3.amazonaws.com/0/67672/4de53a11-2d52-9bfe-5295-f9cf35c51800.png)

---

### 回転・反転もできちゃう

リファレンスには

>配列変数には、左上(0),右上(1),右下(2),左下(3) の順番に座標を入れておく必要があります。

とありましたが、
この座標配列の中身を、１つずつ前(もしくは後ろ)にシフトするなどしてその順番を狂わせると、画像が**回転・反転されて**描画されます[^6]。

[^6]: 変に狂わせると描画範囲がねじれたり妙なものが描画されたりしますのでご注意を。

```hsp:imgRotate.hsp

screen 0, 640, 480

cls 4

celload "Origin.png", 1

dim copyToX, 4 
dim copyToY, 4
dim copyFromX, 4
dim copyFromY, 4

//コピー元
gsel 1 //描画対象のウィンドウIDを指定する


copyFromX(0) = 0
copyFromX(1) = ginfo_winX
copyFromX(2) = ginfo_winX
copyFromX(3) = 0

copyFromY(0) = 0
copyFromY(1) = 0
copyFromY(2) = ginfo_winY
copyFromY(3) = ginfo_winY

gsel 0 //戻すのを忘れずに

//コピー先
//X座標について設定
copyToX(0) = 150
copyToX(1) = 150
copyToX(2) = 0
copyToX(3) = 0

//Y座標について設定
copyToY(0) = 0
copyToY(1) = 150
copyToY(2) = 150
copyToY(3) = 0

gsquare 1, copyToX, copyToY, copyFromX, copyFromY

```

![imgRotate.png](https://qiita-image-store.s3.amazonaws.com/0/67672/4ed5300e-60ee-704d-3eb1-52bb32b2a279.png)

ただし、**`gSquare`**命令は回転に対してはあまり実用的ではありません。  
回転を行うならば、専用の命令**`gRotate`**命令が存在しています。**この命令は回転のない長方形の切り抜きにも対応している**ため、そういう場合はそっちを使いましょう。もし回転のある長方形や長方形以外を切り取ったあとに回転したい場合は、**`gSquare`**命令のみを使ってゴリゴリやることもできることにはできますが、例えば

1. 別のウィンドウIDを初期化
2. そこに**`gSquare`**で切り取った図形をそのまま描き込む
3. そのウィンドウ全体を**`gRotate`**命令で回転して描画

という処理をしたほうが現実的です。

反転処理に関しては(何も`include`しない場合は)この命令が最適のようです。もし反転処理でもっと簡単な手がありましたらコメント・編集リクエストで指摘お願いします。

---

### **三角形**に切る？！？

切り取り位置が自由に設定できるということは、

>４点のうち２点を同一の点にすれば、**三角形に切り抜けるんじゃね？**

そこで実験してみました。画像は先ほどと同じものを使います。  
うまくいけば、元の画像を右上から対角線で切ったとき下側に来る半分が描画されるはずです。

#### 実験用コード
```hsp:imgCropTri.hsp

screen 0, 640, 480

cls 4

celload "Origin.png", 1

dim copyToX, 4 
dim copyToY, 4
dim copyFromX, 4
dim copyFromY, 4

//コピー元
gsel 1 //描画対象のウィンドウIDを指定する

/* コピー元の座標指定の１番目と２番目が同じ点を示しています。
 * 右下に頂角を持つ直角二等辺三角形になるはず...
 */
copyFromX(0) = 0
copyFromX(1) = 0
copyFromX(2) = ginfo_winX
copyFromX(3) = 0

copyFromY(0) = 0
copyFromY(1) = 0
copyFromY(2) = ginfo_winY
copyFromY(3) = ginfo_winY

gsel 0 //戻すのを忘れずに

//コピー先
//X座標について設定
copyToX(0) = 0
copyToX(1) = 0
copyToX(2) = 150
copyToX(3) = 0

//Y座標について設定
copyToY(0) = 0
copyToY(1) = 0
copyToY(2) = 150
copyToY(3) = 150

gsquare 1, copyToX, copyToY, copyFromX, copyFromY

```

#### 結果

![imgCropTri.png](https://qiita-image-store.s3.amazonaws.com/0/67672/8c4f573b-3354-039e-50f2-d424f97f9520.png)

**完 全 勝 利**

---

## まとめ

* **`gSquare`**命令は、回転のない長方形に限らず、四角形全て・果てには三角形状に任意の画像を切り取ることが可能な命令。
* 画像を切り取るだけでなく、変形処理もできる。切り取ってからの変形(回転・反転を含む)も許されている。
* その自由度から、切り取り範囲・描画範囲はどんな形であれ４つの点すべてについて必ず指定しなければならない。
* 回転のない長方形に切りたいときは**`gcopy`**命令を、回転のない長方形の範囲を回転させて描画したい場合は**`gRotate`**命令を使おう。


---