libpsdを使ってみる

今作っているプログラムでスプライトをわりと大量に生成している。
その中に元となるpsd画像をレイヤーが重ならないようにpngに変換して改めてパーツの座標をリスト化するというつまらん作業が含まれている。これは自動化しないといかんと思ったので、まずはpsdを読み込む方法の調査を開始した。
さいわいpsd読み込み用にlibpsdと言うのを発見したのでこれでやってみる。

libpsdのビルド

http://libpsd.graphest.com/
ソースゲット。


libpsdには、conifgureとかMakefileとか入っていないので自分で作る。
ということでpremake4の出番であります。

-- A solution contains projects, and defines the available configurations
solution "libpsd"
configurations { "Debug", "Release" }

-- A project defines one build target
project "psd"
kind "SharedLib"
language "C"
files { "src/*.h", "src/*.c" }
includedirs { "include", }
buildoptions { "`pkg-config --cflags libxml-2.0`", }

configuration "Debug"
defines { "DEBUG" }
flags { "Symbols" }
targetdir "debug"

configuration "Release"
defines { "NDEBUG" }
flags { "Optimize" }  
targetdir "release"
$ premake4 gmake
$ make

以上でビルド完了。
なんかやたら警告が出るが一応ビルドはできた。
下のようなコードがあってそれが原因で大量の警告が出るんだが、いいんかこれ。なんかだめな気がするのだが後で考えよう。

case 'enab':

テストプログラム

test.cを改造してみた

test.cpp

#include "libpsd.h"
#include <iostream>
#include <sstream>
#include <cassert>

#define CASE_ENUM_TO_STR(type) case type: return #type;
std::string layer_type_to_string(psd_layer_type type)
{
  switch(type)
  {
    CASE_ENUM_TO_STR(psd_layer_type_normal);
    CASE_ENUM_TO_STR(psd_layer_type_hidden);
    CASE_ENUM_TO_STR(psd_layer_type_folder);
    CASE_ENUM_TO_STR(psd_layer_type_solid_color);
    CASE_ENUM_TO_STR(psd_layer_type_gradient_fill);
    CASE_ENUM_TO_STR(psd_layer_type_pattern_fill);
    CASE_ENUM_TO_STR(psd_layer_type_levels);
    CASE_ENUM_TO_STR(psd_layer_type_curves);
    CASE_ENUM_TO_STR(psd_layer_type_brightness_contrast);
    CASE_ENUM_TO_STR(psd_layer_type_color_balance);
    CASE_ENUM_TO_STR(psd_layer_type_hue_saturation);
    CASE_ENUM_TO_STR(psd_layer_type_selective_color);
    CASE_ENUM_TO_STR(psd_layer_type_threshold);
    CASE_ENUM_TO_STR(psd_layer_type_invert);
    CASE_ENUM_TO_STR(psd_layer_type_posterize);
    CASE_ENUM_TO_STR(psd_layer_type_channel_mixer);
    CASE_ENUM_TO_STR(psd_layer_type_gradient_map);
    CASE_ENUM_TO_STR(psd_layer_type_photo_filter);
  default:
    assert(false);
    return "";
  }
}

std::string layer_to_string(const psd_layer_record &layer)
{
  std::stringstream ss;
  ss
    << " " << layer.left << ", " << layer.top
    << " - " << layer.right << ", " << layer.bottom
    ;
  return ss.str();
}

int main (int argc, char **argv)
{
  if(argc <= 1){
    std::cout << "usage: " << argv[0] << " {psd file}" << std::endl;
    return -1;
  }

  psd_context * context = NULL;
  psd_status status = psd_image_load(&context, argv[1]);

  std::cout << "layer count: " << context->layer_count << std::endl;

  int count=1;
  for(int i=0; i<context->layer_count; ++i){
    psd_layer_record &layer=context->layer_records[i];
    if(layer.layer_type==psd_layer_type_normal){
      std::cout
        << "[" << count++ << "]"
        << layer_to_string(layer) << std::endl
        ;
    }
    else{
      std::cout
        << "type: " << layer_type_to_string(layer.layer_type) << std::endl
        ;
    }
  }

  psd_image_free(context);

  return (status == psd_status_done ? 0 : -1);
}
$ cd test
$ g++ -I../include -L../debug -lpsd -ljpeg -lexif -lxml2 test.cpp
# テスト
$ LD_LIBRARY_PATH=../debug ./a.out sample.psd

通常レイヤー(psd_layer_type_normal)の枚数と一個前のgimp記事のスクリプトのレイヤー数が一致したので問題ない感じ。gimpは通常のレイヤー以外は捨てるみたいだ。
各レイヤーの位置と大きさが取得できたのでもう少しやれば、透明度付きの画像が取れるだろう。psdのままで画像を運用できればわりと労力が節約できそう。