DelphiでPSDファイルを読み込み表示するライブラリを公開

# はじめに
PSDファイルをDelphiでも扱いたいと思いませんか？
そんな人のためにDelphi用ライブラリとサンプルを公開します。
DelphiでPSDファイルを使いたいという人は是非最後まで読みましょう。

# PSDファイルとは
PSDファイルは画像ファイルの一種でAdobe Photoshopなどで採用されています。
特徴としては1つのファイルの中に複数の画像がレイヤーとして管理されています。
これを利用してアニメ調の絵に動きの差分を作っておき、1つのファイルでありながら様々な表情を表示させることが出来ます。
このファイルのライブラリとして様々な言語用のものがありますがDelphiで使えるものがなかったため自作しました。
それがPsdFile.pasを中心とした各種ライブラリです

# PSDライブラリ
```.pas
PsdFile.pas      // PSDファイルを扱う核となるプログラム
FileStreamEx.pas // PSD用に特化したファイル読み込みライブラリ
BitmapEx.pas     // ビットマップをPNGやJPEG、GIF形式でファイルに出力可能なライブラリ
StringListEx.pas // DelphiのTStringListを少し扱いやすくしたライブラリ
StringLib.pas    // 文字列を加工するためのライブラリ
```

これらのファイルがあれば動作します

使い方のサンプルとして
```.pas
PsdFileSample.dpr
PsdFileSample.dproj
```

プロジェクトファイルとユニットを付属していますのでプロジェクトを開くと
非常に簡単なサンプルが起動しますので動作を確かめて下さい

# ライブラリとサンプルの配布
サンプルとライブラリの配布はこちら
https://drive.google.com/uc?id=10o0a417IujxssBm9qeY9t4QrGjAEppNG

# サンプルの説明
サンプルをコンパイルして実行すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637460/7733cb12-ae93-d3bc-bc64-ee1512e47c67.png)

このような画面が表示されるのでメニューからPSDファイルを開きます
サンプル用のPSDを開くと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637460/a3977963-d08f-8748-e11f-4f93d66a68cc.png)
PSDファイルが開きます。
左側がイメージビュアー、右側はレイヤーツリーになります
レイヤーツリーを展開すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637460/e1332d2c-c594-2ccc-9652-02f689beacfb.png)
さらにレイヤーリストが表示されます
白丸になっているレイヤーをクリックすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637460/a521668f-a285-f057-4083-2288769b88a3.png)
クリックしたところに黒丸が移動し左側の表示が変化しました
これはPSDの規格では無くPSDtoolkitで使われている仕様で、レイヤー名の先頭に「*」があると同じ階層のレイヤーのうち1つが表示されると他のレイヤーを非表示にするというものです。
これにより表情差分の指定が楽になるのです。

PSDtoolkitの説明はこちら

https://oov.github.io/aviutl_psdtoolkit/index.html

あとはサンプルとライブラリを使って勝手にやってね、とはいかないので少しずつ説明していきます。
説明不足があればコメントに応じて加筆します。

# サンプルソース
```sample.pas
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls,PsdFileImageFrame,
  Vcl.Menus,PsdFile,PsdFileTreeFrame;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Splitter1: TSplitter;
    MainMenu1: TMainMenu;
    N1: TMenuItem;
    PSD1: TMenuItem;
    OpenDialog1: TOpenDialog;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure PSD1Click(Sender: TObject);
  private
    { Private 宣言 }
    //FPsdFile : TPsdFile;
    FImage : TFramePsdFileImage;
    FTree  : TFramePsdFileTree;
    procedure OnTreeClick(Sender: TObject);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  //FPsdFile := TPsdFile.Create;

  FImage := TFramePsdFileImage.Create(Self);
  FImage.Parent := Panel1;
  FImage.Align := alClient;
  //FImage.PsdFile := FPsdFile;

  FTree := TFramePsdFileTree.Create(Self);
  FTree.Parent := Panel2;
  FTree.Align := alClient;
  FTree.PsdFile := FImage.PsdFile;
  FTree.OnTreeClick := OnTreeClick;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FImage.Free;
  //FPsdFile.Free;
end;

procedure TForm1.OnTreeClick(Sender: TObject);
begin
  FImage.ShowImage();
end;

procedure TForm1.PSD1Click(Sender: TObject);
begin
  if not OpenDialog1.Execute then exit;
  FImage.PsdFile.LoadFromFile(OpenDialog1.FileName);
  FImage.ShowImage();
  FTree.ShowTree();
end;

```

## 起動と破棄
```sample.pas
procedure TForm1.FormCreate(Sender: TObject);
begin

  FImage := TFramePsdFileImage.Create(Self);
  FImage.Parent := Panel1;
  FImage.Align := alClient;

  FTree := TFramePsdFileTree.Create(Self);
  FTree.Parent := Panel2;
  FTree.Align := alClient;
  FTree.PsdFile := FImage.PsdFile;
  FTree.OnTreeClick := OnTreeClick;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FTree.Free;
  FImage.Free;
end;
```

PSDファイルプレビュークラスを生成
PSDレイヤーツリービュークラスを生成
ツリーのPSDファイルクラスはイメージビュアーと同じに設定
ツリーのクリックイベントにイベント割り当て

## 読み込み

```sample.pas
procedure TForm1.PSD1Click(Sender: TObject);
begin
  if not OpenDialog1.Execute then exit;
  FImage.PsdFile.LoadFromFile(OpenDialog1.FileName);
  FImage.ShowImage();
  FTree.ShowTree();
end;
```

## ツリー表示変化イベント
ファイルメニューで開くイベントをきっかけにダイアログを表示
OKが押されたら処理を続行
イメージビュアーとツリービュアーを表示

```sample.pas
procedure TForm1.OnTreeClick(Sender: TObject);
begin
  FImage.ShowImage();
end;
```

サンプルの説明は以上です

やらないといけないこととかは全てライブラリ内で行われるのでほとんどやることがありません。

# PsdFile.pasの説明
プログラムソースを見るとわかりますが膨大なプログラム量なので全てを説明出来ません。
ユーザー向けに公開している部分をなんとか説明していきます。

##  procedure Clear()
レイヤー情報、レイヤーツリー情報を初期化

## function LoadFromFile(const FileName : string) : Boolean
PSDファイルを読み込みます
この段階ではまだメモリ上にデータが存在するだけです
レイヤー情報やツリー情報は読み込まれていますが画像データは読み込んでいません

##  procedure DrawBitmap();overload
内部のビットマップに現在の表示非表示設定で描画します。
##  procedure DrawBitmap(bmp : TBitmapEx);overload
引数に指定したビットマップクラスへ描画します、ほかは上に同じです。
## procedure DrawBitmapThread()
内部ビットマップに描画しますがスレッドを使用して裏で処理されます
終了すると OnDrawFinish イベントが発生します

## procedure VisibleInit()
レイヤーの表示非表示をユーザーで変えたとき、このメソッドを呼ぶと初期状態に戻ります。

## function LayerVisibleSaveToString() : string
現在のレイヤー表示非表示状態を一括で文字列にします、これを保存しておくと下記のメソッドで復元できます。

## procedure LayerVisibleLoadFromString(const str : string)
上記メソッドで文字列化した表示非表示情報を引数として渡すとレイヤーの表示非表示状態が復元できます。

##  property Layers  : TPsdFileLayers read FLayers
PSDファイル内の全てのレイヤー情報がリスト形式で渡されます。
レイヤー情報の順序はPSDファイルの仕様により前後が逆の状態ですがこのまま使用した方が良いです。

## property Trees  : TPsdFileTrees read FTrees
レイヤー情報リストをツリー形式で渡されます。
渡されるレイヤー情報はLayersと同じものですがこちらはツリー構造となっているため再帰法などの処理が必要です。

## property Bitmap : TBitmapEx read FBitmap
内部ビットマップを渡されます。
Delphi標準のビットマップはBMP形式しか対応していないのでTBitmapExとしてLoadFromFile、SavetoFileで各種ファイルに対応しています。

##  property BitmapThumbnail : TBitmapEx read GetBitmapThumbnail
PSDによってはサムネイルがありますのでデフォルトの表示状態であればこっちを使った方が早くなります。サムネイル情報がない場合は nil になります。

## Width,Height : Integer
PSDファイルを読み込んだときにPSDファイル画像の横幅と縦幅が入ります。
PSDは透明も扱うのでその透明部分も含めたサイズになります。
この情報の調整はPSDファイルを作成した本人しかわかりません。

# 注意
プログラムのソースコードはざっと 2,000行あるので見る気も起きないと思いますが、実験部分や将来拡張を予定している部分もあります。

あと完全にPSDファイルの仕様を再現出来ていません、とくに演算関係はほとんど実装していません。
1,118行から20以上の処理が未実装

# 利用規約
基本的な規約は一般的なPSDファイルを扱うときの規約に従って下さい。

このライブラリに関しては何の制限もなく利用が可能です。
商用利用、ソースファイルの再配布、修正、修正したソースの配布：OK
制作者を語ること：NG←これ以外は法的に遵守する事以外全て問題無いと思って下さい。
## それ以外
使用時とかに作者へ連絡は不要です（あればうれしいだけ）
このライブラリを使ってこんなの作りましたよって連絡もらえたら見に行きます。
作者名をどこかに書く必要はありませんがどこかに書ける場合は「VRAMの魔術師」名義で記載してください。
なんたらライセンスとかではないので制作物のプログラムソースの公開も不要です。

## 対応環境
Delphi XE5～Delphi12で動作確認済み
32ビット64ビット両方の環境に対応。

## 最後に
2年と2ヶ月と20日ぐらいかけて作ったライブラリなので大事に使ってもらえるとありがたいです。

## 追記
PSDの透明情報をうまく扱えるようにDelphiと透明度指定を一部変えています。

具体的に言うと **PixelFormat** の設定で **pf32bit** と設定してビットマップに格納していますが、Delphi自身はほとんどpf32bitの設定に対応していないのでCanvasで描画すると思ったように描画されないことに注意して下さい。

これはDelphi2009の時に

https://docwiki.embarcadero.com/Support/ja/Delphi_2009%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%82%B0%E3%83%A9%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E6%A9%9F%E8%83%BD%E3%81%AE%E5%BC%B7%E5%8C%96%E7%82%B9

としてグラフィック機能を強化したとしているのですが実際にはDrawメソッド以外は全く強化されていないという問題があるのです。

この問題とその回避方法を説明するだけで数本の記事になるのでここでは説明しませんが、このライブラリではPSDファイルから情報を失うことが無いようにTBitmapに格納していますが、それを描画する方法が標準のDelphiライブラリには存在していないことに注意して下さい。