AndroidのBitmap画像プログラミング(4) 線の描画
AndroidのBitmap画像プログラミングの第四回は線の描画方法を紹介します。
線の描画と言っても、CanvasクラスのdrawLineとかではなく、画像バッファのカラー情報を操作する方法を取ります。
今回は横方向に引く関数と、2つの座標を用意して線を引く関数を用意しました。
後者の関数だけで線は引けるのですが、横方向のみの場合はArrays.fillを使用して高速に処理をしています。
これは画像バッファのindexが連続しているからできるのであって、後者ではその判定を行うのが面倒になるからです。
その関数ではブレゼンハムアルゴリズムで、描画すべき画像バッファ位置を割り出しています。
加算・減算だけで画像バッファ位置を割り出せ、乗算・割算を使用しないため処理が高速になります。
/**
* 横線描画処理
*
* @param : int : sx : 始点x
* @param : int : dx : 終点x
* @param : int : y : y座標
* @param : int : color : ARGB
* @return : boolean
* @author : N.Nishimura
* @version : 1.0
* @since : 2011/02/22 1.0
*/
public boolean LineToX(int sx, int dx, int y, int color)
{
// 描く必要なし
if (y < 0) {
return false;
}
if (y >= mHeight) {
return false;
}
// swap
if (sx > dx) {
int t = 0;
t = sx;
dx = sx;
sx = t;
}
// 描く必要なし
if (dx < 0) {
return false;
}
if (sx >= mWidth) {
return false;
}
// クリッピング
if (sx < 0) {
sx = 0;
}
if (dx >= mWidth) {
dx = mWidth - 1;
}
// 始点アドレスを取得
int s = GetPixelAddress(sx, y);
Arrays.fill(mBuffer, s, s + dx - sx, color);
return true;
}
/**
* 線描画処理
* ブレゼンハムアルゴリズム
*
* @param : int : sx : 始点x
* @param : int : dx : 終点x
* @param : int : sy : 始点y
* @param : int : dy : 終点y
* @param : int : color : ARGB
* @return : boolean
* @author : N.Nishimura
* @version : 1.0
* @since : 2011/02/22 1.0
*/
public boolean LineTo(int sx, int dx, int sy, int dy, int color)
{
// クリッピング
if (sx < 0) {
sx = 0;
}
if (sy < 0) {
sy = 0;
}
if (dx < 0) {
dx = 0;
}
if (dy < 0) {
dy = 0;
}
if (sx >= mWidth) {
sx = mWidth - 1;
}
if (dx >= mWidth) {
dx = mWidth - 1;
}
if (sy >= mHeight) {
sy = mHeight -1;
}
if (dy >= mHeight) {
dy = mHeight - 1;
}
// 始点から終点の距離を求める
int distance_x = dx - sx;
int add_x = 1;
if (distance_x < 0) {
distance_x = -distance_x;
add_x = -add_x;
}
int distance_y = dy - sy;
int add_y = 1;
if (distance_y < 0) {
distance_y = -distance_y;
add_y = -add_y;
}
// 現在の座標
int cur_x = sx;
int cur_y = sy;
// 距離が長い方を軸とする
int counter = 0;
if (distance_x > distance_y) {
for (int i = 0; i < distance_x; i++) {
mBuffer[cur_x + mWidth * cur_y] = color;
counter += distance_y;
if (counter > distance_x) {
counter -= distance_x;
cur_y += add_y;
}
cur_x += add_x;
}
}
else {
for (int i = 0; i < distance_x; i++) {
mBuffer[cur_x + mWidth * cur_y] = color;
counter += distance_x;
if (counter > distance_y) {
counter -= distance_y;
cur_x += add_x;
}
cur_y += add_y;
}
}
return true;
}
Activity
package net.n2works.BitmapTest;
// SYSTEM PACKAGE
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PorterDuff.Mode;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class BitmapTest extends Activity
{
// Bitmap
private Bitmap mBitmap;
/**
* Component
*/
// SurfaceView
private SurfaceView mView;
// ExecutorService
private ScheduledExecutorService executor;
/**
* クラス定数
*/
private final int BG_COLOR = 0xff000000;
private final int BITMAP_W = 300;
private final int BITMAP_H = 300;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// 下地の作成
ImageBuffer buf = new ImageBuffer(BITMAP_W, BITMAP_H);
// 横線を描画
buf.LineToX(0, BITMAP_W, BITMAP_H / 2, 0xffff0000);
// 線の描画
buf.LineTo(0, BITMAP_W, 0, BITMAP_H, 0xff0000ff);
buf.LineTo(BITMAP_W, 0, 0, BITMAP_H, 0xffffff00);
mBitmap = Bitmap.createBitmap(BITMAP_W, BITMAP_H, Bitmap.Config.ARGB_8888);
mBitmap.setPixels(buf.GetBuffer(), 0, BITMAP_W, 0, 0, BITMAP_W, BITMAP_H);
// Viewの設定
mView = new SurfaceViewEx(this);
setContentView(mView);
}
protected void onDestroy()
{
mBitmap.recycle();
executor.shutdown();
super.onDestroy();
}
private class SurfaceViewEx extends SurfaceView
implements SurfaceHolder.Callback
{
boolean is_run;
/**
* クラス定数
*/
private static final int FPS = 60;
public SurfaceViewEx(Context context)
{
super(context);
getHolder().addCallback(this);
getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
public void surfaceCreated(SurfaceHolder holder)
{
executor = Executors.newSingleThreadScheduledExecutor();
is_run = true;
executor.scheduleAtFixedRate(
new Runnable()
{
public void run()
{
if (is_run) {
Canvas c = getHolder().lockCanvas();
Draw(c);
getHolder().unlockCanvasAndPost(c);
}
}
},
1000 / FPS, 1000 / FPS,
TimeUnit.MILLISECONDS
);
}
public void surfaceDestroyed(SurfaceHolder holder)
{
is_run = false;
}
protected void Draw(Canvas c)
{
// 背景色クリア
c.drawColor(BG_COLOR, Mode.SCREEN);
// Bitmap描画
c.drawBitmap(mBitmap, 0, 0, null);
}
}
}