無料画像編集ソフト開発記 第2回:GitHubのKonvaを読み、明るさ・コントラストを直す
明るさ・コントラストの開発は、GitHubにあるKonvaのフィルター実装を読むことから始まった。AIとPhotoshot用の計算を書いたが、スライダーが動くだけでは結果は合わない。参考コード、独自実装、測定後の修正という三段階を記録する。
出典照合 · 2026年10月16日改訂
記事を読む ↓

まずKonvaのContrast.tsを開いた
2026年9月18日の開発記録には、konvajs/konvaのsrc/filters/Contrast.tsを実際に開いた履歴がある。RGBを正規化し、中間値を基準にコントラストを変える処理を読めた。
確認できるのはソースの参照と別実装であり、Konvaのフィルターをそのまま導入・複製したという記録ではない。この区別を残しておく。
AIと書いた最初の計算
下は互換性のためv0.16.0に残る旧計算だ。先に明るさを適用し、0.5を中心にコントラストを変える。Konvaで中間値基準の処理を読んだが、ここに示す明るさと係数の式はPhotoshot側の実装である。
参考コードだけで調整レイヤーは完成しない。値の保存、マスク、不透明度、再描画をつなぐ処理も必要だった。
if (t === 'brightness' && a.revision !== 2) {
x = v.brightness < 0 ? x * (1 + v.brightness / 100) : x + ((1 - x) * v.brightness) / 100;
const contrast = v.contrast < 0 ? 1 + v.contrast / 100 : 1 / (1 - v.contrast / 101);
x = (x - 0.5) * contrast + 0.5;
}比較結果から計算を変更した
9月25日にはPhotoshop 27.3.1のテストチャート出力と比較した。明るさ+30、コントラスト+20で、その比較時点の中間実装はチャンネル値の平均差が約11.505、最大25だった。9月18日の初版そのものを直接測定した値ではない。
修正後の通常モードはsampleResponseで測定済みの応答データを読む。下の二行で明るさ、コントラストの順に適用する。参照した式を出発点に、実際の比較結果へ合わせてコードを変えた。
x = sampleResponse(toneResponse.brightness, v.brightness, -150, x);
x = sampleResponse(toneResponse.contrast, v.contrast, -50, x);256要素の表にして再利用する
各チャンネルの8ビット入力は256通りある。全画素で同じ複雑な計算を繰り返す代わりに、compileAdjustmentで対応表を作り再利用する。下はPhotoshotのLUT生成部分だ。
明るさをこの共通経路へ組み込むことで、後のレベル、カーブ、露光量も同じ構造を使えるようになった。計算式だけでなく、再利用できる形へ接続する実装が必要だった。
const tables = ['r', 'g', 'b'].map((channel) =>
Float64Array.from({ length: 256 }, (_, i) => {
let x = i / 255;写真例と数値検証を分ける
写真例の設定は明るさ18、コントラスト12で、数値比較の+30/+20とは異なる。元の人物画像はAI生成の例示素材、画面と補正結果は実際の編集機から取得したものだ。
記録された+30/+20のテストでは修正後の平均差は約0.241、最大1になった。順番をコントラスト→明るさへ逆にすると約1.150、最大4。適用順序も結果に影響した。
GitHub参照から独自コードの検証へ
KonvaのContrast.tsは参照した外部コード。adjustments.tsと応答データの接続はAIと作成・修正したPhotoshotのコードだ。公開実装を探す作業と、自分たちの構造へ組み込んで検証する作業が並行して進んだ。
数値は特定のチャートと設定で得た過去の比較結果であり、あらゆる写真やモードでPhotoshopと一致する意味ではない。次回は共通経路へレベルとヒストグラムを接続する。
