Edited at

psd.c を公開

More than 5 years have passed since last update.

psd.rb をベースに C 言語で移植した psd.c を MIT ライセンスの元で公開しました。ここでは使い方を書いておきます。


psd.rb との違い


  • Photoshop Big 形式 (psb) に対応

  • Unicode 文字列は生で格納しているため変換が必要

  • 木構造のクエリは未対応

  • レンダリングに対応していない


インストール

clib がインストールされていれば package.json に dependenciespsd.c の記述をすることで clib-install を実行後に deps ディレクトリにインストールされます。

{

"dependencies": {
"hkrn/psd.c": "0.1.0"
}
}


使い方の例

ここでは2つの使い方の例を示します。psd.c はここで紹介されている関数の他にも API として定義されています。


psd 自体に格納されている全体の画像を出力する

以下は psd の中身を読み込んで stbi_image_write を使って png として出力します。

簡略化のためエラー処理は省いていますが、psdDocument(Parse|ExtractImage) は返り値に psd_status_t を返すため、この値が PSD_STATUS_SUCCESS かどうかを比較して正常に処理されたかどうかを確認する必要があります。

psd_uint8_t *bytes = ...; /* psd の生データ */

psd_rsize_t length = ...; /* psd の生データの長さ */
psd_buffer_t *buffer = psdBufferCreate(bytes, length);
psd_document_t *document = psdDocumentCreate();
psdDocumentParse(document, buffer);
psdDocumentExtractImage(document);
const psd_header_t *header = psdDocumentGetHeader(document);
psd_size_t size = psdHeaderGetSize(header);
psd_rsize_t ncomponents = 4;
if (const psd_uint8_t *pixel_data = psdImageGetPixelData(psdDocumentGetImage(document))) {
psd_rsize stride = psdHeaderGetStrideLength(header) * ncomponents;
stbi_write_png("test.png", size.columns, size.rows, ncomponents, pixel_data, stride);
}
psdDocumentDestroy(document);
psdBufferDestroy(buffer);

psdImageGetPixelData は psdDocumentExtractImage を呼び出さないと NULL を返します。これは PSD 自体のパースと画像のパースが別処理になっているためです。


psd に格納されているレイヤー毎の画像を出力する

レイヤー毎に格納されている画像データを stb_image_write を使って png として出力します。

psd_uint8_t *bytes = ...; /* psd の生データ */

psd_rsize_t length = ...; /* psd の生データの長さ */
psd_buffer_t *buffer = psdBufferCreate(bytes, length);
psd_document_t *document = psdDocumentCreate();
psdDocumentParse(document, buffer);
psd_rsize_t layer_length;
psd_layer_t *const *layers = psdDocumentGetAllLayers(document, &layer_length);
for (int i = 0; i < layer_length; i++) {
psd_layer_t *layer = layers[i];
psdLayerExtract(layer);
if (const psd_uint8_t *data = psdLayerGetPixelData(layer)) {
const psd_rect_t rect = psdLayerGetRect(layer);
psd_size_t size;
size.columns = psdRectGetWidth(&rect);
size.rows = psdRectGetHeight(&rect);
psd_rsize_t ncomponents = 4;
psd_rsize_t depth = psdHeaderGetDepth(psdDocumentGetHeader(document));
psd_rsize_t stride = psdSizeGetStrideLength(&size, depth) * ncomponents;
char buf[255];
snprintf(buf, sizeof(buf), "%d.png", i);
stbi_write_png(buf, size.columns, size.rows, ncomponents, data, stride);
}
}
free((void *)layers);

psdDocumentExtractImage と同じく psdLayerExtract を呼び出さないと psdLayerGetPixelData が NULL を返します。また、psdDocumentGetAllLayers が返す配列のポインタは使用後 free (カスタムアロケータで設定した場合はその free) で解放する必要があります。


設計

ここでは psd.c の設計について書きます


構造体

psd.c ではおおまかに以下の構造体の単位でグルーピングされています。psd_document が全ての親であり、メモリ管理は配列のポインタを返す関数を除いて psd_document_t が担っています。


  • psd_document_t


    • psd_header_t

    • psd_resource_container_t


      • psd_resource_t



    • psd_layer_container_t


      • psd_layer_t



    • psd_image_t



上記の構造体は全て不定構造体であり、直接構造体を作成することが出来ません。接尾詞が Create の関数を介して作成する必要があります。


psd.c は移植性の観点から int 以外は独自の型を定義し、それらを用いています。以下は C99 の場合です。

typedef _Bool psd_bool_t;

typedef uint8_t psd_uint8_t;
typedef int16_t psd_int16_t;
typedef uint16_t psd_uint16_t;
typedef int32_t psd_int32_t;
typedef uint32_t psd_uint32_t;
typedef int64_t psd_int64_t;
typedef uint64_t psd_uint64_t;
#define psd_false false
#define psd_true true


カスタムアロケータ

psd.c はカスタムアロケータを設定することが出来ます。以下のコードはカスタムアロケータの設定方法を示します。

static psd_global_allocator_t g_allocator = {

customMalloc,
customCalloc,
customRealloc,
customFree
};
/* 元に戻す場合は psdGlobalSetCustomAllocator(0); を呼び出すこと */
psdGlobalSetCustomAllocator(&g_allocator);

接頭詞 psd のつく関数の呼び出し前に設定する必要があります。デフォルトでは OS 標準の malloc/calloc/realloc/free をそのまま呼び出します。