お絵かきアプリの消しゴム機能の軌跡について(PorterDuff.Mode.)

1,625 views
Skip to first unread message

gami

unread,
Sep 6, 2013, 3:17:25 AM9/6/13
to android-g...@googlegroups.com
消しゴム機能として、描画されたpathは消す事(透明色で塗りつぶす)はできます。
そこで、消しゴム機能を使用中に軌跡(path)を表示したいのですが、その軌跡はpaintで透明色に設定しているにもかかわらず黒い軌跡で表示されます(pathの描画が終わると透明に戻る)。

その、原因がわからず困っております。どなたか解決策をご教授おねがいします。

下記コード一部

消しゴム
this.paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
this.paint.setColor(Color.TRANSPARENT);

消しゴム以外
this.paint.setXfermode(null);//初期化
this.paint.setColor(任意の色);
    
 @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.w("palette", "myCanvasView.onTouchEvent");
        float x = event.getX();
        float y = event.getY();
        
        EraserDiscrimination();//ここで消しゴムかペンかの判別
        
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN://タッチ
        
        
            start(x, y);
            invalidate(); // 再描画
            break;
        case MotionEvent.ACTION_MOVE://ドラッグ
            move(x, y);
            invalidate(); // 再描画
            break;
        case MotionEvent.ACTION_UP://離す
         end();
         invalidate(); // 再描画
        }
         return true;
    }

MORIHIRO

unread,
Sep 6, 2013, 9:58:18 AM9/6/13
to android-g...@googlegroups.com
肝心の描画処理のコードがないので推測になりますが、
SDKのFingerPaintをベースにしているのであれば、
Move中のパス描画は onDraw() でやっていて、
Up後のパス描画は touch_up() でやっています。
どちらも drawPath() ですけどそれぞれ描画対象が異なるので
結果が変わってきます。

http://tools.oesf.biz/android-4.3.0_r2.2/xref/development/samples/ApiDemos/src/com/example/android/apis/graphics/FingerPaint.java


2013年9月6日金曜日 16時17分25秒 UTC+9 gami:

gami

unread,
Sep 8, 2013, 7:55:30 PM9/8/13
to android-g...@googlegroups.com
MORIHIROさんpostありがとうございます。
描画対象が異なるがたぶんヒントだと思いました。

描画処理のコードが以下になります。
    
//タッチ開始イベント
    private void start(float x, float y) {
        path.reset();
        path.moveTo(x, y); // 軌跡の開始
        xPos = x;
        yPos = y;
    }
    //タッチ中イベント
    private void move(float x, float y) {
    // 移動座標
        float dx = Math.abs(x - xPos);
        float dy = Math.abs(y - yPos);

     // 移動量が閾値を超えた場合のみ処理
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            path.quadTo(xPos, yPos, (x + xPos) / 2, (y + yPos) / 2); // 二次ベジェ曲線で軌跡をセット
            xPos = x;
            yPos = y;
        }
    }
    // タッチ終了イベント
    private void end() {

        path.lineTo(xPos, yPos);
        
        canvas.drawPath(path, paint); // タッチ中の軌跡を描画
        
        path.reset(); // 軌跡をリセット

MORIHIRO

unread,
Sep 8, 2013, 11:33:57 PM9/8/13
to android-g...@googlegroups.com
gamiさんが問題とされているのはMove中のパス描画ですから、
注目して頂きたいのは onDraw() の方です。

FingerPaintの場合は以下のような理由で黒塗りになります。

  protected void onDraw(Canvas canvas) {
    // 1. 背景色(灰色)で塗りつぶし
    canvas.drawColor(0xFFAAAAAA);

    // 2. 描画中のBitmapイメージ
    canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

    // 3. Move中のパスはここで描画されるため、
    //    PorterDuff.Mode.CLEAR の場合、描画中のBitmapイメージだけでなく
    //    背景色として塗った色もクリアされてしまう
    canvas.drawPath(mPath, mPaint);
  }

2013年9月9日月曜日 8時55分30秒 UTC+9 gami:

gami

unread,
Sep 9, 2013, 1:46:39 AM9/9/13
to android-g...@googlegroups.com
MORIHIROさん、何度もありがとうございます。
ここで、説明不足でしたが、ImageViewに任意の画像をセットし、その上に透明なCanvasを重ねて、以下にもその画像に落書きしているかのようにしています。

    //    PorterDuff.Mode.CLEAR の場合、描画中のBitmapイメージだけでなく
    //    背景色として塗った色もクリアされてしまう

つまり、Canvasの下にセットしているImageViewのbitmapの画像もクリアされ、activityの一番下(黒い画面)がmoveしている間一時的に
見えてしまい、黒い軌跡になっているって事でいいですか?

そうなると本当に解決策が思いつきませんね。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //canvas.drawColor(0x00000000);

    canvas.drawBitmap(bitmap, 0, 0, bitmapPaint);
canvas.drawPath(path, paint); // タッチ中の指の軌跡を描画

    }

MORIHIROさんのおかげでだいぶ何が問題かわかってきました。感謝です。

On Friday, September 6, 2013 4:17:25 PM UTC+9, gami wrote:

MORIHIRO

unread,
Sep 9, 2013, 3:28:29 AM9/9/13
to android-g...@googlegroups.com
パフォーマンス改善が別途必要になりますが、、、
作業用のBitmapとCanvasを作っておいて、
  Bitmap bitmap2 = bitmap.copy(Bitmap.Config.ARGB_8888, true);
  Canvas canvas2 = new Canvas(bitmap2);

以下のように作業用Bitmap上でパスの描画を実施してから
実際に描画してやればいいんじゃないんでしょうか。
  protected void onDraw(Canvas canvas) {
    canvas2.drawBitmap(bitmap, 0, 0, bitmapPaint);
    canvas2.drawPath(path, paint);
    canvas.drawBitmap(bitmap2, 0, 0, bitmapPaint);
  }

イマドキの端末なら問題ないのかもしれませんけど、
全てのMoveイベントで invalidate() するのはちょっと重いかもしれません。


2013年9月9日月曜日 14時46分39秒 UTC+9 gami:

gami

unread,
Sep 9, 2013, 4:56:20 AM9/9/13
to android-g...@googlegroups.com
MORIHIROさん!!素晴らしいです!!なるほどと思いました。
たしかに考え方は単純にってことですね。反省です。

パフォーマンスうんぬんは確かに考えることが多くありますが、思い通りの動きになりました。
PorterDuff.Mode.CLEARのそもそもの動き、canvasを二つ使う方法、すべてが知識になりました。
長い間お付き合いしていただきありがとうございました!

On Friday, September 6, 2013 4:17:25 PM UTC+9, gami wrote:
Reply all
Reply to author
Forward
0 new messages