sips
コマンドとは
macOS には標準で /usr/bin/sips
に画像処理用のコマンドがあります。
ImageMagick などに頼らなくても簡単な画像処理ができるツールとして重宝されています。
従来の sips
コマンドの使い方については Qiita にもいくつか記事が上がっています。
最近 (おそらく Big Sur 以降 1) になって、sips
コマンドがこっそり強化され、JavaScript を使って画像の操作が行えるようになりました。
インターフェースとしては <canvas>
要素のものを模倣しており、内部的には CoreGraphics を使って実装されているようです。
これによって従来 sips
ではできなかった、柔軟な画像処理ができるようになってきました。
実装としては JavaScript の実行環境は JavaScriptCore.framework を使っていて、 Canvas APIは CoreGraphics.framework で同等のものを実装して、JavaScript とブリッジしているようです。
JavaScript インターフェースの使い方
さっそく sips
の manpage を見てみましょう。
$ man sips
...
-j file
--js file
Execute JavaScript file
...
JavaScript
HTML Canvas objects can be created and used to create a 2D drawing context. The com-
mands for drawing into the context are well documented elsewhere. This section will
describe the sips global object and other interesting classes.
...
-j
--js
オプションに JavaScript ファイルを渡すことで、画像の操作ができそうです。
また、HTML の <canvas>
で使える操作ができるとのことですが、その詳細はドキュメントには書かれていません。
Hello world
というわけで早速 Hello world してみましょう。
console.log('Hello, world!');
$ sips -j hello.js
Hello, world!
はい、無事 JavaScript が実行されています。
console.log
以外に print
関数も定義されているので、書き換えても動作します。
また、JavaScriptCore では shebang が使えるのでスクリプト単体を配布することもできます。
#!/usr/bin/sips -j
console.log('Hello, world!');
このファイルに実行属性をつければ、以下のようにスクリプト単体で実行できます。
$ ./hello.js
Hello, world!
ゼロから画像を生成
<canvas>
API を使ってゼロから画像を生成することもできます。
試しに MDN の Canvas API Tutorial のページに載っているサンプルを描いてみましょう。
const canvas = new Canvas(150, 150);
canvas.fillStyle = 'rgb(200,0,0)';
canvas.fillRect(10, 10, 50, 50);
canvas.fillStyle = 'rgba(0,0,200,0.5)';
canvas.fillRect(30, 30, 50, 50);
const output = new Output(canvas, 'rect.png');
output.addToQueue();
$ sips -j rect.js
はい、無事2つの四角形が描画されました。
MDN のサイトに載っている Canvas API を使ったサンプルと異なるところがいくつかあるので見ていきましょう。
まず最初は Canvas
オブジェクトの生成です。
通常ブラウザの API では HTML で <canvas>
要素を定義しておいて、document.getElementById()
などを呼んで HTMLCanvasElement
を取得しますが、sips
コマンドの API では直接 new Canvas(width, height)
の形でインスタンス化できます。
次に、 canvas.getContext('2d')
を読んで 2D 描画コンテキストを取得する必要がない点です。
この API は定義自体はされているので、呼んでも構いませんが、実装としては単に this
を返すだけになっているようです。したがって Canvas API の CanvasRenderingContext2D
オブジェクトに対する操作は Canvas
オブジェクト自体に対してすることができます。
最後に、描画した画像を書き出す API が存在していることです。
Output
オブジェクトは new Output(context, name, type)
の形でインスタンス化できます。
context
引数には Canvas
オブジェクト、name
引数には出力先のパス、省略可能な type
引数には出力ファイルの拡張子か UTI を渡します。
type
引数が省略された場合は name
引数の拡張子から判断されるようです。
UTI の概念は macOS 独特のもので馴染みがない方もいると思うので、利用可能な拡張子と UTI を載せておきます。
拡張子 | UTI |
---|---|
png | public.png |
jpeg / jpg | public.jpeg |
gif | com.compuserve.gif |
tiff | public.tiff |
bmp | com.microsoft.bmp |
パスを使った例
Canvas API と同じく、パスを使った描画も可能です。
やはり MDN のサンプルがほぼ変更なしに動作します。
const canvas = new Canvas(150, 150);
canvas.beginPath();
canvas.arc(75, 75, 50, 0, Math.PI * 2, true);
canvas.moveTo(110, 75);
canvas.arc(75, 75, 35, 0, Math.PI, false);
canvas.moveTo(65, 65);
canvas.arc(60, 65, 5, 0, Math.PI * 2, true);
canvas.moveTo(95, 65);
canvas.arc(90, 65, 5, 0, Math.PI * 2, true);
canvas.stroke();
const output = new Output(canvas, 'smile.png');
output.addToQueue();
$ sips -j smile.js
smile.png
画像を使った例
別の画像を読み込むこともできます。
先程までの例で生成された画像を一枚の画像にしてみましょう。
const canvas = new Canvas(150, 150);
sips.images.forEach(image => {
canvas.drawImage(image, 0, 0);
});
const output = new Output(canvas, 'composite.png');
output.addToQueue();
$ sips -j composite.js rect.png smile.png
composite.png
テキストを描画する
sips
コマンドには CoreText.framework を使ったテキスト描画の機能も Canvas API の一部として提供されています。
const canvas = new Canvas(100, 100);
canvas.filllStyle = 'black';
canvas.fillRect(0, 0, canvas.width, canvas.height);
canvas.font = '42pt Futura';
canvas.textAlign = 'center';
canvas.textBaseline = 'middle';
canvas.fillStyle = 'skyblue';
canvas.fillText('TEXT', canvas.width / 2, canvas.height / 2);
const output = new Output(canvas, 'text.png');
output.addToQueue();
$ sips -j text.js
実装されていない機能
ここまで紹介した機能で十分高度な画像編集ができそうですが、一部 Canvas API に比べて動作が異なる機能があります。
例えば、Canvas API の CanvasRenderingContext2D.createPattern()
は sips
だと Canvas.createPattern()
にあたり、ここまでは実装されているものの、作成したオブジェクトを利用する API が実装されていないようです。
通常は canvas.fillStyle
などに渡すのですが、canvas.fillStyle = canvas.createPattern(...)
では渡せず、sips
バイナリの中にもそのような処理は実装されていないように見えます。
まとめ
これまで ImageMagick などで行っていた画像処理が macOS 標準コマンドでできるようになりました。
API もブラウザの API をよく再現していて知識を使いまわせるので、これから使っていくと良さそうです。
おまけ : 非公式 API リファレンス
公式の manpage にも簡単な使い方が載っていますが、網羅的ではないため Hopper Disassember を使って sips
コマンドの中で実装されている API を解析してみました。
よく使いそうな API は実際に試して動作を確認し、typedoc でドキュメントを作成しました。
以下に置いておいたので、もしよければ参考にしてください。
-
Catalina でも manpages に
-j
--js
オプションの記載はあり一見できそうですが、クラッシュしてしまい動作を確認できなかったためバグレポートを起票しています https://openradar.appspot.com/radar?id=5061043655016448 ↩