
無料Photoshop代替の開発記
短い式にも接続が要る
反転は1−xで表せます。既知の式から編集機能にするには、原画像の保持、マスク、不透明度、合成、設定保存の接続が必要でした。
AI生成の動作例用写真。編集画面と出力画像は実際のPhotoshotで作成しました。
ソースから始める。
短い式の先にある開発
0〜1の成分xを1−xに置き換えます。この数学をAIが発明したのではなく、既存の変換表と文書構造に接続しました。
明るさ、レベル、カーブで使う256入力の変換表経路を再利用します。保存と合成を他の調整と同じ規則に保てます。
RGBとアルファを分ける
8ビットRGBなら0は255、255は0、64は191になります。各色成分に同じ式を適用します。色の反転はマスク反転やアルファを1−アルファにする処理とは別です。
共通処理は完全に透明な画素を飛ばし、元のアルファを保持します。透明背景が色の反転によって不透明になることを避けます。
L550–554Photoshotの実装コード ↗
return (r, g, b, out) => {
out[0] = tables[0][r];
out[1] = tables[1][g];
out[2] = tables[2][b];
};計算の
内側へ。
同じ反転を二度適用する
理想の式を二度適用すれば1−(1−x)=xです。ただし実際の文書では、不透明度、マスク、途中の処理も条件に含まれます。
明るさを変えてから反転する結果と、反転してから明るさを変える結果は必ずしも同じではありません。短い数式だけではレイヤー順序の影響を説明できません。
マスクと合成は共通の経路
計算した色を不透明度、マスク、描画モードとともに共通合成器へ渡します。原画像を残し、設定を保存して再編集できます。
本日16種の既存検査で恒等条件、アルファ、プロジェクト保存往復、合成経路の一致を確認しました。これは保管済み写真の作例とは別であり、新たなマスク写真を撮影したという主張ではありません。
コードを読む。
L539–547Photoshotの実装コード ↗
? clamp(x ** 2.2 * 2 ** v.ev + v.offset) ** (1 / (2.2 * v.gamma))
: srgb(clamp(linear(x) * 2 ** v.ev + v.offset) ** (1 / v.gamma));
if (t === 'invert') x = 1 - x;
if (t === 'posterize')
x =
a.revision === 2
? Math.min(v.steps - 1, Math.floor((i * v.steps) / 256)) / (v.steps - 1)
: Math.round(x * (v.steps - 1)) / (v.steps - 1);
return clamp(x);L836–853Photoshotの実装コード ↗
beforeRGB: RGB = [0, 0, 0],
mixed: RGB = [0, 0, 0];
for (let i = start; i < end; i += 4) {
if (!data[i + 3]) continue;
let amount = opacity * (mask ? mask[i + 3] / 255 : 1);
if (blend === 'dissolve') amount = pixelNoise(i / 4) < amount ? 1 : 0;
if (!amount) continue;
compiled(data[i], data[i + 1], data[i + 2], out);
for (let c = 0; c < 3; c++) beforeRGB[c] = data[i + c] / 255;
if (a.values.dither && (a.type === 'lookup' || a.type === 'gradient'))
for (let c = 0; c < 3; c++) out[c] = clamp(out[c] + (pixelNoise(i / 4) - 0.5) / 255);
blendRGB(beforeRGB, out, blend, mixed);
for (let c = 0; c < 3; c++)
data[i + c] = Math.round(clamp(beforeRGB[c] + (mixed[c] - beforeRGB[c]) * amount) * 255);
}
}L253–265Photoshotの実装コード ↗
export function isIdentityAdjustment(a: Adjustment): boolean {
if (a.type === 'lookup') return !a.cube;
if (a.type === 'curves') return channels.every((c) => a.curves![c].every((p) => p.x === p.y));
if (a.type === 'photo') return a.values.density === 0;
if (['blackwhite', 'gradient', 'invert', 'posterize', 'threshold'].includes(a.type)) return false;
if (a.bands?.some((b) => b.hue || b.saturation || b.lightness)) return false;
return definitions[a.type].controls.every(
(c) =>
['preserve', 'relative', 'skin', 'legacy', 'colorizeHue', 'colorizeSaturation'].includes(
c.key,
) || (a.values[c.key] ?? c.initial) === c.initial,
);
}エディターで見る。

実際の出力と次の機能
保存作例は数値調節のない反転調整、不透明度100%です。原画像はAI生成人物写真で、操作画面と出力は実際のPhotoshotの資料です。
この回の中心は新しい色の公式より、同じ編集モデルに組み込む接続です。次回は連続値を限られた階調にまとめるポスタリゼーションを扱います。
写真で見る結果。


AI生成の動作例用写真。編集画面と出力画像は実際のPhotoshotで作成しました。