写真ズーム(拡大・縮小)処理①

概要

今回はマウス操作によるCANVAS上の写真のズーム操作について勉強します。
下の左の図はCANVAS上に写真をふつうに取り込んだものです。右の図は倍率を5倍に拡大し表示しているものです。スライダーを使って連続的に倍率を変更できる様にしています。

後日、IE11で確認したところ、スライダーのイベントリスナーが動かないことが判りました。IE11の場合はイベントを“input”ではなく、“change”にするとよい様です。次の投稿の写真ズーム(拡大・縮小)処理②では、開いたブラウザーで変更する様にしています。
行番49 :  sld_chk.addEventListener( input, pict_scale , false ) ;

 

基礎知識

写真ズーム(拡大・縮尺)を行う上で2つの方法で確認しました。scale() メソッドとdrawImage() メソッドの2つ用いる方法とdrawImage() メソッドのみを用いる方法です。下記表にそれぞれのプログラムへのリンクを載せておきます。結果は同じですが、自分としては写真中央からズームさせるのに少し苦労したので、下の表に両プログラムへのリンクを残しておきます。

  scale() メソッドとdrawImage() メソッドを用いる方法
  drawImage() メソッドのみを用いる方法

drawImage()メソッドはこれまでも何となく使ってきましたが、ズーム(拡大・縮尺)を行う時に表示位置を制御する為には、scale() メソッドも含めて、ある程度理解しておく必要があると思いましたので、今回整理してみたいと思います。

(1)context . scale(x,y)  
scale(x, y) メソッドの引数 x は水平方向の伸縮係数、引数 y は垂直方向の伸縮係数を表します。使い方は drawImage()等で描画メソッドを実行する前にscale(x, y) メソッドで伸縮係数を設定し、座標系を変換します。下の図は1倍で描画した元画像とscale(2, 2)により座標系を2倍に変換した拡大画像を比較するものです。画像サイズだけでなく、原点からの描画基点までの距離も2倍になっていることが判ります。

(2)context . drawImage()  
drawImage() メソッドは次の3つの引数の指定方法があります。③が元画像の対象領域を指定しているのに対し、省略形の①②は元画像全体を対象領域にしています。図からも判りますが、sw:dw ,sh:dh の設定によって、scale() メソッドを使わなくても、画像倍率を変更することが出来ます。

  context . drawImage(image, dx, dy)
  context . drawImage(image, dx, dy, dw, dh)
  context . drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

(図引用: https://www.w3.org/TR/2dcontext/  )

プログラミング

(1)scale() メソッドとdrawImage() メソッドを用いる方法
行番107でスライダーを設定し、行番49によりスライダーの値変化を監視します。変化があった場合、関数 pict_scale(行番53~74)を実行し、ズーム(拡大・縮小)画像を再描画します。行番58でスライダーの値を倍率として取得します。行番69  scale()メソッドで倍率指定し、行番70でズーム画像を描画します。
画像を拡大することで画像の中心位置は原点から離れていく為、描画基点の補正が必要です。行番60~61で基点補正を行い、ズーム画像を描画する行番70で指定しています。

<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>DRAWING TEST</title>

    <style>
	input { height:30px; }
    </style>

    <script>
	var cvs_pic ; var ctx_pic ;
	var cvs_wdt ; var cvs_hgt ; var img_h ; var img_w ; var zom_h ; var zom_w ;
	var x_b=0; var y_b=0; var x_c=0; var y_c=0; var x_e=0; var y_e=0; var x_dif=0; var y_dif=0; 
	var f_b ;
	var sld_chk ; var tmp_pic ;


	function draw_begin(){
	    cvs_wdt = 840 ; cvs_hgt = 600 ;
	    cvs_pic = document.getElementById('CANVAS_PIC') ; 
	    ctx_pic = cvs_pic.getContext("2d") ;

	    sld_chk = document.getElementById("zoom-slider");
	    sld_chk.min = 0.01; sld_chk.max = 5; sld_chk.step = 'any';
	    sld_chk.value=1;

	    var image = new Image(); 
            image.src = "./sample.jpg" ;

	    image.onload = function () {
		img_h = image.height ; img_w = image.width ;

		cvs_hgt = Math.floor(img_h * cvs_wdt / img_w) ;
                cvs_pic.width = cvs_wdt ; cvs_pic.height = cvs_hgt ;

		document.getElementById('i101').innerHTML = img_w ;
		document.getElementById('i102').innerHTML = img_h ;
		document.getElementById('i103').innerHTML = cvs_wdt ;
		document.getElementById('i104').innerHTML = cvs_hgt ;

                ctx_pic.drawImage( image, 0 , 0 , cvs_wdt , cvs_hgt ) ;
		ctx_pic.strokeRect( 0 , 0 , cvs_wdt, cvs_hgt) ;

		tmp_pic = new Image ;
		tmp_pic.src = ctx_pic.canvas.toDataURL() ;

		sld_chk.addEventListener( "input", pict_scale , false ) ;
	    }
	}

	function pict_scale(event){
	    ctx_pic.clearRect(0, 0, cvs_pic.width, cvs_pic.height);
	    ctx_pic.save();
	    ctx_pic.globalAlpha = 1 ; ctx_pic.lineWidth = 1 ; ctx_pic.strokeStyle = "#000000" ;
	    
	    var scale = event.target.value;
	    zom_h = Math.floor(scale*img_h) ; zom_w = Math.floor(scale*img_w) ;
	    var zoh_s = Math.floor((1-scale)*cvs_hgt/2/scale) ;
	    var zow_s = Math.floor((1-scale)*cvs_wdt/2/scale) ;

	    document.getElementById('i105').innerHTML = Math.floor(1000*scale)/1000 ;
	    document.getElementById('i106').innerHTML = zom_w ;
	    document.getElementById('i107').innerHTML = zom_h ;
	    document.getElementById('i108').innerHTML = zow_s ;
	    document.getElementById('i109').innerHTML = zoh_s ;

	    ctx_pic.scale(scale, scale);
	    ctx_pic.drawImage(tmp_pic , zow_s , zoh_s );
	    // ctx_pic.scale(1 / scale, 1 / scale);
	    ctx_pic.restore() ;
	    ctx_pic.strokeRect( 0 , 0 , cvs_pic.width, cvs_pic.height) ;
	}

    </script>
</head>

<body onLoad="draw_begin()">

    <TABLE>
	<TR>
	<TD id="i001">画像幅</TD>
	<TD id="i002">画像高</TD>
	<TD id="i003">CVS幅</TD>
	<TD id="i004">CVS高</TD>
	<TD id="i005">倍率</TD>
	<TD id="i006">拡大幅</TD>
	<TD id="i007">拡大高</TD>
	<TD id="i008">始点X</TD>
	<TD id="i009">始点Y</TD>
	</TR>
	<TR>
	<TD id="i101"></TD>
	<TD id="i102"></TD>
	<TD id="i103"></TD>
	<TD id="i104"></TD>
	<TD id="i105"></TD>
	<TD id="i106"></TD>
	<TD id="i107"></TD>
	<TD id="i108"></TD>
	<TD id="i109"></TD>
	</TR>
    </TABLE>

    <TABLE><TR>
    <TD>縮小</TD><TD><input id="zoom-slider" type="range"></TD><TD>拡大</TD>
    </TR></TABLE>

    <canvas id="CANVAS_PIC"></canvas>

</body> 

</html>

(2) drawImage() メソッドのみを用いる方法
scale(x, y) メソッドを用いず、drawImage() メソッドの元画像と変換画像の比率を変えて、ズーム(拡大・縮小)を行っています。

<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>DRAWING TEST</title>

    <style>
	input { height:30px; }
    </style>

    <script>

	var cvs_pic ; var ctx_pic ;
	var cvs_wdt ; var cvs_hgt ; var img_h ; var img_w ; var zom_h ; var zom_w ;
	var x_b=0; var y_b=0; var x_c=0; var y_c=0; var x_e=0; var y_e=0; var x_dif=0; var y_dif=0; 
	var f_b ;
	var sld_chk ; var tmp_pic ;

	function draw_begin(){
	    cvs_wdt = 840 ; cvs_hgt = 600 ;
	    cvs_pic = document.getElementById('CANVAS_PIC') ; 
	    ctx_pic = cvs_pic.getContext("2d") ;
	    
	    sld_chk = document.getElementById("zoom-slider");
	    sld_chk.min = 0.01; sld_chk.max = 5; sld_chk.step = 'any';
	    sld_chk.value=1;

	    var image = new Image(); 
            image.src = "./sample.jpg" ;

	    image.onload = function () {
		img_h = image.height ; img_w = image.width ;

		cvs_hgt = Math.floor(img_h * cvs_wdt / img_w) ;
                cvs_pic.width = cvs_wdt ; cvs_pic.height = cvs_hgt ;

		document.getElementById('i101').innerHTML = img_w ;
		document.getElementById('i102').innerHTML = img_h ;
		document.getElementById('i103').innerHTML = cvs_wdt ;
		document.getElementById('i104').innerHTML = cvs_hgt ;

                ctx_pic.drawImage( image, 0 , 0 , cvs_wdt , cvs_hgt ) ;
		ctx_pic.strokeRect( 0 , 0 , cvs_wdt, cvs_hgt) ;

		tmp_pic = new Image ;
		tmp_pic.src = ctx_pic.canvas.toDataURL() ;

		sld_chk.addEventListener( "input", pict_scale , false ) ;
	    }
	}

	function pict_scale(event){
	    ctx_pic.clearRect(0, 0, cvs_pic.width, cvs_pic.height);
	    ctx_pic.globalAlpha = 1 ; ctx_pic.lineWidth = 1 ; ctx_pic.strokeStyle = "#000000" ;
	    var scale = event.target.value;
	    zom_h = Math.floor(scale*img_h) ; zom_w = Math.floor(scale*img_w) ;
	    var zoh_s = Math.floor((1-scale)*cvs_hgt/2) ;
	    var zow_s = Math.floor((1-scale)*cvs_wdt/2) ;

	    document.getElementById('i105').innerHTML = Math.floor(1000*scale)/1000 ;
	    document.getElementById('i106').innerHTML = zom_w ;
	    document.getElementById('i107').innerHTML = zom_h ;
	    document.getElementById('i108').innerHTML = zow_s ;
	    document.getElementById('i109').innerHTML = zoh_s ;

	    ctx_pic.drawImage(tmp_pic, 0 , 0 , img_w , img_h , zow_s , zoh_s , zom_w , zom_h ) ;
	    ctx_pic.strokeRect( 0 , 0 , cvs_pic.width , cvs_pic.height ) ;
	}

    </script>
</head>

<body onLoad="draw_begin()">

    <TABLE>
	<TR>
	<TD id="i001">画像幅</TD>
	<TD id="i002">画像高</TD>
	<TD id="i003">CVS幅</TD>
	<TD id="i004">CVS高</TD>
	<TD id="i005">倍率</TD>
	<TD id="i006">拡大幅</TD>
	<TD id="i007">拡大高</TD>
	<TD id="i008">始点X</TD>
	<TD id="i009">始点Y</TD>
	</TR>
	<TR>
	<TD id="i101"></TD>
	<TD id="i102"></TD>
	<TD id="i103"></TD>
	<TD id="i104"></TD>
	<TD id="i105"></TD>
	<TD id="i106"></TD>
	<TD id="i107"></TD>
	<TD id="i108"></TD>
	<TD id="i109"></TD>
	</TR>
    </TABLE>

    <TABLE><TR>
    <TD>縮小</TD><TD><input id="zoom-slider" type="range"></TD><TD>拡大</TD>
    </TR></TABLE>

    <canvas id="CANVAS_PIC"></canvas>

</body> 

</html>
まとめ

今回はズーム(拡大・縮小)の基礎的な方法について確認しました。次回は領域選択して拡大する様なトリミング処理について検討してみたいと思っています。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です