Weird! Fatal signal 11 when drawing outside screen bounds - android

I get a
Fatal signal 11 (SIGSEGV) code=1
whenever I draw a filled polygon that crosses the screen bounds. This happens on OS 4.3 on a Samsung Note 10.1. What could cause this? I assume the OS should be able to clip polygons.
The offending code:
public SplashStarShape(Context context, int x, int y, int size) {
super(context);
paint = new Paint();
star = new Path();
star.setFillType(Path.FillType.WINDING);
paint.setAntiAlias(true);
paint.setStrokeWidth(DS.scale(10));
paint.setColor(EslColors.WHITE);
int k = 0;
for (int i = 0; i < 360; i += 36) {
int radius = ((k % 2) == 0) ? size * 2 : size - DS.scale(30);
k ++;
float px = (float)x + (float) (Math.cos(0.6 + (float)i * 3.1415926f * 2.0f / (float)360.0f) * (float)(radius/2.0f));
float py = (float)y + (float) (Math.sin(0.6 + (float)i * 3.1415926f * 2.0f / (float)360.0f) * (float)(radius/2.0f));
if (i == 0) {
star.moveTo(px, py);
} else {
star.lineTo(px, py);
}
}
star.close();
this.invalidate();
}
#Override
protected void onDraw (Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(star, paint);
}
If I reduce the radius so that the star fits on screen, there is no crash. If the tentacles stick outside the screen, I get the fatal error. Very strange!

Related

SurfaceView only update UI once

I was trying to learn how to use SurfaceView, since I need to use it in a future project. I read that if I need to update the UI quickly, it is preferable to use SurfaceView with the help of a thread.
The purpose of my code is to be able to print on a bitmap, the pattern of a 'B',here is the code:
public void testTransition(Canvas canvas) {
if (canvas != null) {
canvas.drawColor(Color.BLACK);
Log.i("Thread", "Running...");
for (int y = 0; y < 8; y++) {
if (test[y] == 1) {
canvas.drawBitmap(ledBitMap, (x - diameterLeds) / 2, diameterLeds * y, paint);
}
}
canvas.drawColor(Color.BLACK);
delay(1000);
for (int y=0; y<8; y++){
if(test[y + 8] == 1)
canvas.drawBitmap(ledBitMap, (x - diameterLeds) / 2,diameterLeds * y, paint);
}
canvas.drawColor(Color.BLACK);
//ledThread.delay(1000);
for (int y=0; y<8; y++){
if(test[y + 16] == 1)
canvas.drawBitmap(ledBitMap, (x- diameterLeds) / 2,diameterLeds * y, paint);
}
canvas.drawColor(Color.BLACK);
//ledThread.delay(1000);
for (int y=0; y<8; y++){
if(test[y + 24] == 1)
canvas.drawBitmap(ledBitMap, (x - diameterLeds) / 2,diameterLeds * y, paint);
}
canvas.drawColor(Color.BLACK);
//ledThread.delay(1000);
for (int y=0; y<8; y++){
if(test[y + 32] == 1)
canvas.drawBitmap(ledBitMap, (x - diameterLeds) / 2,diameterLeds * y, paint);
}
//canvas.drawColor(Color.BLACK);
//ledThread.delay(600);
//delay(1);
}
}
#Override
public void run() {
long ticksPS = 1000 / 30;
long startTime;
long sleepTime;
while(isRunning){
Canvas canvas = null;
startTime = System.currentTimeMillis();
try {
canvas = ledView.getHolder().lockCanvas();
synchronized (ledView.getHolder()){
testTransition(canvas);
}
}finally {
if(canvas != null){
ledView.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
The variable test[] is a matrix of 40 elements ( 0 or 1)
private static int test[] = {1,1,1,1,1,1,1,1, 1,0,0,1,0,0,0,1, 1,0,0,1,0,0,0,1, 1,0,0,1,0,0,0,1, 0,1,1,0,1,1,1,0};
My goal is to read this arrangement and simulate that 'on or off' of the pattern of the letter B. But when I execute the code, the activity stays a few seconds with the blank screen and after that the circles appear painted on the bitmap but does not alternate the sequence, although the thread seems to be running. Only the last 8 elements of the test array are drawn
With a SurfaceView and lockCanvas(), the screen is not able to update until after you call unlockCanvasAndPost(). (I think you were expecting the screen to update midway through testTransition().)
The solution is to implement a state machine, that dirties the SurfaceView based on a timer (for example), and advances the LED display one row on each iteration.

How tro draw fading path

How can I draw Path with fading (opacity or thicknes) line? Something like this.
I know there is LinearGradient shader for Paint, but it won't bend along the Path.
One possible solution might be to get points along the Path and just draw it by myself through the segments`. But I coouldn't find any method for that either.
I came up with the following code. The mos important thing is PathMeasure's getPosTan() method.
if (getGesturePath() != null) {
final short steps = 150;
final byte stepDistance = 5;
final byte maxTrailRadius = 15;
pathMeasure.setPath(getGesturePath(), false);
final float pathLength = pathMeasure.getLength();
for (short i = 1; i <= steps; i++) {
final float distance = pathLength - i * stepDistance;
if (distance >= 0) {
final float trailRadius = maxTrailRadius * (1 - (float) i / steps);
pathMeasure.getPosTan(distance, pathPos, null);
final float x = pathPos[0] + RandomUtils.nextFloat(0, 2 * trailRadius) - trailRadius;
final float y = pathPos[1] + RandomUtils.nextFloat(0, 2 * trailRadius) - trailRadius;
paint.setShader(new RadialGradient(
x,
y,
trailRadius > 0 ? trailRadius : Float.MIN_VALUE,
ColorUtils.setAlphaComponent(Color.GREEN, random.nextInt(0xff)),
Color.TRANSPARENT,
Shader.TileMode.CLAMP
));
canvas.drawCircle(x, y, trailRadius, paint);
}
}
}

Android Listener on canvas?

I just got started with Android and I was wondering how to use listeners in a canvas. Particularly as part of my project, the objective is for an event to occur when we drag click from one point to another. The concept is from a game called brainvita. The game need not be understood to answer my question. All I want to know is the simplest way to make a listener for the drag clicks from one point to another on the canvas?
Do I need to map the canvas to a grid and have multiple listeners? What is the simplest way?
Additionally I'm attaching the code of the game developed so far, just the basics that help in displaying the grid!
package com.project.android.brainvita;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class GameView extends View {
Paint paint = new Paint();
Paint paintF = new Paint();
Paint paintG = new Paint();
int width, height;
int MARGIN = 4;
int MARGIN_BIG = 10;
int sx = 2;
public GameView(Context context) {
super(context);
paint.setColor(Color.WHITE);
paintF.setColor(Color.rgb(40, 60, 204));
paintG.setColor(Color.rgb(240, 30, 20));
}
public void onDraw(Canvas canvas) {
// Draw external circle
canvas.drawCircle(width / 2, height / 2, (width - MARGIN) / 2, paintF);
// Calculate radius of small circles
int radius = (width - MARGIN_BIG*2) / 14;
// Draw grid
for (int j = -3; j <= 3; j++) {
if (j >= -1 && j <= 1)
for (int i = -3; i <= 3; i++) {
canvas.drawCircle(width / 2 + (2 * radius * i), height / 2
+ (2 * radius * j), radius - sx, paint);
}
else
for (int i = -1; i <= 1; i++) {
canvas.drawCircle(width / 2 + (2 * radius * i), height / 2
+ (2 * radius * j), radius - sx, paint);
}
}
}
protected void onSizeChanged(int w, int h, int ow, int oh) {
width = w;
height = h;
}
}
In android development, a canvas is used for drawing. A view is used for interacting with the user. There isn't a direct mechanism for a canvas to receive user input. This must be handled through a view.
You'll want to add an onTouchListener to the View that is hosting your canvas and use that listener to store state information about touches from the user.

Android - Why doesn't my axis draw?

The following is supposed to draw an axis in the middle of the screen. However, nothing appears. I am positive that is has to do with my Paths.
#Override
protected void onDraw(Canvas canvas) {
//Variables declared here temporarily for testing purposes
int canterX = getWidth() /2;
int centerY = getHeight() /2;
int radius = 150;
Path verticalAxis = new Path();
Path horizontalAxis = new Path();
drawAxis();
}
private void drawAxis(Canvas canvas) {
int axisLineThickness = 1;
int verticalEndX;
int verticalEndY;
int horizontalEndX;
int horizontalEndY;
Paint axisPaint = new Paint();
axisPaint.setColor(Color.WHITE);
axisPaint.setStrokeWidth(axisLineThickness);
double theta;
for(int i = 90; i < 360; i += 180) {
theta = toRadians(i);
verticalEndX = centerX + (int) ((cos(theta)) * radius);
verticalEndY = centerY + (int) ((sin(theta)) * radius);
verticalAxis.moveTo(centerX, centerY);
verticalAxis.lineTo(verticalEndX, verticalEndY);
}
canvas.drawPath(verticalAxis, axisColor);
for(int i = 90; i < 360; i += 180) {
theta = toRadians(i);
horizontalEndX = centerX + (int) ((cos(theta)) * radius);
horizontalEndY = centerY + (int) ((sin(theta)) * radius);
horizontalAxis.moveTo(centerX, centerY);
horizontalAxis.lineTo(verticalEndX, verticalEndY);
}
canvas.drawPath(horizontalAxis, axisColor);
}
I know I can make the axis draw if I add the following to the vertical and horizontal for loops respectively:
Vertical For Loop:
canvas.drawLine(centerX, centerY, verticalEndX, verticalEndY, paint);
Horizontal For Loop:
canvas.drawLine(centerX, centerY, horizontalEndX, horizontalEndY, paint);
But I don't want to solve the issue this way, I want to correct what is wrong with my paths. Can anyone tell me why the points aren't adding to my path correctly? The loop should only go through twice which creates a line for each side of the axis. Ie. One loop creates the top of the vertical axis and the second loop creates the bottom part.
How do I get my paths create that full line and then draw it outside of the loop?
Paint's default style appears to be FILL, so maybe just having a line in your path is confusing things. Try setting it to STROKE:
axisPaint.setStyle(Paint.Style.STROKE);
See Paint.Style

How do I antialias the clip boundary on Android's canvas?

I'm using Android's android.graphics.Canvas class to draw a ring. My onDraw method clips the canvas to make a hole for the inner circle, and then draws the full outer circle over the hole:
clip = new Path();
clip.addRect(outerCircle, Path.Direction.CW);
clip.addOval(innerCircle, Path.Direction.CCW);
canvas.save();
canvas.clipPath(clip);
canvas.drawOval(outerCircle, lightGrey);
canvas.restore();
The result is a ring with a pretty, anti-aliased outer edge and a jagged, ugly inner edge:
What can I do to antialias the inner edge?
I don't want to cheat by drawing a grey circle in the middle because the dialog is slightly transparent. (This transparency isn't as subtle on on other backgrounds.)
As far as I know, you can't antialias clip regions.
I'd suggest using bitmap masking instead. Render the the pink, white, and light gray foreground to one bitmap, render the outer/inner circle mask (the grayscale alpha channel) to another bitmap, and then use Paint.setXfermode to render the foreground bitmap with the mask as its alpha channel.
An example can be found in the ApiDemos source code here.
I know this is not a general answer, but in this particular case, you could draw arcs with a thick stroke width, instead of the circles + mask.
I too had this same problem. I tried using Bitmap masking (xFermode) to fix the Aliasing, but it was heavy.
So for API < 19, I used the Bitmap masking way and for API >= 19, I used Path.Op. Instead of clipping the path and then drawing the shape. I did a REVERSE_DIFFERENCE of the path and the shape(which is of type Path). You can perform operations on Path from API 19 and above.
Works perfectly for me!
you can try the following code:
public class GrowthView extends View {
private static final String TAG = "GrowthView";
private int bgColor = Color.parseColor("#33485d");
private int valColor = Color.parseColor("#ecb732");
private int[] scores = new int[]{0, 10, 80, 180, 800, 5000, 20000, 50000, 100000};
private Context mContext;
private float w;
private float h;
private Paint bgPaint;
private Paint growthPaint;
private Paint textPaint;
private Paint clipPaint;
private Path bgPath;
private Path bgClipPath;
private Path growthPath;
private int growthValue = 0;
private float bgFullAngle = 240.0f;
private float gapAngle = bgFullAngle / (scores.length - 1);
private float gapRadius = 21.5f;//实际为21px 略大半个像素避免path无法缝合error
private float outerRadius = 240.0f;
private float innerRadius = outerRadius - gapRadius * 2;
private RectF outerRecF;
private RectF innerRecF;
private RectF leftBoundRecF;
private RectF rightBoundRecF;
public GrowthView(Context context) {
this(context, null);
}
public GrowthView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GrowthView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
init();
}
private void init() {
Xfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.DARKEN);
bgPaint = new Paint();
bgPaint.setStyle(Paint.Style.FILL);
bgPaint.setColor(bgColor);
bgPaint.setStrokeWidth(0.1f);
bgPaint.setAntiAlias(true);
growthPaint = new Paint();
growthPaint.setStyle(Paint.Style.FILL_AND_STROKE);
growthPaint.setColor(valColor);
growthPaint.setStrokeWidth(1f);
growthPaint.setAntiAlias(true);
clipPaint = new Paint();
clipPaint.setStyle(Paint.Style.FILL);
clipPaint.setColor(Color.WHITE);
clipPaint.setStrokeWidth(.1f);
clipPaint.setAntiAlias(true);
clipPaint.setXfermode(xFermode);
textPaint = new Paint();
textPaint.setTextSize(96);//todo comfirm the textSize
textPaint.setStrokeWidth(1f);
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setColor(valColor);
bgPath = new Path();
growthPath = new Path();
//todo 暂定中心点为屏幕中心
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
w = metrics.widthPixels;
h = metrics.heightPixels;
outerRecF = new RectF(w / 2 - outerRadius, h / 2 - outerRadius, w / 2 + outerRadius, h / 2 + outerRadius);
innerRecF = new RectF(w / 2 - innerRadius, h / 2 - innerRadius, w / 2 + innerRadius, h / 2 + innerRadius);
rightBoundRecF = new RectF(w / 2 + (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 - gapRadius,
h / 2 + (innerRadius + gapRadius) / 2 - gapRadius,
w / 2 + (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 + gapRadius,
h / 2 + (innerRadius + gapRadius) / 2 + gapRadius);
leftBoundRecF = new RectF(w / 2 - (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 - gapRadius,
h / 2 + (innerRadius + gapRadius) / 2 - gapRadius,
w / 2 - (float) Math.pow(3, 0.5) * (innerRadius + gapRadius) / 2 + gapRadius,
h / 2 + (innerRadius + gapRadius) / 2 + gapRadius);
bgClipPath = new Path();
bgClipPath.arcTo(innerRecF, 150.0f, 359.9f, true);
bgClipPath.close();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//bg
float startAngle = 150.0f;
float endRecfFullAngle = 180.0f;
bgPath.arcTo(outerRecF, startAngle, bgFullAngle, true);
bgPath.arcTo(rightBoundRecF, 30.0f, endRecfFullAngle, true);
bgPath.arcTo(innerRecF, startAngle, bgFullAngle);
bgPath.arcTo(leftBoundRecF, -30.0f, endRecfFullAngle);
bgPath.rMoveTo(w / 2 - outerRadius * (float) Math.pow(3, 0.5) / 2, h / 2 + outerRadius / 2);
bgPath.setFillType(Path.FillType.WINDING);
bgPath.close();
//growth
if (getGrowthVal() != 0) {
float temp = getGrowthAngle(getGrowthVal());
growthPath.arcTo(outerRecF, startAngle, temp, true);
growthPath.arcTo(getDynamicRecF(getGrowthVal()), getDynamicOriginAngle(getGrowthVal()), endRecfFullAngle, true);
growthPath.arcTo(innerRecF, startAngle, temp);
growthPath.arcTo(leftBoundRecF, -30.0f, endRecfFullAngle);
growthPath.rMoveTo(w / 2 - outerRadius * (float) Math.pow(3, 0.5) / 2, h / 2 + outerRadius / 2);
growthPath.close();
}
canvas.drawText(formatVal(getGrowthVal()), w / 2, h / 2, textPaint);
canvas.clipPath(bgClipPath, Region.Op.DIFFERENCE);
canvas.drawPath(bgPath, bgPaint);
canvas.drawPath(growthPath, growthPaint);
canvas.drawPath(bgClipPath, clipPaint);
}
private float getDynamicOriginAngle(int growthVal) {
return growthVal <= 30 ? getGrowthAngle(growthVal) + 150 :
getGrowthAngle(growthVal) - 210;
}
private RectF getDynamicRecF(int growthVal) {
float dynamicAngle = getGrowthAngle(growthVal);
//动态圆心
float _w = w / 2 + (float) Math.sin(Math.toRadians(dynamicAngle - 120)) * (outerRadius - gapRadius);
float _y = h / 2 - (float) Math.sin(Math.toRadians(dynamicAngle - 30)) * (outerRadius - gapRadius);
return new RectF(_w - gapRadius, _y - gapRadius, _w + gapRadius, _y + gapRadius);
}
private int getGrowthVal() {
return this.growthValue;
}
public void setGrowthValue(int value) {
if (value < 0 || value > 100000) {
try {
throw new Exception("成长值不在范围内");
} catch (Exception e) {
Log.e(TAG, e.getMessage());
e.printStackTrace();
}
}
this.growthValue = value;
invalidate();
}
private float getGrowthAngle(int growthVal) {
return gapAngle * (getLevel(growthVal) - 1)
+ gapAngle * (growthVal - scores[getLevel(growthVal) - 1]) /
(scores[getLevel(growthVal)] - scores[getLevel(growthVal) - 1]);
}
private int getLevel(int score) {
return score < 0 ? -1 : score <= 10 ? 1 : score <= 80 ? 2 : score <= 180 ? 3 : score <= 800 ?
4 : score <= 5000 ? 5 : score <= 20000 ? 6 : score <= 50000 ? 7 : 8;
}
private String formatVal(int value) {
StringBuilder builder = new StringBuilder(String.valueOf(value));
return value < 1000 ? builder.toString() : builder.insert(builder.length() - 3, ',').toString();
}
}
Use the Xfermode Api with canvas.clipPath() may resolve this problem...
Result

Categories

Resources