Sibainu Relax Room

愛犬の柴犬とともに過ごす部屋

Canvas.drawBitmap について

周りを見渡しても桜が咲いているのはこの木だけか。不思議だなという顔をしている柴犬です。

概要

カメラの撮影した画像をピンチ操作で広げたり回したりしていろいろ試してみました。

規則性がありそうでないようなよくわかりませんので調べてみました。

結果、規則性は分かりませんでした。

人間の操作感覚に合うように行列計算しているんだと理解しました。

1冊だけでは理解の助けにはならないので買い足しました。

WEBのみでは断片的で覚えにくいので最初に購入した Kotlin の本です。

Canvas.drawBitmap

Canvas.drawBitmap は引数により7パターンもあります。

このなかで使うのが一番上の引数のものです。引数に Matrix を持ち何やら得体のしれないものです。

Canvas.drawBitmap
メソッド
内容
戻り値
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
指定された行列を使用してビットマップを描画します。
void
drawBitmap(int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, Paint paint)
このメソッドは API レベル 21 で非推奨となりました。ハードウェアアクセラレーションを使用した canvas を使用すると、このメソッドが呼び出されるたびにカラーバッファの内容の内部コピーが必要になります。ビットマップを使用すると、このコピーが回避され、アプリケーションはピクセルデータの寿命とコピーをより明示的に制御できるようになります。
void
drawBitmap(int[] colors, int offset, int stride, int x, int y, int width, int height, boolean hasAlpha, Paint paint)
このメソッドはAPIレベル21で非推奨となりました。ハードウェアアクセラレーションキャンバスで使用する場合、このメソッドが呼び出されるたびにカラーバッファの内容の内部コピーが必要になります。ビットマップを使用すると、このコピーが回避され、アプリケーションでピクセルデータの寿命とコピーをより明示的に制御できるようになります。
void
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
指定されたビットマップを描画し、宛先の矩形を埋めるように自動的に拡大/縮小します。
void
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
指定されたビットマップを描画し、自動的に拡大/縮小して、コピー先の矩形を埋めます。
void
drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
指定されたビットマップを、その上/左隅を (x,y) にして、指定されたペイントを使用して、現在の行列で変換して描画します。
void
drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)
メッシュを通してビットマップを描画し、メッシュの頂点がビットマップに均等に配置されます。
void
 
計 7要素
 

Graphics.Matrix

Matrix の公式HPの説明は次のURLにあります。Matrix には opengl.Matrix など複数あり間違えないようにしなければいけません。

JAVA
https://developer.android.com/reference/android/graphics/Matrix

KOTLIN
https://developer.android.com/reference/kotlin/androidx/compose/ui/graphics/Matrix

コンストラクタは次のようです。

Matrix() 

次の表はメッソドをまとめたものです。

Graphics.Matrixのメソッド
メソッド
内容
戻り値
dump(PrintWriter pw)
行列を人間が読めるように短縮した文字列を、与えられたストリームにダンプします。
final void
equals(Object obj)
obj が行列であり、その値が我々の値と等しい場合に真を返します。
boolean
getValues(float[] values)
行列から 9 個の値を配列にコピーします。
void
hashCode()
オブジェクトのハッシュコード値を返す。
int
invert(Matrix inverse)
この行列が反転可能であれば true を返し、inverse が NULL でなければ inverse をこの行列の逆行列に設定します。
boolean
isAffine()
この行列がAffineであるかどうかを取得します。
boolean
isIdentity()
行列が恒等式である場合は true を返します。
boolean
mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount)
src で指定した 2 次元点の配列にこの行列を適用し、変換後の点を dst で指定した点の配列に書き込みます。
void
mapPoints(float[] dst, float[] src)
src で指定された2次元点の配列にこの行列を適用し、変換後の点を dst で指定された点の配列に書き込みます。
void
mapPoints(float[] pts)
この行列を2次元点の配列に適用し、変換後の点を配列に書き戻します。
void
mapRadius(float radius)
この行列によって写像された後の円の平均半径を返します。
float
mapRect(RectF rect)
この行列を矩形に適用し、変換後の矩形を矩形に書き戻します。
boolean
mapRect(RectF dst, RectF src)
この行列を src の矩形に適用し、変換後の矩形を dst に書き出します。
boolean
mapVectors(float[] vecs)
この行列を 2D ベクトルの配列に適用し、変換後のベクトルを配列に書き戻します。
void
mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount)
src で指定された2次元ベクトルの配列にこの行列を適用し、変換後のベクトルを dst で指定されたベクトルの配列に書き込みます。
void
mapVectors(float[] dst, float[] src)
src で指定された2次元ベクトルの配列にこの行列を適用し、変換後のベクトルを dst で指定されたベクトルの配列に書き込みます。
void
postConcat(Matrix other)
行列を指定された行列でpostConcatします。
M’ = other * M
boolean
postRotate(float degrees, float px, float py)
指定された回転を持つ行列をpostConcatします。
M’ = R(degrees, px, py) * M
boolean
postRotate(float degrees)
指定された回転を持つ行列を連結します。
boolean
postScale(float sx, float sy, float px, float py)
指定されたスケールを持つ行列を連結します。
M’ = S(sx, sy, px, py) * M
boolean
postScale(float sx, float sy)
指定された縮尺で行列を後方一致させます。
boolean
postSkew(float kx, float ky)
行列を指定された傾きで後方一致させます。
boolean
postSkew(float kx, float ky, float px, float py)
行列を指定された傾きで後方一致させます。
M’ = K(kx, ky, px, py) * M
boolean
postTranslate(float dx, float dy)
行列を指定された平行移動量で後方一致させます。
M’ = T(dx, dy) * M
boolean
preConcat(Matrix other)
行列を指定された行列でpreConcatを行います。
M’ = other * M
boolean
preRotate(float degrees)
指定された回転を持つ行列を事前連結します。
boolean
preRotate(float degrees, float px, float py)
指定された回転を持つ行列を前方一致させます。
M’ = M * R(degrees, px, py)
boolean
preScale(float sx, float sy)
行列を指定されたスケールで平行移動します。
boolean
preScale(float sx, float sy, float px, float py)
行列を指定されたスケールで平行移動します。
M’ = M * S(sx, sy, px, py)
boolean
preSkew(float kx, float ky)
行列を指定された傾きで平行移動します。
boolean
preSkew(float kx, float ky, float px, float py)
行列を指定された傾きで平行移動します。
M’ = M * K(kx, ky, px, py)
boolean
preTranslate(float dx, float dy)
指定された平行移動量を持つ行列を事前変換します。
M’ = M * T(dx, dy)
boolean
rectStaysRect()
矩形を別の矩形にマップする場合は true を返します。
boolean
reset()
行列を identity に設定します。
void
set(Matrix src)
(deep) src の行列をこの行列にコピーします。
void
setConcat(Matrix a, Matrix b)
指定された 2 つの行列を連結したものに行列を設定し、true を返します。
boolean
setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount)
指定された src の点が指定された dst の点に写るように行列を設定します。
boolean
setRectToRect(RectF src, RectF dst, Matrix.ScaleToFit stf)
入力矩形を出力矩形に写像する scale と translate の値を行列に設定し、その結果が表現できる場合に true を返します。
boolean
setRotate(float degrees, float px, float py)
(px,py)を支点として、指定された度数だけ回転するように行列を設定します。
void
setRotate(float degrees)
指定された度数だけ (0,0) を中心に回転するように行列を設定します。
void
setScale(float sx, float sy)
行列を sx と sy で拡大縮小するように設定します。
void
setScale(float sx, float sy, float px, float py)
行列を (px, py) を支点として sx と sy で拡大縮小するように設定します。
void
setSinCos(float sinValue, float cosValue, float px, float py)
(px,py)を支点として、行列を指定されたサイン値およびコサイン値で回転するように設定します。
void
setSinCos(float sinValue, float cosValue)
行列を指定されたsinとcosin値で回転するように設定します。
void
setSkew(float kx, float ky)
行列が sx と sy だけ傾くように設定します。
void
setSkew(float kx, float ky, float px, float py)
(px,py)を支点として、行列を sx と sy で傾けるように設定します。
void
setTranslate(float dx, float dy)
行列を (dx, dy) で平行移動するように設定します。
void
setValues(float[] values)
9個の値を配列から行列にコピーします。
void
toShortString() /toString()
オブジェクトの文字列表現を返します。
String
 
計 50要素
 

detector.getScaleFactor

ScaleGestureDetector.SimpleOnScaleGestureListener の onScale の中で次のようなコードを使っています。

ピンチ操作開始時
_focusx = detector.getFocusX();
_focusy = detector.getFocusY();

ピンチ操作中
_lastscalefactor = detector.getScaleFactor();

相似で拡大縮尺移動
drawmatrix.postScale(_lastscalefactor,
                     _lastscalefactor,
                     _focusx,
                     _focusy);

公式HPの説明は次のURLにあります。

https://developer.android.com/reference/android/view/ScaleGestureDetector

前のスケール イベントから現在のイベントまでのスケール係数を返します。 スケール係数は、getCurrentSpan() / getPreviousSpan() で与えられるとあります。

getCurrentSpan() は「焦点を通る進行中のジェスチャを形成する各ポインター間の平均距離(pixels)を返します。」とあります。

getPreviousSpan() は「焦点を通る進行中のジェスチャを形成する各ポインター間の以前の平均距離(pixels)を返します。」とあります。

ですので、式の意味はピンチ操作開始時の焦点の XY 座標(_focusx, _focusy)とスケール係数を基にS(sx, sy, px, py) 行列計算して Matrix を得るということになります。S(sx, sy, px, py) の中身は分かりませんでしたが、人間の操作感覚に合った行列計算をさせているようです。

Matrix.postTranslate

GestureDetector.SimpleOnGestureListener の onScroll の中で次のようなコードを使っています。

onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)

drawmatrix.postTranslate(-distanceX, -distanceY);

なぜ -distanceX、-distanceY とマイナスが付くのか不思議に思いました。 distanceX、distanceY ではいけないのか。

考えてみました。キャンバス自体を右に動かす場合は座標系を右に動かすので例えば原点は右に動くのでデバイスから見ると原点までの距離がマイナスになる。右へ指を動かすことはマイナス方向であると言えそうである。キャンバスが固定の場合は右へ描画を動かすことは原点までの距離がプラスにならなければならないから方向が逆になる。

間違いかもしれませんが、スクロールの決め事はキャンバスを動かすことが基礎にあるので、キャンバスの上にあるものを動かすシンメトリックはマイナスにすると理解しました。

今週初めの寒さで開花が遅れていた桜が満開になりました。綺麗でした。

今回はここまでとします。