My application takes an image from the camera, saves it and then displays it on an ImageView, but the next step is to place a circle on top of the displayed image when the user touches the screen, and then save the "modified image".
Kinda like a image editor if you wish, problem is I do not know where to begin with the image editing. I tried this
#Override
public boolean onTouch(View v, MotionEvent event) {
circleView.setVisibility(View.VISIBLE);
circleView.setX(event.getX()-125);
circleView.setY(event.getY()-125);
try{
Bitmap bitmap = Bitmap.createBitmap(relativeLayout.getWidth(),relativeLayout.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
v.draw(canvas);
mImageView.setImageBitmap(bitmap);
FileOutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory());
bitmap.compress(Bitmap.CompressFormat.PNG,100,output);
output.close();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
return true;
}//ENDOF onTouch
What can I do to save the image?
It'd be helpful if you included a bit more information about what libraries and language you're using. From the #override I'll assume this is java on android?
As for how to create a circle - there are many techniques you could use and there are probably more than a few libraries that you can use to do this. However, we can keep it pretty simple by using functions on the Bitmap object's interface, namely getPixels and setPixels.
What you need to do is grab a rectangle of pixels in to pre-allocated buffer (using getPixels), then draw your circle in to this buffer and then write the buffer back using 'setPixels'.
Here's a simple (although not exactly efficient) method for drawing a circle in the buffer you'd get from 'getPixels' in javaish pseudocode (untested):
//Return the distance between the point 'x1, y1' and 'x2, y2'
float distance(float x1, float y1, float x2, float y2)
{
float dx = x2 - x1;
float dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
//draw a circle in the buffer of pixels contained in 'int [] pixels'
//at position 'cx, cy' with the given radius and colour.
void drawCircle(int [] pixels, int stride, int height, float cx, float cy, float radius, int colour)
{
for (int y = 0; y < height; ++y)
for (int x = 0; x < stride; ++x)
{
if (distance((float)x, (float)y, cx, cy) < radius)
pixels[x + y * stride] = colour;
}
}
This just asks the question, for each pixel, 'is the point 'x,y' inside the circle given by 'cx, cy, radius'?' and if it is, it draw a pixel.
More efficient approaches might include a scanline rasteriser that steps through the left and right sides of the circle, removing the need to do a costly 'distance' calculation for each pixel.
However, this 'implicit surface' approach is quite flexible and you can achieve a lot of effects with it. Other options might be to copy a pre made circle bitmap instead of creating your own on the fly.
You could also blend the 'colour' based on the fractional value of 'distance - radius' to achieve anti-aliasing.
Related
I am developing an application for technical drawing and I need to add a few tools to draw lines, circles, rectangles and corners. Now I can draw lines and free hand drawing but I cannot draw circles, rectangles and corners. I found in lot of websites how to draw it but static, I mean, draw the shape in the position you pre-set or the position where you touch but I need to know how to draw, for example, a circle in the position I touch and make it bigger than I separate my fingers. I hope you understand what I mean.
You can have two variables x and y, then every time you touch the screen set x and y to that value, while drawing draw the circle with coordinates x and y.
If you are drawing and you just want to keep a painted circle, you can paint the circle and add it inside your canvas on x and y, then the next time you touch the screen a new circle will be painted on x and y and the old one will remain painted.
Are you using Canvas ? if so you can find out how to do this here (Canvas documentation) and here (Bitmap documentation). Depending on your situation you can create a new Bitmap and assign it to Canvas then draw on the Canvasand inside your bitmap you will have your desired circles and other shapes, on the next drawing frame, draw new shapes and the changes will remain.
Edit: In order to have dynamic radius follow this logic, when you touch the screen, set x and y to that point (the center of the circle), while moving the finger on the screen, calculate the radius comparing to x and y, when lifting your finger apply the drawing on the bitmap as told above.
Some code:
public boolean onTouchEvent(MotionEvent e)
{
switch (e.getAction())
{
case MotionEvent.ACTION_DOWN:
x = (int) event.getX();
y = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
//If I'm not wrong this is how to calculate radius,
//I'm at work and can't test this now, you can use your way
int distanceX = (int) event.getX() -x;
int distanceY = (int) event.getY() -y;
radius = sqrt(distanceX *distanceX + distanceY *distanceY);
break;
case MotionEvent.ACTION_UP:
//Draw circle inside your bitmap here
//This is like a flag to notify the program that no new shape is being drawn
x = -1;
break;
}
public void draw(Canvas canvas)
{
canvas.drawBitmap(myBitmap, 0, 0, null);
//A new shape is being drawn
if (x != -1)
//In here you place a switch to decide which shape you are drawing
//for this example i assume circle
canvas.drawCircle(radius, x, y, paint);
}
When you are lifting your finger the new circle should be painted on your bitmap so you don't have to add extra code for each new circle.
Edit2: I will add more code with the Bitmap and Canvas method i described.
Bitmap myBitmap;
Canvas myCanvas;
//Constructor
public myActivity(Bundle bundle) //or whatever your constructor is
{
myBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
myCanvas = new Canvas(myBitmap);
}
Now anything you draw on "myCanvas" will be applied to "myBitmap", when ACTION_UP activates draw circle on "myCanvas" which is drawn on the draw function.
case MotionEvent.ACTION_UP:
myCanvas.drawCircle(radius, x, y, paint);
x = -1;
break;
I am trying to understand the basics of moving objects on the screen etc. I have a bitmap and I am moving that bitmap on the screen. How can I keep the bitmap inside the rectangle and still move it. I would like to put the bitmap in a rectangle as that will help in collision detection with other objects. Below is my code so far.
Thanks
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.yellow_ball);
x1 = 0;
y1 = 100;
#Override
protected void onDraw(Canvas canvas) {
if(x1 < canvas.getWidth()){
x1 += 5;
}
else{
x1 = 0;
}
canvas.drawBitmap(bitmap1, x1, y1, null);
}
You have the basic idea.
First, decide where you want to move the object to.
Then compare the new X coordinate to the rectangle's left and right bounds,
and if the new X coordinate is outside of the rectangle's bounds, reset the coordinate to the bound that it is exceeding.
Then, do the same for the Y coordinate.
Finally, move your bitmap to the adjusted X and Y coordinates.
I wrote code to do this a while back, see https://github.com/rfreedman/android-constrained-drag-and-drop-view for an example.
I have a problem with my implementation of a hexagonal grid based game. Because the game requires a 91-cell hex board, like my simple board below, I wanted the user to be able to scale the board with zoom/pinch, and move it around with panning. However, none of my implementations of scaling and moving allow me to simultaneously have:
1) Consistent bounds, allowing me to be able to drag and drop pieces onto the Drawables that make up each cell.
2) Consistent relative positioning, allowing the cells to remain next to each other in the same configuration below.
3) Smooth scaling and panning, allowing the game to run quickly and have a zoom/pinch experience similar to that of other apps.
Some things I've tried (All being done using an Activity -> SurfaceView -> Thread, like in the LunarLander example) :
Drawing to a bitmap, scaling the bitmap with a matrix, translating the canvas. Handles points 2 and 3, but I can't figure out how to keep the bounds of the cell consistent. A HexCell is a class that holds a Drawable of a single cell, and the two dimensional array, board, contains HexCells.
public void drawBoard(Canvas c, int posX, int posY, float scaleFactor, float pivotPointX, float pivotPointY, boolean firstDraw) {
for(int i = 0; i < board.size(); i++) {
for(int j = 0; j < board.get(i).size(); j++) {
board.get(i).get(j).draw(bitmapCanvas);
}
}
if(firstDraw) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float scale;
if(canvasWidth < canvasHeight) {
scale = ((float) canvasWidth) / width;
}
else {
scale = ((float) canvasHeight) / height;
}
Matrix matrix = new Matrix();
// Resize the bit map
matrix.postScale(scale, scale);
// Recreate the new Bitmap
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
c.drawBitmap(bitmap, matrix, null);
}
c.save();
c.translate(posX, posY);
Matrix matrix = new Matrix();
matrix.postScale(scaleFactor, scaleFactor, pivotPointX, pivotPointY);
c.drawBitmap(bitmap, matrix, null);
c.restore();
}
Modifying the bounds of the Drawable using a Matrix. This updates the bounds for point 1, and with some tweaking I think I could get 2 and 3 but I'm unsure how to make the scaling stop being "sticky", meaning not as smooth as the first method. In addition, when scaling the cells by a large amount, some of them become different sizes and start moving out of position relative to the other cells.
public void drawBoard(Canvas c, int posX, int posY, float scaleFactor, float pivotPointX, float pivotPointY, boolean firstDraw) {
for(int i = 0; i < board.size(); i++) {
for(int j = 0; j < board.get(i).size(); j++) {
Rect bounds = board.get(i).get(j).getBounds();
RectF boundsF = new RectF(bounds.left, bounds.top, bounds.right, bounds.bottom);
matrix.mapRect(boundsF);
bounds = new Rect((int)boundsF.left, (int)boundsF.top, (int)boundsF.right, (int)boundsF.bottom);
board.get(i).get(j).setBounds(bounds);
board.get(i).get(j).draw(c);
}
}
}
Directly modifying the bounds of the Drawable. This updates the bounds for point 1, but lacks in points 2 and 3. In addition, because I'm not using the matrix postScale with pivotPoints, the cells stay centered where they are and become smaller/larger without moving to stay next to each other.
public void resize(int dx, int dy, float scaleFactor) {
xPos += dx;
yPos += dy;
width *= scaleFactor;
height *= scaleFactor;
cell.setBounds(xPos, yPos, xPos + width, yPos + height);
}
What should I do? How can I update bounds while scaling and moving so that I can eventually place pieces on the board? Should I scrap my desire for scaling and panning, and instead use a GridView or something like that to implement the board?
Edit:
Working on this some more, I've determined that option 1 is the best way to go. It is much faster, and keeps the cells in a consistent formation. I found out that if you invert the transformation applied to the canvas and apply that to the coordinates from a touch event, you can get back to the original bounds of the cells, and therefore select them appropriately.
However, I'm having trouble accurately inverting the transformation.
x /= scaleFactor;
y /= scaleFactor;
x -= posX + pivotPointX;
y -= posY + pivotPointY;
is not a working inversion of:
canvas.save();
canvas.translate(posX, posY);
canvas.scale(scaleFactor, scaleFactor, pivotPointX, pivotPointY);
Does anyone know how to invert it appropriately?
In the drawBoard() method I did:
canvas.save();
canvas.translate(posX, posY);
canvas.scale(scaleFactor, scaleFactor, pivotPointX, pivotPointY);
canvas.getMatrix(canvasMatrix); // Save the matrix that has the transformations so we can invert it
for(int i = 0; i < board.size(); i++) {
for(int j = 0; j < board.get(i).size(); j++) {
board.get(i).get(j).draw(c);
}
}
canvas.restore();
In the onTouchEvent(MotionEvent ev) method in the View I did:
float x = ev.getX();
float y = ev.getY();
float[] pts = {x, y};
Matrix canvasMatrix = boardThread.getCanvasMatrix(); // get the matrix with the transformations
Matrix invertMatrix = new Matrix();
canvasMatrix.invert(invertMatrix); // invert the matrix
invertMatrix.mapPoints(pts); // map the inversion to the points x and y
boardThread.selectHex((int)pts[0], (int)pts[1]);
This does the trick! Now if anyone has any suggestions to avoid using the deprecated canvas.getMatrix() method, that would be great :)
I am trying to make an application in which i can zoom in and draw some thing on the image
i have used following link to make a zoomable image view which is working fine
zoom functionality for images
now the problem is that i dont know how to scale and translate whatever i am drawing on my canvas to the image size when it is zoomed out.
following is the onDraw() function.
i have recorded the touch points added them into a list and in this onDraw() function of my view i am retrieving those points but these points are relative to screen .
i have to translate and scale it according to the image operation(in case of zoom in/out of the image) .
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mMarkers!=null)
{
for(Marker m : mMarkers) {
// draw the marker
Log.v("IP","position"+ m.x+" "+ m.y);
Paint paint=new Paint();
paint.setARGB(255, 2, 255, 2);
canvas.drawText("O", m.x, m.y, paint);
if(x!=-1){
Log.v("IP","LINE"+x+" " + y+ " "+ m.x+" "+ m.y);
canvas.drawLine(x, y,m.x, m.y, paint);
}
x=m.x;
y=m.y;
}
x=y=-1;
}
}
Use this code
public boolean onTouchEvent(MotionEvent env) {
float x = env.getX() / mScaleFactor + rect.left;
float y = env.getY() / mScaleFactor + rect.top;
}
Please try this code
public boolean onTouchEvent(MotionEvent env) {
float x = env.getX() / mScaleFactor + rect.left;
float y = env.getY() / mScaleFactor + rect.top;
}
you can get 9 point array from the matrix which contain the relative x,y and scale factor in x and y use this to convert you current x,y (which is with respect to screen) to x,y on original image and reconvert it before drawing again on different zoom levels.
I want to create a live wallpaper, and I want to have the bottom background slide together with swiping homescreen pages, while another layer stays always on top of the background and under the app icons.
Is this possible and how can this be done?
You'll have to override public void onOffsetsChanged (float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset)
Using the value of xOffset, you can define a source rectangle that extracts part of your bitmap and draws that part on the screen.
This image should give you an understanding of how xOffset works:
Assuming that there are 5 homescreen pages,
If your picture is of size 960 x 800 (width x height) and if you want to draw a portion of size 480 x 800 every time, then you can define a source rectangle whose co-ordinates would be:
x1 = xOffset * (960 - 480); y1 = 0; x2 = x1 + 480, y2 = 800;
Then your destination rectangle would be the portion of the screen you want to draw onto.
You can then use public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint) method to draw the bitmap onto the screen.
I had used this technique a long time ago. I didn't check this in code before posting, and there may be alternatives (like using canvas.translate()). But hopefully this should help you get started. :)