package com.aviary.android.feather.widget; import it.sephiroth.android.library.imagezoom.ImageViewTouch; import android.content.Context; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.MotionEvent; import com.aviary.android.feather.library.graphics.drawable.IBitmapDrawable; // TODO: Auto-generated Javadoc /** * The Class ImageViewSpotDraw. */ public class ImageViewSpotDraw extends ImageViewTouch { /** * The Enum TouchMode. */ public static enum TouchMode { /** The IMAGE. */ IMAGE, /** The DRAW. */ DRAW }; /** * The listener interface for receiving onDraw events. The class that is interested in processing a onDraw event implements this * interface, and the object created with that class is registered with a component using the component's * <code>addOnDrawListener<code> method. When * the onDraw event occurs, that object's appropriate * method is invoked. * * @see OnDrawEvent */ public static interface OnDrawListener { /** * On draw start. * * @param points * the points * @param radius * the radius */ void onDrawStart( float points[], int radius ); /** * On drawing. * * @param points * the points * @param radius * the radius */ void onDrawing( float points[], int radius ); /** * On draw end. */ void onDrawEnd(); }; /** The m paint. */ protected Paint mPaint; /** The m current scale. */ protected float mCurrentScale = 1; /** The m brush size. */ protected float mBrushSize = 30; /** The tmp path. */ protected Path tmpPath = new Path(); /** The m canvas. */ protected Canvas mCanvas; /** The m touch mode. */ protected TouchMode mTouchMode = TouchMode.DRAW; /** The m y. */ protected float mX, mY; protected float mStartX, mStartY; /** The m identity matrix. */ protected Matrix mIdentityMatrix = new Matrix(); /** The m inverted matrix. */ protected Matrix mInvertedMatrix = new Matrix(); /** The Constant TOUCH_TOLERANCE. */ protected static final float TOUCH_TOLERANCE = 2; /** The m draw listener. */ private OnDrawListener mDrawListener; /** draw restriction **/ private double mRestiction = 0; /** * Instantiates a new image view spot draw. * * @param context * the context * @param attrs * the attrs */ public ImageViewSpotDraw( Context context, AttributeSet attrs ) { super( context, attrs ); } /** * Sets the on draw start listener. * * @param listener * the new on draw start listener */ public void setOnDrawStartListener( OnDrawListener listener ) { mDrawListener = listener; } /* * (non-Javadoc) * * @see it.sephiroth.android.library.imagezoom.ImageViewTouch#init() */ @Override protected void init() { super.init(); mPaint = new Paint( Paint.ANTI_ALIAS_FLAG ); mPaint.setFilterBitmap( false ); mPaint.setDither( true ); mPaint.setColor( 0x66FFFFCC ); mPaint.setStyle( Paint.Style.STROKE ); mPaint.setStrokeCap( Paint.Cap.ROUND ); tmpPath = new Path(); } public void setDrawLimit( double value ) { mRestiction = value; } /** * Sets the brush size. * * @param value * the new brush size */ public void setBrushSize( float value ) { mBrushSize = value; if ( mPaint != null ) { mPaint.setStrokeWidth( mBrushSize ); } } /** * Gets the draw mode. * * @return the draw mode */ public TouchMode getDrawMode() { return mTouchMode; } /** * Sets the draw mode. * * @param mode * the new draw mode */ public void setDrawMode( TouchMode mode ) { if ( mode != mTouchMode ) { mTouchMode = mode; onDrawModeChanged(); } } /** * On draw mode changed. */ protected void onDrawModeChanged() { if ( mTouchMode == TouchMode.DRAW ) { Matrix m1 = new Matrix( getImageMatrix() ); mInvertedMatrix.reset(); float[] v1 = getMatrixValues( m1 ); m1.invert( m1 ); float[] v2 = getMatrixValues( m1 ); mInvertedMatrix.postTranslate( -v1[Matrix.MTRANS_X], -v1[Matrix.MTRANS_Y] ); mInvertedMatrix.postScale( v2[Matrix.MSCALE_X], v2[Matrix.MSCALE_Y] ); mCanvas.setMatrix( mInvertedMatrix ); mCurrentScale = getScale(); mPaint.setStrokeWidth( mBrushSize ); } } /** * Gets the paint. * * @return the paint */ public Paint getPaint() { return mPaint; } /** * Sets the paint. * * @param paint * the new paint */ public void setPaint( Paint paint ) { mPaint.set( paint ); } /* * (non-Javadoc) * * @see android.widget.ImageView#onDraw(android.graphics.Canvas) */ @Override protected void onDraw( Canvas canvas ) { super.onDraw( canvas ); canvas.drawPath( tmpPath, mPaint ); } public RectF getImageRect() { if ( getDrawable() != null ) { return new RectF( 0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight() ); } else { return null; } } /* * (non-Javadoc) * * @see it.sephiroth.android.library.imagezoom.ImageViewTouch#onBitmapChanged(android.graphics.drawable.Drawable) */ @Override protected void onBitmapChanged( Drawable drawable ) { super.onBitmapChanged( drawable ); if ( drawable != null && ( drawable instanceof IBitmapDrawable ) ) { mCanvas = new Canvas(); mCanvas.drawColor( 0 ); onDrawModeChanged(); } } /** The m moved. */ private boolean mMoved = false; /** * Touch_start. * * @param x * the x * @param y * the y */ private void touch_start( float x, float y ) { mMoved = false; tmpPath.reset(); tmpPath.moveTo( x, y ); mX = x; mY = y; mStartX = x; mStartY = y; if ( mDrawListener != null ) { float mappedPoints[] = new float[2]; mappedPoints[0] = x; mappedPoints[1] = y; mInvertedMatrix.mapPoints( mappedPoints ); tmpPath.lineTo( x + .1f, y ); mDrawListener.onDrawStart( mappedPoints, (int) ( mBrushSize / mCurrentScale ) ); } } /** * Touch_move. * * @param x * the x * @param y * the y */ private void touch_move( float x, float y ) { float dx = Math.abs( x - mX ); float dy = Math.abs( y - mY ); if ( dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE ) { if ( !mMoved ) { tmpPath.setLastPoint( mX, mY ); } mMoved = true; if ( mRestiction > 0 ) { double r = Math.sqrt( Math.pow( x - mStartX, 2 ) + Math.pow( y - mStartY, 2 ) ); double theta = Math.atan2( y - mStartY, x - mStartX ); final float w = getWidth(); final float h = getHeight(); double scale = ( mRestiction / mCurrentScale ) / (double) ( w + h ) / ( mBrushSize / mCurrentScale ); double rNew = Math.log( r * scale + 1 ) / scale; x = (float) ( mStartX + rNew * Math.cos( theta ) ); y = (float) ( mStartY + rNew * Math.sin( theta ) ); } tmpPath.quadTo( mX, mY, ( x + mX ) / 2, ( y + mY ) / 2 ); mX = x; mY = y; } if ( mDrawListener != null ) { float mappedPoints[] = new float[2]; mappedPoints[0] = x; mappedPoints[1] = y; mInvertedMatrix.mapPoints( mappedPoints ); mDrawListener.onDrawing( mappedPoints, (int) ( mBrushSize / mCurrentScale ) ); } } /** * Touch_up. */ private void touch_up() { tmpPath.reset(); if ( mDrawListener != null ) { mDrawListener.onDrawEnd(); } } /** * Gets the matrix values. * * @param m * the m * @return the matrix values */ public static float[] getMatrixValues( Matrix m ) { float[] values = new float[9]; m.getValues( values ); return values; } /* * (non-Javadoc) * * @see it.sephiroth.android.library.imagezoom.ImageViewTouch#onTouchEvent(android.view.MotionEvent) */ @Override public boolean onTouchEvent( MotionEvent event ) { if ( mTouchMode == TouchMode.DRAW && event.getPointerCount() == 1 ) { float x = event.getX(); float y = event.getY(); switch ( event.getAction() ) { case MotionEvent.ACTION_DOWN: touch_start( x, y ); invalidate(); break; case MotionEvent.ACTION_MOVE: touch_move( x, y ); invalidate(); break; case MotionEvent.ACTION_UP: touch_up(); invalidate(); break; } return true; } else { if ( mTouchMode == TouchMode.IMAGE ) return super.onTouchEvent( event ); else return false; } } }