導入
PHPの標準画像処理ライブラリであるGDには、標準で楕円を描画する imageellipse() 関数が存在します。しかし、この関数はアンチエイリアス(縁のギザギザを滑らかにする処理)に対応しておらず、拡大したり斜めの線を引いたりすると非常に粗い見た目になってしまいます。本記事では、数学的なアルゴリズムを応用し、GD環境下でも高品質な滑らかな楕円を描画する手法を解説します。
基礎知識
アンチエイリアスとは、ピクセルの境界線を周囲の色と混色させることで、視覚的に滑らかに見せる技術です。今回利用する「Xiaolin Wu(シャオリン・ウー)のアルゴリズム」は、本来は直線の描画に用いられる手法ですが、これを楕円に応用することで、ピクセル単位で不透明度を計算し、境界をぼかすことが可能になります。
実装/解決策
単純な楕円描画とは異なり、Xiaolin Wuのアルゴリズムでは、計算された誤差(ピクセルの中間位置にあるか否か)に基づいて、隣接するピクセルのアルファ値(透明度)を動的に変更します。これにより、ハードエッジを排除した滑らかな曲線を実現します。
サンプルプログラム
以下のコードは、指定した中心座標とサイズで、アンチエイリアスのかかった楕円を描画する関数です。
/
function wu_ellipse($image, $cx, $cy, $width, $height, $color) {
$rx = $width / 2;
$ry = $height / 2;
$rx2 = $rx $rx;
$ry2 = $ry $ry;
// 楕円を4つの象限に分割して描画
// 水平方向の描画ループ
$quarter = round($rx2 / sqrt($rx2 + $ry2));
for ($x = 0; $x <= $quarter; $x++) {
$y = $ry sqrt(1 - $x $x / $rx2);
$error = $y - floor($y); // 小数点以下の誤差を計算
$transparency = round($error 0x7F);
// アルファ値を計算して描画
$alpha = $color | ($transparency << 24);
$alpha2 = $color | ((0x7F - $transparency) << 24);
plot4points($image, $cx, $cy, $x, floor($y), $alpha);
plot4points($image, $cx, $cy, $x, floor($y) + 1, $alpha2);
}
// 垂直方向の描画ループ
$quarter = round($ry2 / sqrt($rx2 + $ry2));
for ($y = 0; $y <= $quarter; $y++) {
$x = $rx sqrt(1 - $y $y / $ry2);
$error = $x - floor($x);
$transparency = round($error 0x7F);
$alpha = $color | ($transparency << 24);
$alpha2 = $color | ((0x7F - $transparency) << 24);
plot4points($image, $cx, $cy, floor($x), $y, $alpha);
plot4points($image, $cx, $cy, floor($x) + 1, $y, $alpha2);
}
}
// 4点対称にピクセルを描画する補助関数
function plot4points($image, $cx, $cy, $dx, $dy, $color) {
imagesetpixel($image, $cx + $dx, $cy + $dy, $color);
imagesetpixel($image, $cx - $dx, $cy + $dy, $color);
imagesetpixel($image, $cx + $dx, $cy - $dy, $color);
imagesetpixel($image, $cx - $dx, $cy - $dy, $color);
}
// 画像の生成と実行
$image = imagecreatetruecolor(300, 300);
$color = imagecolorallocate($image, 51, 51, 51); // 描画色
$bg = imagecolorallocate($image, 255, 255, 255); // 背景色
imagefill($image, 0, 0, $bg);
wu_ellipse($image, 150, 150, 240, 240, $color);
header("Content-type: image/png");
imagepng($image);
imagedestroy($image);
?>
応用・注意点
パフォーマンスの注意: この手法は標準の imageellipse() よりも計算コストが高くなります。大量の楕円を一度に描画するような処理(例えば動的なチャート生成など)では、描画後の画像をキャッシュして再利用する設計を推奨します。また、アルファチャンネルを扱うため、必ず imagecreatetruecolor() で作成した画像リソースを使用してください。パレットベースの画像では正しくアンチエイリアスがかからない場合があります。