無料画像編集ソフト開発記 第1回:GitHubのコードとAIでレイヤー・マスクをつなぐ
無料のPhotoshop代替を目指すPhotoshotは、すべてをゼロから書いたわけではない。GitHubで開発されるReactやPixiJSを利用し、文書構造、レイヤー、マスクを結ぶコードをAIとともに書いた。初期コミットと現在の実装から、借りた部分と新しく実装した部分をたどる。
出典照合 · 2026年10月16日改訂
記事を読む ↓

GitHubから持ってきた土台
Reactは画面と状態管理、初期のPixiJSは画像レイヤーの描画を担当した。初期公開コミットではpixi.jsからApplication、Sprite、Textureを読み込んでいる。名前だけを参考にしたのではなく、実行経路に組み込まれた依存ライブラリだった。
ここで「GitHubのコードを使った」とは、パッケージを導入し公開APIを利用したという意味だ。完成済みのエディターを丸ごと複製したわけではない。
'use client';
import { useEffect, useRef } from 'react';
import { Application, Sprite, Texture } from 'pixi.js';
import 'pixi.js/advanced-blend-modes';
import { layerSurface, type StudioDoc } from './document';AIと書いたのは接続部分
PixiJSはPhotoshotが位置や回転、不透明度をどこに保存するか知らない。AIと書いた接続コードはレイヤーの描画面からSpriteを作り、Photoshot側の値をPixiJSへ渡す。下のlは文書内のレイヤーデータ、sは描画側のオブジェクトだ。
既存ライブラリと独自のデータ構造の間を埋めることが、AIと行った実装の具体的な役割だった。
const s = new Sprite(Texture.from(canvas));
s.anchor.set(0.5);
s.position.set(l.x + l.width / 2, l.y + l.height / 2);
s.rotation = (l.rotation * Math.PI) / 180;
s.alpha = l.opacity;
s.blendMode = l.blend;
a.stage.addChild(s);元画像を消さずにマスクを適用する
レイヤーが表示できても、マスクを塗った時に元の画素まで消えては困る。Photopeaのマスク説明は動作の参考資料とし、Photoshotの接続処理は別に実装した。
現在のコードは一時的なレイヤー描画面へdestination-inを適用し、処理後にsource-overへ戻す。元画像とマスクを別々に保持するため、マスクを無効にすれば元の見え方に戻せる。
if (layer.mask?.enabled) {
ctx.globalCompositeOperation = 'destination-in';
ctx.drawImage(maskSurface(layer.mask), 0, 0, c.width, c.height);
ctx.globalCompositeOperation = 'source-over';
}初期PixiJSと現在のCanvasを区別する
開発が進むと、プレビューと書き出しを同じ合成経路にそろえる必要が出てきた。v0.16.0のStageは初期のSprite生成ではなくcompositeを呼び、表示サイズの上限と古い計算を中止する条件を渡す。
初期にはPixiJSを実際に使い、その後はCanvasベースの共通合成処理へ移った。この二段階を区別することが、GitHubの履歴から開発を説明する意味だ。
const image = await composite(d, false, {
maxEdge: 1600,
cancelled: () => !mounted.current || r !== revision.current,
});
if (!mounted.current) break;画面の裏で確認したこと
掲載画像は実際の編集画面と実装記録だ。レイヤー順序、マスクの切り替え、保存と再読み込み、書き出しまで確認した。マスク描画テストには元画像を変更しないことの確認も含まれる。
AIが速くコードを書くことと、編集結果が保たれることは別の問題だ。ライブラリの責任範囲と、独自の文書・保存・合成コードの責任範囲を分けて調べた。


利用したもの、実装したもの
利用したのはReactと初期PixiJSの機能。参考にしたのはマスクの動作説明。AIと新規作成・修正したのはレイヤーの接続、マスク適用、保存構造、その後の共通合成処理だった。既存の部品を編集ソフトにつなげる作業が出発点になった。
次回はGitHubでKonvaのコントラスト処理を読み、独自実装を比較・修正した過程を追う。今回のコードは初期公開コミットとv0.16.0から抜粋した。