現在開発中のオリジナル合成ソフトで基本的なトランスフォームをレイヤーごとに操作できるようにするためにアフィン変換を行うtemplate関数を作りました。アフィン変換は簡単な式で実装できるんですが、昔大学の授業で実装して以来だったので正直あまり覚えていなかったです。社会人になって数学に触れる機会がめっきり減ったので危機感を覚えますね。
とにかくアフィン変換部分は無事動き、合成ソフトではレイヤーごとにトランスフォームを編集できるようになりました。
ざっくりですが作った関数はこんな感じです。
struct AffineParam{
float anchor_x, anchor_y;
float translate_x, translate_y;
float scale_x, scale_y;
float rotate_angle;
};
template<typename _Type>
wsp::Result wsp::img::AffineTransform(
const _Type *in_src,
_Type *o_dest,
int width, int height, int num_channels,
const struct wsp::img::AffineParam ¶m
)
{
if( width<1 || height<1 || num_channels<1 )
{
return wsp::ResultInvalidArgument();
}
int wstep = width*num_channels;
memset( o_dest, 0, sizeof(_Type)*wstep*height );
if( param.scale_x==0.0f || param.scale_y==0.0f )
{
return wsp::ResultSuccess();
}
int x, y;
float centered_x, centered_y;
int trans_x, trans_y;
float rotate_rad = param.rotate_angle * PI / 180.0f;
float sx_mult = 1.0f / param.scale_x;
float sy_mult = 1.0f / param.scale_y;
float tx = param.translate_x;
float ty = param.translate_y;
float ax = param.anchor_x;
float ay = param.anchor_y;
_Type *dst_ptr = o_dest;
const _Type *src_ptr, *end_ptr;
for( y=0; y<height; ++y )
{
for( x=0; x<width; ++x )
{
centered_x = x - ax;
centered_y = y - ay;
trans_x = (int)( (centered_x * cos(rotate_rad) - centered_y * sin(rotate_rad)) * sx_mult - tx + ax );
trans_y = (int)( (centered_x * sin(rotate_rad) + centered_y * cos(rotate_rad)) * sy_mult - ty + ay );
if( trans_y<0 || trans_y>=height || trans_x<0 || trans_x>=width )
{
dst_ptr += num_channels;
continue;
}
src_ptr = in_src + trans_y * wstep + trans_x * num_channels;
end_ptr = src_ptr + num_channels;
for( ; src_ptr!=end_ptr; ++src_ptr, ++dst_ptr )
{
*dst_ptr = *src_ptr;
}
}
}
return wsp::ResultSuccess();
}
次はキーフレームを各パラメータに打てるようにすることなんですが、こちらは中身よりGUIの方が実装が大変そうです。まぁ気長にやっていこうと思います。