Android BlurMaskFilter Issues - android

I'm creating a drawing app, and I want on of the brushes the user can draw with to have the following effect:
Currently, to achieve this effect, I'm drawing 5 separate Paths, each with the same X values but a small Y offset, and each with a BlurMaskFilter. Here's the code from the PaintBrush class:
public PaintBrush(int initColor, int initSize) {
strokeRadius = ((10 * initSize) + 20) / 2;
currentColor = initColor
setupPaths();
}
private void setupPaths(){
paths = new Path[5];
paints = new Paint[5];
for (int i = 0; i < 5; i++){
paths[i] = new Path();
Paint curPaint = new Paint();
curPaint.setColor(currentColor);
curPaint.setAntiAlias(true);
curPaint.setDither(true);
curPaint.setStyle(Paint.Style.STROKE);
curPaint.setStrokeJoin(Paint.Join.ROUND);
curPaint.setStrokeCap(Paint.Cap.ROUND);
paints[i] = curPaint;
}
Paint paint1 = paints[0];
paint1.setStrokeWidth(strokeRadius / 3);
paint1.setMaskFilter(new BlurMaskFilter(Math.max(strokeRadius / 5, 1), BlurMaskFilter.Blur.NORMAL));
Paint paint2 = paints[1];
paint2.setStrokeWidth(strokeRadius / 6);
paint2.setMaskFilter(new BlurMaskFilter(Math.max(strokeRadius / 7, 1), BlurMaskFilter.Blur.NORMAL));
Paint paint3 = paints[2];
paint3.setStrokeWidth(strokeRadius / 5);
paint3.setMaskFilter(new BlurMaskFilter(Math.max(strokeRadius / 7, 1), BlurMaskFilter.Blur.NORMAL));
Paint paint4 = paints[3];
paint4.setStrokeWidth(strokeRadius / 3);
paint4.setMaskFilter(new BlurMaskFilter(Math.max(strokeRadius / 4, 1), BlurMaskFilter.Blur.NORMAL));
Paint paint5 = paints[4];
paint5.setStrokeWidth(strokeRadius / 4);
paint5.setMaskFilter(new BlurMaskFilter(Math.max(strokeRadius / 4, 1), BlurMaskFilter.Blur.NORMAL));
}
public void drawBrush(Canvas canvas){
for (int i = 0; i < 5; i++) {
canvas.drawPath(paths[i], paints[i]);
}
}
public void startStroke(float x, float y){
paths[0].moveTo(x, y - (strokeRadius * 4 / 6));
paths[1].moveTo(x, y - (strokeRadius / 3));
paths[2].moveTo(x, y - (strokeRadius / 8));
paths[3].moveTo(x, y + (strokeRadius / 3));
paths[4].moveTo(x, y + (strokeRadius * 3 / 4));
prevX = x;
prevY = y;
}
public void moveStroke(float x, float y){
float dx = Math.abs(x - prevX);
float dy = Math.abs(y - prevY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
paths[0].quadTo(prevX, prevY - (strokeRadius * 4 / 6), (x + prevX) / 2, ((y + prevY) / 2) - (strokeRadius * 4 / 6));
paths[1].quadTo(prevX, prevY - (strokeRadius / 3), (x + prevX) / 2, ((y + prevY) / 2) - (strokeRadius / 3));
paths[2].quadTo(prevX, prevY - (strokeRadius / 8), (x + prevX) / 2, ((y + prevY) / 2) - (strokeRadius / 8));
paths[3].quadTo(prevX, prevY + (strokeRadius / 3), (x + prevX) / 2, ((y + prevY) / 2) + (strokeRadius / 3));
paths[4].quadTo(prevX, prevY + (strokeRadius * 3 / 4), (x + prevX) / 2, ((y + prevY) / 2) + (strokeRadius * 3 / 4));
}
prevX = x;
prevY = y;
}
public void endStroke(Canvas canvas){
drawBrush(canvas);
paths[0].reset();
paths[1].reset();
paths[2].reset();
paths[3].reset();
paths[4].reset();
}
And the code for the custom View:
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(drawingCanvas, 0, 0, canvasPaint);
currentBrush.drawBrush(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!activity.isPaused()) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
currentBrush.startStroke(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
currentBrush.moveStroke(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
currentBrush.endStroke(touchX, touchY, drawingCanvas);
break;
default:
return false;
}
invalidate();
}
return true;
}
I'm aware that BlurMaskFilter is not compatible with hardware accelerations, so in my main activity i have the following code:
drawingView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
This code works, however, with hardware acceleration disabled, my drawing activity took a massive (and unacceptable) performance hit; it draws with a extremely noticeable lag, especially as the paths get longer. My question is: is there a way I could increase the performance, without using hardware acceleration, enough to decrease the lag? If not, is there any way to emulate this same effect in ways that are compatible with hardware acceleration?

Keep a Bitmap of the size of the entire screen.
Keep a list of all points that the finger moved through. Have some minimal threshold for the distance from the previous touch point to the last one. You essentially break the drawing motion into a list of points.
Now, every time the finger moves, find the bounding rectangle of the area that was changed since the last touch event you handled, clear this rectangle, and redraw only the lines on the list that are in this rectangle with your drawPath.

You can try using the RasmView library: github.com/Raed-Mughaus/DrawingVie

Related

I want to display numbers from 0 to 100 clockwise in canvas

I want to print numbers from 0 to 100 clockwise on canvas it will work when we have values from 0 to 12 but once i changed the values or increased the it is stopped working in my case can anyone help me here?
private int[] mClockHours = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12};
private void drawNumeral(Canvas canvas) {
mPaint.setTextSize(fontSize);
for (int number : mClockHours) {
String tmp = String.valueOf(number);
mPaint.getTextBounds(tmp, 0, tmp.length(), mRect);
double angle = Math.PI / 6 * (number - 2);
Log.d("drawNumeral", "number: "+number);
Log.d("drawNumeral", "Math.PI: "+Math.PI);
Log.d("drawNumeral", "angle: "+angle);
Log.d("drawNumeral", "temp: "+tmp);
int x = (int) (width / 2 + Math.cos(angle) * mRadius - mRect.width() / 2);
int y = (int) (height / 2 + Math.sin(angle) * mRadius - mRect.height() / 2);
canvas.drawText(tmp, x, y, mPaint);
}
}
You need to invalidate() the view to see changes you made. Here is a good tutorial to understand how to draw on canvas.

Android How to draw triangles inside a hexagon?

First thing I have drawn the three lines of the hexagon width specific width then I have drawn the six points using this formula section = 2 * PI / NumberOfPoints. Here is the pic of lines I have drawn:
what I want is to draw a triangle between every two lines which will be 6 triangles. Here is the pic when I drew the triangles:
My problem is I want the triangle to be drawn between the lines, not above them which mean the triangle should not overlaps the line.
Here is how I drew the lines:
for (int i = 1; i <= 6; i++) {
float eX = (float) (x + radius * Math.cos(section * i));
float eY = (float) (y + radius * Math.sin(section * i));
linesPaint.setShader(new LinearGradient(x, y, eX, eY, Color.BLACK, Color.TRANSPARENT, Shader.TileMode.MIRROR));
canvas.drawLine(x, y, eX, eY, linesPaint);
}
and here is how I drew the triangles
for (int i = 1; i <= 6; i++) {
TriangleColor triangleColor = triangleColors.get(i - 1);
trianglesPaint.setShader(new LinearGradient(0, 0, 0, getHeight(), triangleColor.firstColor, triangleColor.secondColor, Shader.TileMode.REPEAT));
float x1 = (float) (x + radius * Math.cos(section * i));
float y1 = (float) (y + radius * Math.sin(section * i));
float x2 = (float) (x + radius * Math.cos(section * (i + 1)));
float y2 = (float) (y + radius * Math.sin(section * (i + 1)));
trianglesPath.moveTo(x, y);
trianglesPath.lineTo(x1, y1);
trianglesPath.lineTo(x2, y2);
trianglesPath.lineTo(x, y);
canvas.drawPath(trianglesPath, trianglesPaint);
trianglesPath.reset();
}
Can anyone help me with the formula? Thanks in advance.
I assume that you have lines with thickness 2d
To avoid overwriting these lines, you can shift the central point for every triangle by dstance d/sin(30) = 2*d in direction of bisector between two lines
Perhaps also you need to diminish radius (length of triangle side along the line)
newradius = radius - d*sqrt(3) - d * sqrt(3)/3
for (int i = 1; i <= 6; i++) {
cx = x + 2 * d * Math.cos(section * (i + 0.5));
cy = x + 2 * d * Math.sin(section * (i + 0.5));
float x1 = (float) (cx + newradius * Math.cos(section * i)); /
//and similar for other coordinates
trianglesPath.moveTo(cx, cy);
trianglesPath.lineTo(x1, y1);
...

Fill arrow head goes off in line in canvas Android

The arrow head with stroke working fine in canvas, but the filled arrow head is not drawing at the right position ,Here is my code for draw arrow.
private void drawArrow(Point startPoint, Point endPoint, Paint paint, Canvas mCanvas) {
Path mPath = new Path();
float deltaX = endPoint.x - startPoint.x;
float deltaY = endPoint.y - startPoint.y;
//float frac = (float) 0.1;
int ARROWHEAD_LENGTH = 15;
float sideZ = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
float frac = ARROWHEAD_LENGTH < sideZ ? ARROWHEAD_LENGTH / sideZ : 1.0f;
float point_x_1 = startPoint.x + (float) ((1 - frac) * deltaX + frac * deltaY);
float point_y_1 = startPoint.y + (float) ((1 - frac) * deltaY - frac * deltaX);
float point_x_2 = endPoint.x;
float point_y_2 = endPoint.y;
float point_x_3 = startPoint.x + (float) ((1 - frac) * deltaX - frac * deltaY);
float point_y_3 = startPoint.y + (float) ((1 - frac) * deltaY + frac * deltaX);
mPath.moveTo(point_x_1, point_y_1);
mPath.lineTo(point_x_2, point_y_2);
mPath.lineTo(point_x_3, point_y_3);
//mPath.lineTo(point_x_1, point_y_1);
//mPath.lineTo(point_x_1, point_y_1);
mCanvas.drawPath(mPath, paint);
invalidate();
}
to resolve this issue, I have placed the end line to the center of triangle.
You can do it with this formula :
float endLineX = (point_x_1 + point_x_2 + point_x_3) / 3;
float endLineY = (point_y_1 + point_y_2 + point_y_3) / 3;
VoilĂ 

Android View Invalidate optimization idea

I'm trying to make an animation on my android application.
I have a list of View I want to draw every 10 ms to have a smooth animations.
class CircularAnimationSector extends View{
private double scale;
private double circularPosition;
private double circularLength;
private double sectionRadiusRatio;
private double circularSpeed;
private int color;
private Paint paint = new Paint();
private ICircular iCircular;
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
Path path = new Path();
int width = this.getWidth();
// int height = this.getHeight();
Point center = new Point((int)(width / 2.0f), (int)(width / 2.0f));
int innerWidthRadius = (int) (this.iCircular.GetViewSize() / 2.0f);
float startRadius = (float)(innerWidthRadius * this.iCircular.GetStartRatio());
float endRadius = (float)(startRadius + this.scale * (this.sectionRadiusRatio - this.iCircular.GetStartRatio()) * innerWidthRadius);
float startAngle = (float)(this.circularPosition - this.circularLength / 2.0f);
float endAngle = (float)(this.circularPosition + this.circularLength / 2.0f);
Point p1 = new Point((int) (center.x + startRadius * Math.cos(startAngle)), (int) (center.y + startRadius * Math.sin(startAngle)));
Point p3 = new Point((int) (center.x + endRadius * Math.cos(endAngle)), (int) (center.y + endRadius * Math.sin(endAngle)));
// PATH DRAWING
path.moveTo((float) (p1.x), (float) (p1.y));
path.arcTo(new RectF(center.x - startRadius, center.y - startRadius, center.x + startRadius, center.y + startRadius), (float) (startAngle * 180 / Math.PI), (float) ((endAngle - startAngle) * 180 / Math.PI));
path.lineTo((float) (p3.x), (float) (p3.y));
path.arcTo(new RectF(center.x - endRadius, center.y - endRadius, center.x + endRadius, center.y + endRadius), (float) (endAngle * 180 / Math.PI), (float) ((startAngle - endAngle) * 180 / Math.PI));
path.lineTo((float) (p1.x), (float) (p1.y));
canvas.drawPath(path, this.paint);
this.circularPosition += this.circularSpeed;
}
}
But it seems that the animation is not as smooth as expected and the application lag a little.
Do you have any advice to optimize my animation ?
I just want to draw a view every 10 ms (for instance) and then upadte its position and draw it again around a circle.

How to redraw correctly angle on circle, which starts from current time, to the point affected on the circle?

I have a circle drawn on canvas. On top on this circle I draw an angle, which starts from part of the circle equal to current time.
Then onTouchEvent, the angle should be redrawn from start point (current time), to end point, based on a point on the circle.
The problem is with calculation dynamically sweep angle in method onTouchEvent.
I've tried different calculations, taken from different Stackoverflow posts / suggestions, and none did not work according to my expectation. The reaction of the angle, onTouchEvent, was always a bit unpredicted.
My code snippet:
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
int radius = getRadius();
int startAngle = getStartAngle();
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, this.bkgPaint);
this.arcRect = new RectF((getWidth() / 2) - radius, (getHeight() / 2) - radius, (getWidth() / 2) + radius, (getHeight() / 2) + radius);
canvas.drawArc(this.arcRect, startAngle, sweepAngle, true, arcPaint);
}
#Override
public boolean onTouchEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_MOVE) {
sweepAngle = (int) ((360.0D + Math.toDegrees(Math.atan2(e.getX() - 360.0D, 360.0D - e.getY()))) % 360.0D);
invalidate();
}
return true;
}
private int getStartAngle() {
Calendar cal = Calendar.getInstance();
int minutes = cal.get(Calendar.HOUR_OF_DAY) * 60 + cal.get(Calendar.MINUTE);
if (minutes > 720) {
minutes -= 720;
}
int angle = minutes / 2;
return (angle += 270) % 360;
}
private int getRadius() {
return ((80 * getWidth()) / 100) / 2;
}
I've used similar calculations in my color picker. It's open source, you can find sources here.
In your case I would start with calculating end angle, like this:
#Override
public boolean onTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = (int) e.getX();
int y = (int) e.getY();
int cx = x - getWidth() / 2;
int cy = y - getHeight() / 2;
endAngle = (float) (Math.toDegrees(Math.atan2(cy, cx)) + 360f) % 360f;
invalidate();
return true;
}
return super.onTouchEvent(e);
}
Adding 360 degrees with modulo is required. After this operation you'll have 0 degrees on the right side of the circle, 90 at the bottom, 180 on the left and 270 at the top.
Now your sweep angle will be endAngle - startAngle. And that's it!

Categories

Resources