もどる TOP

[JavaScript] ドットの円と楕円を描画するサンプル

 ドットで丸を描画できたらゲームのエフェクトに使えると軽い気持ちで手を出したが甘い甘い甘い甘い アルゴリズムがまるで理解できなくて結局コピペでゴリ押ししてしまった

ドットの円と楕円のサンプル

 最初は、円を描画するアルゴリズムを調べた。英語版WikipediaにJavaScriptのソースが載ってたので、変数名とかを自分に分かるように書き直して使った。よくわからないが、dyの値が1だと半径が小さいときに円というより四角になってしまったので、いろいろ触ってみて2に変更した。

Midpoint circle algorithm - Wikipedia

 次に、楕円を描画するアルゴリズムを調べた。Stack Overflowにコピペされてたのを使わせてもらった。触ってみても1ミリしか解らなくて魂が砕けた。ただ、試してみると、この方法だと縦横の長さを入れ替えて90度回転させたとき、完全に一致する楕円にはならないらしい。

javascript - Is there a midpoint ellipse algorithm? - Stack Overflow

 これらをお借りして、ドットで線を引けるようにいじったサンプルが以下である。

サンプル1 [直接開く]
サンプル1 HTML
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8" />
	<meta name="robots" content="noarchive" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
	<meta name="landscape" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
	<link rel="stylesheet" href="style.css" />
	<script src="sample1.js"></script>
	<title>Canvas ドットの円・楕円描画サンプル その1</title>
</head>
<body>
	種類: <select id="draw-type">
		<option value="circle">円</option>
		<option value="ellipse-horizontal">楕円(横)</option>
		<option value="ellipse-vertical">楕円(縦)</option>
	</select> 
	ドットの大きさ: <select id="pixel-size">
		<option value="1">1</option>
		<option value="2">2</option>
		<option value="3">3</option>
		<option value="4">4</option>
		<option value="6">6</option>
		<option value="8">8</option>
	</select>px
    <canvas id="canvas"></canvas>
</body>
</html>
サンプル1 JavaScript
const CANVAS_WIDTH = 320;
const CANVAS_HEIGHT = 320;
var _strokeColor = [255, 255, 255, 255];


window.addEventListener('load', function() {
	// 初期設定
	var canvas = document.getElementById('canvas');
	canvas.width  = CANVAS_WIDTH;
	canvas.height = CANVAS_HEIGHT;

	var ctx = canvas.getContext('2d');
	ctx.fillStyle = 'rgba(0, 0, 0, 255)';
	
	// 種類・ドットの大きさ変更時に再描画
	document.getElementById('draw-type').addEventListener('change', function(e) {
		var type = e.target.value;
		var pixelSize = parseInt(document.getElementById('pixel-size').value, 10);
		draw(ctx, type, pixelSize);
	});
	document.getElementById('pixel-size').addEventListener('change', function(e) {
		var type = document.getElementById('draw-type').value;
		var pixelSize = parseInt(e.target.value, 10);
		draw(ctx, type, pixelSize);
	});

	// ドットの大きさ1pxで円を描画
	draw(ctx, 'circle', 1);
});

/**
 * Canvasにサンプルを描画する
 * @param {CanvasRenderingContext2D} ctx Canvasのコンテキスト
 * @param {string} type 種類
 * @param {integer} pixelSize ドットの大きさ
 */
function draw(ctx, type, pixelSize) {
	// 黒で塗りつぶす
	ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

	// 値をドットの大きさの整数倍に補正する関数
	var pixelize = function(value) {
		return value - (value % pixelSize);
	};
	var x = pixelize(160);
	var y = pixelize(160);
	
	// ImageData取得して書き込み
	var imageData = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
	switch (type) {
		case 'circle':
			putPixelCircle(imageData, x, y, pixelize(10), pixelSize);
			putPixelCircle(imageData, x, y, pixelize(30), pixelSize);
			putPixelCircle(imageData, x, y, pixelize(50), pixelSize);
			putPixelCircle(imageData, x, y, pixelize(80), pixelSize);
			putPixelCircle(imageData, x, y, pixelize(110), pixelSize);
			putPixelCircle(imageData, x, y, pixelize(140), pixelSize);
			break;
		case 'ellipse-horizontal':
			putPixelEllipse(imageData, x, y, pixelize(16), pixelize(8), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(48), pixelize(24), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(80), pixelize(40), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(120), pixelize(60), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(180), pixelize(90), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(240), pixelize(120), pixelSize);
			break;
		case 'ellipse-vertical':
			putPixelEllipse(imageData, x, y, pixelize(8), pixelize(16), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(24), pixelize(48), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(40), pixelize(80), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(60), pixelize(120), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(90), pixelize(180), pixelSize);
			putPixelEllipse(imageData, x, y, pixelize(120), pixelize(240), pixelSize);
			break;
	}


	// ImageDataをcanvasに反映
	ctx.putImageData(imageData, 0, 0);
}

/**
 * ImageDataにドットの円を描画する
 * https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
 * @param {ImageData} imageData
 * @param {integer} cx 中心X座標
 * @param {integer} cy 中心Y座標
 * @param {integer} radius 半径
 * @param {integer} pixelSize ドットの大きさ
 */
function putPixelCircle(imageData, cx, cy, radius, pixelSize) {
	var x = radius; // 半径 - 線幅
	var y = 0;
	var diameter = radius * 2; // 直径
	var dx = 1;
	var dy = 2; // わかんないけど1だと半径が小さい時に四角になっちゃうから2にした
	var decisionOver2 = dx - diameter;
	
	// 0・90・180・270度の地点から両側に向かって線を伸ばしていき、計8本の弧を書く
	while (x >= y) {
		putPixel(imageData, cx + x, cy - y, pixelSize); //   0 ->  45
		putPixel(imageData, cx + y, cy - x, pixelSize); //  45 <-  90
		putPixel(imageData, cx - y, cy - x, pixelSize); //  90 -> 135
		putPixel(imageData, cx - x, cy - y, pixelSize); // 135 <- 180
		putPixel(imageData, cx - x, cy + y, pixelSize); // 180 -> 225
		putPixel(imageData, cx - y, cy + x, pixelSize); // 225 <- 270
		putPixel(imageData, cx + y, cy + x, pixelSize); // 270 -> 315
		putPixel(imageData, cx + x, cy + y, pixelSize); // 315 <- 360
		
		// わかんない
		if (decisionOver2 <= 0) {
		    y += pixelSize;
			decisionOver2 += dy * pixelSize;
			dy += 2 * pixelSize;
		}
		if (decisionOver2 > 0) {
			x -= pixelSize;
			dx += 2 * pixelSize;
			decisionOver2 += ((-diameter) + dx) * pixelSize;
		}
	}
}

/**
 * ImageDataにドットの楕円を描画する
 * https://stackoverflow.com/questions/15474122/is-there-a-midpoint-ellipse-algorithm
 * @param {ImageData} imageData
 * @param {integer} cx 中心X座標
 * @param {integer} cy 中心Y座標
 * @param {integer} a 横の長さ
 * @param {integer} b タテの長さ
 * @param {integer} pixelSize ドットの大きさ
 */
function putPixelEllipse(imageData, cx, cy, a, b, pixelSize) {
	var aa = a * a;
	var bb = b * b;
	var aa2 = 2 * aa;
	var bb2 = 2 * bb;
	var p;
	var x = 0;
	var y = b;
	var px = 0;
	var py = aa2 * y;

	// 最初の点を描画
	putPixel(imageData, cx + x, cy + y, pixelSize);
	putPixel(imageData, cx - x, cy + y, pixelSize);
	putPixel(imageData, cx + x, cy - y, pixelSize);
	putPixel(imageData, cx - x, cy - y, pixelSize);

	// 上下を描画 (わかんない)
	p = bb - (aa * b) + (0.25 * aa);
	while (px < py) {
		x += pixelSize;
		px += bb2 * pixelSize;
		if (p < 0) {
			p += bb + px * pixelSize;
		} else {
			y -= pixelSize;
			py -= aa2 * pixelSize;
			p += (bb + px - py) * pixelSize;
		}
		putPixel(imageData, cx + x, cy + y, pixelSize);
		putPixel(imageData, cx - x, cy + y, pixelSize);
		putPixel(imageData, cx + x, cy - y, pixelSize);
		putPixel(imageData, cx - x, cy - y, pixelSize);
	}

	// 左右を描画 (わかんない)
	p = bb * (x + 0.5) * (x + 0.5) + aa * (y - 1) * (y - 1) - aa * bb;
	while (y > 0) {
		y -= pixelSize;
		py -= aa2 * pixelSize;
		if (p > 0) {
			p += (aa - py) * pixelSize;
		} else {
			x += pixelSize;
			px += bb2 * pixelSize;
			p += (aa - py + px) * pixelSize;
		}
		putPixel(imageData, cx + x, cy + y, pixelSize);
		putPixel(imageData, cx - x, cy + y, pixelSize);
		putPixel(imageData, cx + x, cy - y, pixelSize);
		putPixel(imageData, cx - x, cy - y, pixelSize);
	}
}

/**
 * ImageDataにドットを描画する
 * @param {ImageData} imageData
 * @param {integer} x
 * @param {integer} y
 * @param {integer} pixelSize ドットの大きさ
 */
function putPixel(imageData, x, y, pixelSize) {
	// はみ出たら描画しない
	if (x < 0 || CANVAS_WIDTH <= x || y < 0 || CANVAS_HEIGHT < y) {
		return;
	}
	// 描画開始座標をドットごとのマス目の左上に補正
	x = x - (x % pixelSize);
	y = y - (y % pixelSize);

	// ImageDataにドット書き込み
	for (var offsetY = 0; offsetY < pixelSize; ++offsetY) {
		for (var offsetX = 0; offsetX < pixelSize; ++offsetX) {
			var i = 4 * ((y + offsetY) * CANVAS_WIDTH + (x + offsetX));
			imageData.data[i] = _strokeColor[0];
			imageData.data[i + 1] = _strokeColor[1];
			imageData.data[i + 2] = _strokeColor[2];
			imageData.data[i + 3] = _strokeColor[3];
		}
	}
}

ペイントっぽいサンプル

 ペイントソフトよろしく好きな大きさの丸を描けるサンプルも作ってみた。マウス操作不可なので、PCで見ている貴姉は開発者ツールなどで確認されたし。

 こっちだと、先述の縦横の挙動が一致しない感じがわりと分かる。横長で高さ1pxの丸は描けないが、縦長で幅1pxの線が描けるあたりで顕著である。同じ理由で、楕円の描画処理を使ってそのまま円を描こうとすると若干歪むので、縦横の長さが同じときは円のアルゴリズムに切り替えるようにしてみた。

サンプル2 (※タッチ操作のみ) [直接開く]
サンプル2 HTML
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8" />
	<meta name="robots" content="noarchive" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
	<meta name="landscape" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
	<link rel="stylesheet" href="style.css" />
	<script src="sample2.js"></script>
	<title>Canvas ドットの円・楕円描画サンプル その2</title>
</head>
<body>
	タッチ操作で丸を描けます。(動かない場合は一旦ドットの大きさを変更してみてください)<br />
	ドットの大きさ: <select id="pixel-size">
		<option value="1">1</option>
		<option value="2">2</option>
		<option value="3">3</option>
		<option value="4">4</option>
		<option value="6">6</option>
		<option value="8">8</option>
	</select>px
    <canvas id="canvas"></canvas>
</body>
</html>
サンプル2 JavaScript
const CANVAS_WIDTH = 320;
const CANVAS_HEIGHT = 320;
var _strokeColor = [255, 255, 255, 255];


window.addEventListener('load', function() {
	// Canvas初期設定
	var canvas = document.getElementById('canvas');
	canvas.width  = CANVAS_WIDTH;
	canvas.height = CANVAS_HEIGHT;
	
	// コンテキスト取得
	var ctx = canvas.getContext('2d');
	ctx.fillStyle = 'rgba(0, 0, 0, 255)';
	ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

	// 描画開始時の情報記憶用
	var imageData_org = null;
	var startPos = { x: null, y: null };

	// ドットの大きさセット
	var pixelSize = parseInt(document.getElementById('pixel-size').value, 10);

	// ドットの大きさ変更時の処理セット
	document.getElementById('pixel-size').addEventListener('change', function(e) {
		pixelSize = parseInt(e.target.value, 10);
	});
	
	// 描画開始時の処理セット
	canvas.addEventListener('touchstart', function(e) {
		if (imageData_org !== null) {
			return;
		}
		e.preventDefault();
		e = e.changedTouches[0];
		var imageData = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

		// Canvas内でのイベント発生座標を算出
		var bounds = e.target.getBoundingClientRect();
		var x = e.clientX - bounds.left;
		var y = e.clientY - bounds.top;

		// 描画開始時の座標とピクセル情報を保持しておく
		startPos.x = x;
		startPos.y = y;
		imageData_org = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

		// イベント座標からImageDataに楕円を描画
		putPixelEllipseByEventCoords(imageData, startPos.x, startPos.y, x, y, pixelSize);

		// ImageDataをcanvasに反映
		ctx.putImageData(imageData, 0, 0);
	});

	// 描画開始後にカーソル移動時の処理セット
	canvas.addEventListener('touchmove', function(e) {
		if (imageData_org === null) {
			return;
		}
		e.preventDefault();
		e = e.changedTouches[0];
		var imageData = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

		// 描画確定してない楕円を消す
		copyImageData(imageData_org, imageData);

		// Canvas内でのイベント発生座標を算出
		var bounds = e.target.getBoundingClientRect();
		var x = e.clientX - bounds.left;
		var y = e.clientY - bounds.top;

		// イベント座標からImageDataに楕円を描画
		putPixelEllipseByEventCoords(imageData, startPos.x, startPos.y, x, y, pixelSize);

		// ImageDataをcanvasに反映
		ctx.putImageData(imageData, 0, 0);
	});

	// 描画確定時の処理セット
	canvas.addEventListener('touchend', function(e) {
		if (imageData_org === null) {
			return;
		}
		e.preventDefault();
		e = e.changedTouches[0];
		var imageData = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

		// 描画確定してない楕円を消す
		copyImageData(imageData_org, imageData);

		// Canvas内でのイベント発生座標を算出
		var bounds = e.target.getBoundingClientRect();
		var x = e.clientX - bounds.left;
		var y = e.clientY - bounds.top;

		// イベント座標からImageDataに楕円を描画
		putPixelEllipseByEventCoords(imageData, startPos.x, startPos.y, x, y, pixelSize);

		// ImageDataをcanvasに反映
		ctx.putImageData(imageData, 0, 0);

		// 描画開始時に保存した値を破棄
		startPos.x = startPos.y = null;
		imageData_org = null;
	});
});

/**
 * ImageDataのピクセル情報をコピーする
 * @param {ImageData} src コピー元ImageData
 * @param {ImageData} dst コピー先ImageData
 */
function copyImageData(src, dst) {
	for (var i = 0, len = dst.data.length; i < len; ++i) {
		dst.data[i] = src.data[i];
	}
}

/**
 * イベント座標からドットの楕円を描画
 * @param {ImageData} imageData
 * @param {integer} startX 描画開始座標X
 * @param {integer} startY 描画開始座標Y
 * @param {integer} x イベント座標X
 * @param {integer} y イベント座標Y
 * @param {integer} pixelSize ドットの大きさ
 */
function putPixelEllipseByEventCoords(imageData, startX, startY, x, y, pixelSize) {
	// クリック座標とクリック開始座標から横とタテの長さを算出
	var a = (x - startX) / 2;
	var b = (y - startY) / 2;

	// クリック座標・横・タテの長さをドットの大きさの整数倍に補正
	x = x - (x % pixelSize);
	y = y - (y % pixelSize);
	a = Math.floor(a - (a % pixelSize));
	b = Math.floor(b - (b % pixelSize));

	// 中心座標算出
	var cx = Math.floor(startX + a);
	var cy = Math.floor(startY + b);

	// b辺の長さがマイナス (カーソルを上方向に動かした時) だったら補正
	b = b < 0 ? -1 * b : b;

	if (a === b) {
		// 縦横の長さが同じなら円を描画 (こっちの方が綺麗だから)
		putPixelCircle(imageData, cx, cy, a, pixelSize);
	} else {
		// 楕円を描画
		putPixelEllipse(imageData, cx, cy, a, b, pixelSize);
	}
}

/**
 * ImageDataにドットの楕円を描画する
 * https://stackoverflow.com/questions/15474122/is-there-a-midpoint-ellipse-algorithm
 * @param {ImageData} imageData
 * @param {integer} cx 中心X座標
 * @param {integer} cy 中心Y座標
 * @param {integer} a 横の長さ
 * @param {integer} b タテの長さ
 * @param {integer} pixelSize ドットの大きさ
 */
function putPixelEllipse(imageData, cx, cy, a, b, pixelSize) {
	var aa = a * a;
	var bb = b * b;
	var aa2 = 2 * aa;
	var bb2 = 2 * bb;
	var p;
	var x = 0;
	var y = b;
	var px = 0;
	var py = aa2 * y;

	// 最初の点を描画
	putPixel(imageData, cx + x, cy + y, pixelSize);
	putPixel(imageData, cx - x, cy + y, pixelSize);
	putPixel(imageData, cx + x, cy - y, pixelSize);
	putPixel(imageData, cx - x, cy - y, pixelSize);

	// 上下を描画 (わかんない)
	p = bb - (aa * b) + (0.25 * aa);
	while (px < py) {
		x += pixelSize;
		px += bb2 * pixelSize;
		if (p < 0) {
			p += bb + px * pixelSize;
		} else {
			y -= pixelSize;
			py -= aa2 * pixelSize;
			p += (bb + px - py) * pixelSize;
		}
		putPixel(imageData, cx + x, cy + y, pixelSize);
		putPixel(imageData, cx - x, cy + y, pixelSize);
		putPixel(imageData, cx + x, cy - y, pixelSize);
		putPixel(imageData, cx - x, cy - y, pixelSize);
	}

	// 左右を描画 (わかんない)
	p = bb * (x + 0.5) * (x + 0.5) + aa * (y - 1) * (y - 1) - aa * bb;
	while (y > 0) {
		y -= pixelSize;
		py -= aa2 * pixelSize;
		if (p > 0) {
			p += (aa - py) * pixelSize;
		} else {
			x += pixelSize;
			px += bb2 * pixelSize;
			p += (aa - py + px) * pixelSize;
		}
		putPixel(imageData, cx + x, cy + y, pixelSize);
		putPixel(imageData, cx - x, cy + y, pixelSize);
		putPixel(imageData, cx + x, cy - y, pixelSize);
		putPixel(imageData, cx - x, cy - y, pixelSize);
	}
}

/**
 * ImageDataにドットの円を描画する
 * https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
 * @param {ImageData} imageData
 * @param {integer} cx 中心X座標
 * @param {integer} cy 中心Y座標
 * @param {integer} radius 半径
 * @param {integer} pixelSize ドットの大きさ
 */
function putPixelCircle(imageData, cx, cy, radius, pixelSize) {
	var x = radius; // 半径 - 線幅
	var y = 0;
	var diameter = radius * 2; // 直径
	var dx = 1;
	var dy = 2; // わかんないけど1だと半径が小さい時に四角になっちゃうから2にした
	var decisionOver2 = dx - diameter;
	
	// 0・90・180・270度の地点から両側に向かって線を伸ばしていき、計8本の弧を書く
	while (x >= y) {
		putPixel(imageData, cx + x, cy - y, pixelSize); //   0 ->  45
		putPixel(imageData, cx + y, cy - x, pixelSize); //  45 <-  90
		putPixel(imageData, cx - y, cy - x, pixelSize); //  90 -> 135
		putPixel(imageData, cx - x, cy - y, pixelSize); // 135 <- 180
		putPixel(imageData, cx - x, cy + y, pixelSize); // 180 -> 225
		putPixel(imageData, cx - y, cy + x, pixelSize); // 225 <- 270
		putPixel(imageData, cx + y, cy + x, pixelSize); // 270 -> 315
		putPixel(imageData, cx + x, cy + y, pixelSize); // 315 <- 360
		
		// わかんない
		if (decisionOver2 <= 0) {
		    y += pixelSize;
			decisionOver2 += dy * pixelSize;
			dy += 2 * pixelSize;
		}
		if (decisionOver2 > 0) {
			x -= pixelSize;
			dx += 2 * pixelSize;
			decisionOver2 += ((-diameter) + dx) * pixelSize;
		}
	}
}

/**
 * ImageDataにドットを描画する
 * @param {ImageData} imageData
 * @param {integer} x
 * @param {integer} y
 * @param {integer} pixelSize ドットの大きさ
 */
function putPixel(imageData, x, y, pixelSize) {
	// はみ出たら描画しない
	if (x < 0 || CANVAS_WIDTH <= x || y < 0 || CANVAS_HEIGHT < y) {
		return;
	}

	// 描画開始座標をドットごとのマス目の左上に補正
	x = x - (x % pixelSize);
	y = y - (y % pixelSize);

	// ImageDataにドット書き込み
	for (var offsetY = 0; offsetY < pixelSize; ++offsetY) {
		for (var offsetX = 0; offsetX < pixelSize; ++offsetX) {
			var i = 4 * ((y + offsetY) * CANVAS_WIDTH + (x + offsetX));
			imageData.data[i] = _strokeColor[0];
			imageData.data[i + 1] = _strokeColor[1];
			imageData.data[i + 2] = _strokeColor[2];
			imageData.data[i + 3] = _strokeColor[3];
		}
	}
}

その他

 他に参考にさせていただいたところでは、以下のサイト様の解説は懇切丁寧でよかった。

伝説のお茶の間 No007-09(1) 円の描画(1) MichenerとBresenham

 ほか、次のサイト様のソースコードは、シンプルなのに意味不明で、流したら本当に○が描けて心底恐ろしかった。

円の描画(アルゴリズム)

 以下に、このコードをJavaScriptで動かすサンプルを置いておく。

わからん!! [直接開く]
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8" />
	<meta name="robots" content="noarchive" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
	<meta name="landscape" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
	<link rel="stylesheet" href="style.css" />
	<script src="wakaran.js"></script>
	<title>わからん</title>
</head>
<body>
	<canvas id="canvas"></canvas>
</body>
</html>
JavaScript
/**
 * これは以下サイト様のソースコードをJavaScriptで試せるようにしたものです。
 * 
 * 円の描画(アルゴリズム)
 * https://www.kazetest.com/vcmemo/drawcircle/drawcircle.htm
 */
const CANVAS_WIDTH = 320;
const CANVAS_HEIGHT = 320;

window.addEventListener('load', function() {
	var canvas = document.getElementById('canvas');
	canvas.width  = CANVAS_WIDTH;
	canvas.height = CANVAS_HEIGHT;

	var ctx = canvas.getContext('2d');
	ctx.fillStyle = 'rgba(0, 0, 0, 255)';
	var imageData = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

	drawCircle(180, 170, 16);
	drawCircle(160, 160, 52);
	drawEllipse(160, 160, 14, 34);
	drawEllipse(160, 160, 124, 62);
	
	
	function drawCircle(cx, cy, r) {
		var xx = r << 7;
		var yy = 0;
		var x = 0;
		var y = 0;
		while (yy <= xx ) {
			x = xx >> 7;
			y = yy >> 7;
			putPixel(cx + x, cy + y);
			putPixel(cx - x, cy - y);
			putPixel(cx - x, cy + y);
			putPixel(cx + x, cy - y);
			putPixel(cx + y, cy + x);
			putPixel(cx - y, cy - x);
			putPixel(cx - y, cy + x);
			putPixel(cx + y, cy - x);
			yy += xx >> 7;
			xx -= yy >> 7;
		}
		ctx.putImageData(imageData, 0, 0);
	}

	function drawEllipse(cx, cy, a, b) {
		var xx = a << 6;
		var yy = 0;
		var x = 0;
		var y = 0;
		
		while (xx >= 0) {
			x = xx >> 6;
			y = yy >> 6;
			putPixel(cx + x, cy + y);
			putPixel(cx - x, cy - y);
			putPixel(cx - x, cy + y);
			putPixel(cx + x, cy - y);
			yy += xx * b / a >> 6;
			xx -= yy * a / b >> 6;
		}
		ctx.putImageData(imageData, 0, 0);
	}

	function putPixel(x, y) {
		var i = 4 * (y * CANVAS_WIDTH + x);
		imageData.data[i] = 255;
		imageData.data[i + 1] = 255;
		imageData.data[i + 2] = 255;
		imageData.data[i + 3] = 255;
	}
});
サイドバーを表示する
ブログ
ShortCircuit
ShortCircuit
花火大会
天使
去る512時間前、キリウ君は折れてない千歳飴を渡してきて、ぼくが折るよう仕向けた。1024時間前、彼はこの世のものではないハッシュアルゴリズムでひとりブロックチェーンを始めていた。