Android subpixel rendering - android

I have a line that should get thinner the longer it gets. The problem is, that you can clearly see a jump when it gets a pixel thinner. Is there a way to do subpixel rendering/antialiasing on Android?
canvas.drawRect() takes float values, but it's ignoring those. Here's the code:
#Override
protected void onDraw(Canvas canvas) {
float width = getMeasuredWidth() / (float) getMeasuredHeight() * getMinimumHeight();
float left = (getMeasuredWidth() - width) / 2.0f;
canvas.drawRect(left, 0, left + width, getMeasuredHeight(), paint);
super.onDraw(canvas);
}
The paint object has ANTI_ALIAS_FLAG enabled and contains a solid color.
This is the default line:
This is when it gets longer and thinner. It should have some anti aliasing on the sides, though to make the whole transition seems smoother.

This seems to do a better job:
#Override
protected void onDraw(Canvas canvas) {
float width = getMeasuredWidth() / (float) getMeasuredHeight() * getMinimumHeight();
float left = (getMeasuredWidth() - width) / 2.0f;
paint.setStrokeWidth(width * getResources().getDisplayMetrics().density);
canvas.drawLine(left, 0, left, getMeasuredHeight(), paint);
super.onDraw(canvas);
}

Related

Android PorterDuff: Why doesn't PorterDuff.Mode.SRC_IN cut out the rest of the source?

I'm trying to use the PorterDuff library to trim a canvas circle and rectangle to form a quarter of a square in my custom view for my app, I managed to get it to work but not fully because it trims the square out but keeps the rest of the circle in, I'm using the SRC_IN mode which seems like the right mode to use from looking at the android documentation for it but it's not working as expected, this is a snippet of the onDraw method in my custom view class:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = getWidth();
canvas.drawCircle(width / 2, height / 2, (width + height) / 10, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
Rect rectangle = new Rect(0, 0, width / 2, height / 2);
paint.setColor(Color.RED);
canvas.drawRect(rectangle, paint);
}
I'm drawing the circle in the center of the screen and then drawing the square in the top left of the screen and the PorterDuff mode should basically get the intersected part between the shapes but it just trims non-intersected square part out but doesn't do the same for the circle.
This is what it looks like:
I can't tell what i'm doing wrong here, hopefully someone can point it out.
the issue is with the source, the rectangle is the source in this context, draw the rectangle first then use PorterDuff.Mode.SRC_IN to cut the circle based on it.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int height = getHeight();
int width = getWidth();
Rect rectangle = new Rect(0, 0, width / 2, height / 2);
paint.setColor(Color.RED);
canvas.drawRect(rectangle, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawCircle(width / 2, height / 2, (width + height) / 10, paint);
}

Wrong Stroke width drawing circle on custom view Android

I am writing a custom view in android. I want to draw a circle that cover all width and height of my view. this is my code
private void init() {
bgpaint = new Paint();
bgpaint.setColor(bgColor);
bgpaint.setAntiAlias(true);
bgpaint.setStyle(Paint.Style.STROKE);
bgpaint.setStrokeWidth(strokeWidth);
rect = new RectF();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw background circle anyway
int strokeWidth = 50;
rect.set(strokeWidth, strokeWidth, getwidth()- strokeWidth,
getheight() - strokeWidth);
canvas.drawArc(rect, -90, 360, fill, bgpaint);
}
But when I run result will be like this
I want be like this
What the problem with my code?
Probem is in setting your rectangle's coordinates. You must set half of stroke width, not whole stroke width.
float left=strokeWidth / 2;
float top=strokeWidth / 2;
float right=minDimen - strokeWidth/ 2;
float bottom=minDimen - strokeWidth / 2;
rect.set(left, top, right,bottom);
Because it comes to circle, I assume your width and height of your circleView are the same dimension. That is minDimen in my code. Because I use smaller dimension of those two.
final int minDimen = Math.min(width, height);
setMeasuredDimension(minDimen, minDimen);
(this must be called in onMeasure method)
Anyway, it's good method to set width and height in same dimensions.

Animation not fluent Android Wear

I'm starting with Android Wear and I want to make a circle animation, making grow.
I know how to do it, I think, but it's doing it very very slow, hope you can help me
I have this class variable
Paint mAnimation;
intialized on the method OnCreate
mAnimation = new Paint(Paint.ANTI_ALIAS_FLAG);
mAnimation.setStyle(Paint.Style.FILL);
mAnimation.setColor(Color.GREEN);
and on the OnDraw method I have
#Override
public void onDraw(Canvas canvas, Rect bounds) {
int width = bounds.width();
int height = bounds.height();
float centerX = width / 2f;
float centerY = height / 2f;
// Draw the background.
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
canvas.drawCircle(0, centerY, radiusPlus, mAnimation);
radiusPlus += 20;
}
The animation is "correct", but is very slow, like if was paused.
Thanks!!
EDIT
I found a example and now I finally found why. I didn't invalidate the view at the end of the OnDraw. Now It's working fine. Thanks.
#Override
public void onDraw(Canvas canvas, Rect bounds) {
int width = bounds.width();
int height = bounds.height();
float centerX = width / 2f;
float centerY = height / 2f;
// Draw the background.
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
canvas.drawCircle(0, centerY, radiusPlus, mAnimation);
radiusPlus += 5;
if (isVisible() && !isInAmbientMode()) {
invalidate();
}
}

How to clip a circular path inside a rectangle in Android

I've read over 20 questions/answers but I still can't get what I want. I want to cut a circle inside a rectangle as seen below:
Here is my code:
#Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setARGB(180, 0, 0, 0);
canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
Path circularPath = new Path();
circularPath.addCircle(getWidth() / 2, getHeight() / 2, radius, Path.Direction.CCW);
canvas.clipPath(circularPath, Region.Op.REPLACE);
canvas.drawColor(0x00000000);
}
My background (setARGB) displays correctly, however nothing is clipped. I've also tried different Op values other than REPLACE, forced software rasterization (as I've read on some Android versions clipPath doesn't support some of the Ops) by calling setLayerType(LAYER_TYPE_SOFTWARE, null); on constructor, but no avail. How do I achieve the desired effect?
Note: My minimum SDK version is 15, so I don't need to support anything lower than 4.0.
Use clipPath before drawRect.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = this.getWidth();
int height = this.getHeight();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPaint(mPaint);
float rectWidth = Utils.dpToPx(100.0f);
Path circularPath = new Path();
circularPath.addCircle(width / 2.0f, rectWidth / 2.0f, rectWidth / 3.0f, Path.Direction.CCW);
canvas.clipPath(circularPath, Region.Op.DIFFERENCE);
mPaint.setColor(Color.BLUE);
canvas.drawRect((width - rectWidth) / 2.0f, 0.0f, ((width - rectWidth) / 2.0f) + rectWidth, rectWidth, mPaint);
}
Try clipping your path in dispatchDraw():
#Override
protected void dispatchDraw(Canvas canvas)
{
canvas.clipPath(mClipPath, mRegion); // previously created path & region
super.dispatchDraw(canvas);
}
Remove the path clipping code from your onDraw method, and that should do it.
Edit:
When creating your path, make sure you do so only after a measure occurred, for example:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mClipPath.reset();
float radius = Math.min((float)getMeasuredWidth() / 2f, (float)getMeasuredHeight() / 2f) + 5;
mClipPath.addCircle((float)getMeasuredWidth() / 2f, (float)getMeasuredHeight() / 2f, radius, Path.Direction.CCW);
}

Strange behaviour of `drawTextOnPath()` with hardware accelration

In hardware accelerated custom View added in ScrollView or ListView both of the following code snippets produces same result: (please ignore best practises for a sec)
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// centering stuff
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
float size = 80;
float halfSize = size / 2f;
float left = centerX - halfSize;
float top = centerY - halfSize;
RectF oval = new RectF(left, top, left + size, top + size);
Path path = new Path();
path.addArc(oval, 160, 359);
Paint paint = new Paint();
paint.setTextSize(30);
paint.setStyle(Style.STROKE);
canvas.drawTextOnPath("Hello world", path, 0, 0, paint); //<--- line A
canvas.drawCircle(centerX, centerY, 10, paint); //<--- line B
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// centering stuff
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
float size = 80;
float halfSize = size / 2f;
float left = centerX - halfSize;
float top = centerY - halfSize;
RectF oval = new RectF(left, top, left + size, top + size);
Path path = new Path();
path.addArc(oval, 160, 359);
Paint paint = new Paint();
paint.setTextSize(30);
paint.setStyle(Style.STROKE);
canvas.drawCircle(centerX, centerY, 10, paint); //<--- line B
canvas.drawTextOnPath("Hello world", path, 0, 0, paint); //<--- line A
}
Same Result:
But with later code snippet, as soon as you scroll the ScrollView (I have invisible dummy View below so I can scroll) and helloworld touches ActionBar, something very intersting happens and you see something that intelligent humankind used to see in old Windows OS .
I know drawTextOnPath() is not supported in hardware accelration mode, but then why it works if you call it first?
drawTextOnPath() is supported by hardware acceleration after Android 4.1
This is mentioned officially here: https://code.google.com/p/android/issues/detail?id=37925
but the next comment seems to indicate your problem in a way so maybe a bug.
Of course for pre 4.1 just dont make it use HW accel - Set a software layer type on your View by calling View.setLayerType(View.LAYER_TYPE_SOFTWARE, null) and try to get a tradeoff on perf vs errors

Categories

Resources