無料画像編集ソフト開発記 第4回:GitHubのglfx.jsから考えたカーブ実装
カーブはGitHubのglfx.jsを読み、制御点の間を256要素の表にする考え方を参考にした。Photoshotの補間と編集機への接続はAIと別に実装。その後、比較結果に基づいて計算を変更しながら旧プロジェクトの見え方も守った。
出典照合 · 2026年10月16日改訂
記事を読む ↓

glfx.jsで読んだのは処理の構造
evanw/glfx.jsのsrc/filters/adjust/curves.jsは制御点から補間値を256個用意し、チャンネルごとの参照に使う。元の開発記録にも、このLUTの考え方を検討したと残っている。
WebGLフィルター全体を取り込んだのではない。Photoshotは補間関数を別に書いてcompileAdjustmentへ接続した。参考にした構造と、独自に選んだ計算法を区別する。
最初は形状保持Hermite補間
最初のcurveTableは形状保持Hermite補間だった。下は各点の接線を決める部分で、前後の傾きの符号が変わる場合は0にし、急な編集付近での不要な突出を抑える。
glfx.jsの補間関数を複製したものではない。事前計算の構造を参考にし、数式とTypeScript実装は別に用意した。
const m = points.map((_, i) =>
i === 0
? d[0]
: i === n - 1
? d[n - 2]
: d[i - 1] * d[i] <= 0
? 0
: 2 / (1 / d[i - 1] + 1 / d[i]),
);比較後に自然3次スプラインを追加
制御点が同じでも、点の間のつなぎ方で中間調が変わる。比較後の新しい経路にはnaturalCurveTableを追加した。下はスプラインの曲率を求めるための前進計算部分だ。
「AIがカーブ機能を作った」だけでは、参考にした構造を独自実装へ落とし込み、結果を比較して補間法を選び直した過程が見えない。
for (let i = 1; i < n - 1; i++) {
const span = points[i + 1].x - points[i - 1].x,
sig = (points[i].x - points[i - 1].x) / span,
p = sig * y2[i - 1] + 2;
y2[i] = (sig - 1) / p;
const slope =
(points[i + 1].y - points[i].y) / (points[i + 1].x - points[i].x) -
(points[i].y - points[i - 1].y) / (points[i].x - points[i - 1].x);
u[i] = ((6 * slope) / span - sig * u[i - 1]) / p;
}
for (let i = n - 2; i >= 0; i--) y2[i] = y2[i] * y2[i + 1] + u[i];画面と画素で同じ版のカーブを使う
新計算をすべての保存ファイルへ適用すると、以前の作品の色が変わりかねない。revision 2は自然スプライン、旧調整はHermiteを維持する。編集画面の表も同じ条件で選ぶ。
画素処理では個別チャンネルの表を先に読み、共通RGB表を適用する。小数位置は隣り合う二つの値で補間する。LUTの考え方をPhotoshotのチャンネル順序と互換性ルールへ組み込んだ部分だ。
const table = (value.revision === 2 ? naturalCurveTable : curveTable)(points),if (t === 'curves') {
x = ct![channel as Channel][i];
const f = clamp(x) * 255,
lo = Math.floor(f),
hi = Math.min(255, lo + 1);
x = ct!.rgb[lo] + (ct!.rgb[hi] - ct!.rgb[lo]) * (f - lo);
}同じ点でも結果が違う問題を検証
写真例のRGB点は(0,0)、(64,80)、(192,176)、(255,255)。人物原画像はAI生成素材で、画面と結果は実際のPhotoshotによるものだ。
過去の比較チャートは(0,0)、(64,48)、(192,208)、(255,255)という別設定を使った。その記録では修正後の平均差・最大差とも0。ただし、その入力での一致であり、すべてのカーブや写真の一致を保証しない。
参考の考え方を製品のルールへ合わせる
glfx.jsはLUTを用いたカーブを理解する具体的な出発点だった。Hermite・自然スプライン関数、編集機との接続、旧データ互換処理はAIと別に作成・修正した。
公開コードを読む価値は完成機能の取り込みだけでなく、設計を理解して自分たちのレイヤー構造へつなぐことにもある。次回は露光量とバージョン互換処理へ進む。
