作 ▸
CanvasでRGBチャンネルごとに色の位置をずらす加工
タイトルわかりにく!! グリッチっぽいあれです。

加工の流れは次の通りです。
- 全ピクセル (
ImageData.data
) をスキャンして、各座標の指定チャンネルの値を取得したあと、現在の値をゼロにします。 - ふたたび全ピクセルをスキャンして、移動先座標の指定チャンネルに、取得しておいた値を加算します。
移動先座標を算出するので、2回目のスキャンはxyでやってます。xyの座標は、(y \* 幅 + x) * 4
で ImageData.data
のインデックスに変換できます。
/**
* 指定チャンネルの描画位置をずらす
* @param {HTMLCanvasElement} canvas canvas要素
* @param {number} channel 0: 赤, 1: 緑, 2: 青
* @param {number} shiftX 右方向移動量 (px)
* @param {number} shiftY 下方向移動量 (px)
*/
function shiftRGB(canvas, channel, shiftX, shiftY) {
const ctx = canvas.getContext('2d');
const w = canvas.width;
const h = canvas.height;
const tmp = [];
const imageData = ctx.getImageData(0, 0, w, h);
for (let i = 0, len = imageData.data.length; i < len; i += 4) {
tmp[i] = imageData.data[i + channel];
imageData.data[i + channel] = 0;
}
for (let y = 0; y < h; ++y) {
for (let x = 0; x < w; ++x) {
const dstX = x + shiftX;
const dstY = y + shiftY;
if (0 <= dstX && dstX < w && 0 <= dstY && dstY < h) {
const i = (y * w + x) * 4;
const dstI = (dstY * w + dstX) * 4;
imageData.data[dstI + channel] = imageData.data[dstI + channel] + tmp[i];
}
}
}
ctx.putImageData(imageData, 0, 0);
}
以下、ページ上部でやってるサンプルの例です。
<figure>
<img id="exsample-org" src="/assets/entry/20171118-rgb-channel-shifting/org.gif" />
<figcaption>元の画像</figcaption>
</figure>
<div style="display:flex; flex-wrap:wrap;">
<figure style="flex-grow:1;">
<canvas id="exsample-1"></canvas>
<figcaption>サンプル1: 赤を→2</figcaption>
</figure>
<figure style="flex-grow:1;">
<canvas id="exsample-2"></canvas>
<figcaption>サンプル2: 緑を←2, ↑2</figcaption>
</figure>
<figure style="flex-grow:1;">
<canvas id="exsample-3"></canvas>
<figcaption>サンプル3: 青を←4, ↓3</figcaption>
</figure>
</div>
document.getElementById('example-org').addEventListener("load", (event) => {
/**
* Canvasをコピー
* @param {HTMLCanvasElement} src コピー元
* @param {HTMLCanvasElement} dst コピー先
*/
function copyCanvas(src, dst) {
dst.width = src.width;
dst.height = src.height;
const ctx = dst.getContext('2d');
ctx.drawImage(src, 0, 0, dst.width, dst.height);
return dst;
}
const orgImage = event.currentTarget;
const canvas1 = copyCanvas(orgImage, document.getElementById('example-1'));
const canvas2 = copyCanvas(orgImage, document.getElementById('example-2'));
const canvas3 = copyCanvas(orgImage, document.getElementById('example-3'));
// サンプル1: 赤を→2
shiftRGB(canvas1, 0, 2, 0);
// サンプル2: 緑を←2, ↑2
shiftRGB(canvas2, 1, -2, -2);
// サンプル3: 青を←4, ↓3
shiftRGB(canvas3, 2, -4, 3);
});