I want to save up memory for my game and I wanted to ask you because I couldn't find anything and last time that I asked something here I got a good answer. Can i flip the bitmap inside eclipse so i could save on memory for sprites? All the tutorials that i found were about rotating and not flipping. The tutorials for flipping a bitmap were only for open Gl or something like that. Please help me.
I've been looking up for tutorials in google but i gave up at page 5. Can anyone help me?
Does anyone have a good tutorial?
By the way I am using a canvas.
Thanks!
I get a force close everytime I try to run it... can u figure it out? here is my code:
Matrix flipHorizontalMatrix = new Matrix();
flipHorizontalMatrix.setScale(-1,1);
flipHorizontalMatrix.postTranslate(0, canvas.getHeight()-arrowL.getHeight());
canvas.drawBitmap(arrowL, flipHorizontalMatrix, null);
I want the arrow to be at the bottom right corner.
Since you're using Canvas, why not try the drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) method. Use a Matrix that flips the x coordinates.
You can do something like this:
Matrix flipHorizontalMatrix = new Matrix();
flipHorizontalMatrix.setScale(-1,1);
flipHorizontalMatrix.postTranslate(myBitmap.getWidth(),0);
canvas.drawBitmap(myBitmap, flipHorizontalMatrix, myPaint);
Thanks,check out this code it may be useful for you to rotate an Bitmap image..
Here i have a taken a aquarium fish example,it should be moved from left to right and flipped and continue moving from right to left and viceversa..
here is the code for you..
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
fish = BitmapFactory.decodeResource(getResources(), R.drawable.fish);
v = new OurView(this);
public class OurView extends SurfaceView implements Runnable {
Thread t = null;
SurfaceHolder holder;
boolean isitOK = false;
String Flag = "right";
Bitmap rotatedBitmap=null;
Matrix rotateRight = new Matrix();
Matrix rotateLeft = new Matrix();
Bitmap rSprite=null;
Bitmap lSprite=null;
public OurView(Context context) {
super(context);
holder = getHolder();
rotateLeft.setScale(-1, 1);
rSprite = Bitmap.createBitmap(fish, 0, 0,
fish.getWidth(), fish.getHeight(), rotateRight, true);
lSprite = Bitmap.createBitmap(fish, 0, 0,
fish.getWidth(), fish.getHeight(), rotateLeft, true);
}
#Override
public void run() {
// TODO Auto-generated method stub
while (isitOK == true) {
if (!holder.getSurface().isValid()) {
continue;
}
Canvas canvas = holder.lockCanvas();
canvas.drawBitmap(bg, 0, 0, null);
if(Flag == "right")
canvas.drawBitmap(lSprite, x, y, null);
if(Flag == "left")
canvas.drawBitmap(fish, x, y, null);
if (Flag == "right" && x <= 60) {
x++;
if (x == 60) {
Flag = "left";
// canvas.drawBitmap(rSprite, 0, fish.getWidth(), null);
canvas.drawBitmap(fish, x, y, null);
}
}
if (Flag == "left" && x >= 0) {
x--;
if (x == 0) {
Flag = "right";
canvas.drawBitmap(fish, x, y, null);
}
}
holder.unlockCanvasAndPost(canvas);
}
}
Related
I am trying to make a simple feature with WebRTC on an Android (mobile) app.
The app right now can make a simple video call: connect two devices with each other and allow them to hear and see.
What I am trying to achieve is some live drawing during the call. To put it simply: User1 call User2, the call gets connected, then User1 click on a draw button which will freeze the video frame and allow him to draw on this frozen frame. Obviously, User2 should see this drawing happening live on his phone.
Right now I can freeze the frame (by calling videoCapture.stopCapture()) and draw on it with a custom SurfaceViewRenderer. The problem is that User2 does NOT see the drawing, only the frozen frame.
First I tried to create a new videotrack containing the drawing canvas AND the frozen frame to draw on but I couldn't succeed.
When creating a videotrack with peerConnectionFactory.createVideoTrack("ARDAMSv1_" + rand, videoSource);
I am supposed to specify the video source of the track but the source can only be a VideoSource and this VideoSource can only be created with a VideoCapturer which is directly linked to a device camera (without any drawing on it of course). This explains why User2 is not seeing any drawing on his device.
My question here is: how can I create a VideoCapturer which can stream the camera stream (frozen frame) AND a canvas with the drawing on it?
So I tried to implements my own VideoCapturer to either:
1) Capture a View (for example the layout containing the drawing and the frozen frame) and stream it for the VideoSource
OR 2)Capture the camera view but also add the drawing to the frame before streaming it.
I couldn't make any of this work because I have no idea how to manipulate the I420Frame object to draw on it and return it with the right callback.
Maybe I am totally wrong with this approach and need to do something completely different, I am open to any suggestion.
PS: I am using Android API 25 with WebRTC 1.0.19742. I do NOT want to use any paid third party SDK/lib.
Does anyone have a clue how to proceed to achieve a simple WebRTC live drawing from one android app to another android app?
We came back to that feature a couple weeks ago and I managed to find a way.
I extended my own CameraCapturer class to get the hold on the camera frame before the rendering. I then created my own CanvasView to be able to draw on it.
From there what I did was merging the two bitmap together (the camera view + my canvas with the drawing), then I draw it with OpenGL on the buffer and displayed it on the SurfaceView.
If someone is interested I could potentially post some code.
#Override
public void startCapture(int width, int height, int fps) {
Log.d("InitialsClass", "startCapture");
surTexture.stopListening();
cameraHeight = 480;
cameraWidth = 640;
int horizontalSpacing = 16;
int verticalSpacing = 20;
int x = horizontalSpacing;
int y = cameraHeight - verticalSpacing;
cameraBitmap = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
YuvFrame frame = new YuvFrame(null, PROCESSING_NONE, appContext);
surTexture.startListening(new VideoSink() {
#Override
public void onFrame(VideoFrame videoFrame) {
frame.fromVideoFrame(videoFrame, PROCESSING_NONE);
}
});
if (captureThread == null || !captureThread.isInterrupted()) {
captureThread = new Thread(() -> {
try {
if (matrix == null) {
matrix = new Matrix();
}
long start = System.nanoTime();
capturerObs.onCapturerStarted(true);
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
YuvConverter yuvConverter = new YuvConverter();
WindowManager windowManager = (WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
//The bitmap is drawn on the GPU at this point.
TextureBufferImpl buffer = new TextureBufferImpl(cameraWidth, cameraHeight - 3, VideoFrame.TextureBuffer.Type.RGB, textures[0], matrix, surTexture.getHandler(), yuvConverter, null);
Resources resources = appContext.getResources();
float scale = resources.getDisplayMetrics().density;
Log.d("InitialsClass before", "camera start capturer width- " + cameraWidth + " height- " + cameraHeight);
while (!Thread.currentThread().isInterrupted()) {
ByteBuffer gBuffer = frame.getByteBuffer();
if (gBuffer != null) {
Log.d("InitialsClass ", "gBuffer not null");
cameraBitmap.copyPixelsFromBuffer(gBuffer);
}
if (cameraBitmap != null) {
if (canvas == null) {
canvas = new Canvas();
}
if (appContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT)
rotationDegree = -90;
else {
assert windowManager != null;
if (windowManager.getDefaultDisplay().getRotation() == Surface.ROTATION_0) {
// clockwise
rotationDegree = 0;
} else if (windowManager.getDefaultDisplay().getRotation() == Surface.ROTATION_90) {
// anti-clockwise
rotationDegree = -180;
}
}
canvas.save(); //save the position of the canvas
canvas.rotate(rotationDegree, (cameraBitmap.getWidth() / 2), (cameraBitmap.getHeight() / 2)); //rotate the canvas
canvas.drawBitmap(cameraBitmap, 0, 0, null); //draw the image on the rotated canvas
canvas.restore(); // restore the canvas position.
matrix.setScale(-1, 1);
matrix.postTranslate(/*weakBitmap.get().getWidth()*/ cameraBitmap.getWidth(), 0);
matrix.setScale(1, -1);
matrix.postTranslate(0, /*weakBitmap.get().getHeight()*/ cameraBitmap.getHeight());
canvas.setMatrix(matrix);
if (textPaint == null) {
textPaint = new TextPaint();
}
textPaint.setColor(Color.WHITE);
textPaint.setTypeface(Typeface.create(typeFace, Typeface.BOLD));
textPaint.setTextSize((int) (11 * scale));
if (textBounds == null) {
textBounds = new Rect();
}
textPaint.getTextBounds(userName, 0, userName.length(), textBounds);
textPaint.setTextAlign(Paint.Align.LEFT);
textPaint.setAntiAlias(true);
canvas.drawText(userName, x, y, textPaint);
if (paint == null) {
paint = new Paint();
}
if (isLocalCandidate) {
paint.setColor(Color.GREEN);
} else {
paint.setColor(Color.TRANSPARENT);
}
paint.setStrokeWidth(8);
paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(0, 8, cameraWidth - 8, cameraHeight - 8, paint);
if (surTexture != null && surTexture.getHandler() != null && surTexture.getHandler().getLooper().getThread().isAlive()) {
surTexture.getHandler().post(() -> {
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, /*weakBitmap.get()*/ cameraBitmap, 0);
//We transfer it to the VideoFrame
VideoFrame.I420Buffer i420Buf = yuvConverter.convert(buffer);
long frameTime = System.nanoTime() - start;
VideoFrame videoFrame = new VideoFrame(i420Buf, 0, frameTime);
capturerObs.onFrameCaptured(videoFrame);
});
}
}
Thread.sleep(100);
}
} catch (InterruptedException ex) {
Log.d("InitialsClass camera", ex.toString());
Thread.currentThread().interrupt();
return;
}
});
}
captureThread.start();
}
#Anael. Check it out.
I was working on a similar application, so I share the draw on webrtc stream part:
What you need to do is get the stream to canvas.
Then have drawing application to edit on the canvas, I look at a William Malone project. (if you want to import pictures, make then transparent!)
Finally (what you missed I guess) is stream from canvas as you would with any WebRTC.
A little demo I cooked up specially for you here (local webrtc, see log).
PS: I use getDisplayMedia, not userMedia as my webcam is kaput...
I've edited this code to move the Rect instantiations out of the onDraw method. I've tested it on several devices.
public class BallBouncesActivity extends Activity {
BallBounces ball;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ball = new BallBounces(this);
setContentView(ball);
}
}
class BallBounces extends SurfaceView implements SurfaceHolder.Callback {
GameThread thread;
int screenW; //Device's screen width.
int screenH; //Devices's screen height.
int ballX; //Ball x position.
int ballY; //Ball y position.
int initialY ;
float dY; //Ball vertical speed.
int ballW;
int ballH;
int bgrW;
int bgrH;
int angle;
int bgrScroll;
int dBgrY; //Background scroll speed.
float acc;
Bitmap ball, bgr, bgrReverse;
boolean reverseBackroundFirst;
boolean ballFingerMove;
Rect toRect1, fromRect1;
Rect toRect2, fromRect2;
//Measure frames per second.
long now;
int framesCount=0;
int framesCountAvg=0;
long framesTimer=0;
Paint fpsPaint=new Paint();
//Frame speed
long timeNow;
long timePrev = 0;
long timePrevFrame = 0;
long timeDelta;
public BallBounces(Context context) {
super(context);
ball = BitmapFactory.decodeResource(getResources(), R.drawable.rocket); //Load a ball image.
bgr = BitmapFactory.decodeResource(getResources(), R.drawable.sky_bgr); //Load a background.
ballW = ball.getWidth();
ballH = ball.getHeight();
toRect1 = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
fromRect1 = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
toRect2 = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
fromRect2 = new Rect(0, 0, bgr.getWidth(), bgr.getHeight());
//Create a flag for the onDraw method to alternate background with its mirror image.
reverseBackroundFirst = false;
//Initialise animation variables.
acc = 0.2f; //Acceleration
dY = 0; //vertical speed
initialY = 100; //Initial vertical position
angle = 0; //Start value for the rotation angle
bgrScroll = 0; //Background scroll position
dBgrY = 1; //Scrolling background speed
fpsPaint.setTextSize(30);
//Set thread
getHolder().addCallback(this);
setFocusable(true);
}
#Override
public void onSizeChanged (int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//This event-method provides the real dimensions of this custom view.
screenW = w;
screenH = h;
bgr = Bitmap.createScaledBitmap(bgr, w, h, true); //Scale background to fit the screen.
bgrW = bgr.getWidth();
bgrH = bgr.getHeight();
//Create a mirror image of the background (horizontal flip) - for a more circular background.
Matrix matrix = new Matrix(); //Like a frame or mould for an image.
matrix.setScale(-1, 1); //Horizontal mirror effect.
bgrReverse = Bitmap.createBitmap(bgr, 0, 0, bgrW, bgrH, matrix, true); //Create a new mirrored bitmap by applying the matrix.
ballX = (int) (screenW /2) - (ballW / 2) ; //Centre ball X into the centre of the screen.
ballY = -50; //Centre ball height above the screen.
}
//***************************************
//************* TOUCH *****************
//***************************************
#Override
public synchronized boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
ballX = (int) ev.getX() - ballW/2;
ballY = (int) ev.getY() - ballH/2;
ballFingerMove = true;
break;
}
case MotionEvent.ACTION_MOVE: {
ballX = (int) ev.getX() - ballW/2;
ballY = (int) ev.getY() - ballH/2;
break;
}
case MotionEvent.ACTION_UP:
ballFingerMove = false;
dY = 0;
break;
}
return true;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Draw scrolling background.
fromRect1.set(0, 0, bgrW - bgrScroll, bgrH);
toRect1.set(bgrScroll, 0, bgrW, bgrH);
fromRect2.set(bgrW - bgrScroll, 0, bgrW, bgrH);
toRect2.set(0, 0, bgrScroll, bgrH);
// Rect fromRect1 = new Rect(0, 0, bgrW - bgrScroll, bgrH);
// Rect toRect1 = new Rect(bgrScroll, 0, bgrW, bgrH);
//
// Rect fromRect2 = new Rect(bgrW - bgrScroll, 0, bgrW, bgrH);
// Rect toRect2 = new Rect(0, 0, bgrScroll, bgrH);
if (!reverseBackroundFirst) {
canvas.drawBitmap(bgr, fromRect1, toRect1, null);
canvas.drawBitmap(bgrReverse, fromRect2, toRect2, null);
}
else{
canvas.drawBitmap(bgr, fromRect2, toRect2, null);
canvas.drawBitmap(bgrReverse, fromRect1, toRect1, null);
}
//Next value for the background's position.
if ( (bgrScroll += dBgrY) >= bgrW) {
bgrScroll = 0;
reverseBackroundFirst = !reverseBackroundFirst;
}
//Compute roughly the ball's speed and location.
if (!ballFingerMove) {
ballY += (int) dY; //Increase or decrease vertical position.
if (ballY > (screenH - ballH)) {
dY=(-1)*dY; //Reverse speed when bottom hit.
}
dY+= acc; //Increase or decrease speed.
}
//Increase rotating angle
if (angle++ >360)
angle =0;
//DRAW BALL
//Rotate method one
/*
Matrix matrix = new Matrix();
matrix.postRotate(angle, (ballW / 2), (ballH / 2)); //Rotate it.
matrix.postTranslate(ballX, ballY); //Move it into x, y position.
canvas.drawBitmap(ball, matrix, null); //Draw the ball with applied matrix.
*/// Rotate method two
canvas.save(); //Save the position of the canvas matrix.
canvas.rotate(angle, ballX + (ballW / 2), ballY + (ballH / 2)); //Rotate the canvas matrix.
canvas.drawBitmap(ball, ballX, ballY, null); //Draw the ball by applying the canvas rotated matrix.
canvas.restore(); //Rotate the canvas matrix back to its saved position - only the ball bitmap was rotated not all canvas.
//*/
//Measure frame rate (unit: frames per second).
now=System.currentTimeMillis();
canvas.drawText(framesCountAvg+" fps", 40, 70, fpsPaint);
framesCount++;
if(now-framesTimer>1000) {
framesTimer=now;
framesCountAvg=framesCount;
framesCount=0;
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new GameThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class GameThread extends Thread {
private SurfaceHolder surfaceHolder;
private BallBounces gameView;
private boolean run = false;
public GameThread(SurfaceHolder surfaceHolder, BallBounces gameView) {
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
public void setRunning(boolean run) {
this.run = run;
}
public SurfaceHolder getSurfaceHolder() {
return surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (run) {
c = null;
//limit frame rate to max 60fps
timeNow = System.currentTimeMillis();
timeDelta = timeNow - timePrevFrame;
if ( timeDelta < 16) {
try {
Thread.sleep(16 - timeDelta);
}
catch(InterruptedException e) {
}
}
timePrevFrame = System.currentTimeMillis();
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
//call methods to draw and process next fame
gameView.onDraw(c);
}
} finally {
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
If you notice, there's code to measure the frame rate:
now=System.currentTimeMillis();
canvas.drawText(framesCountAvg+" fps", 40, 70, fpsPaint);
framesCount++;
if(now-framesTimer>1000) {
framesTimer=now;
framesCountAvg=framesCount;
framesCount=0;
}
I'm seeing that on both of my Galaxy Nexus devices, running Android 4.0 and 4.2, it's around 22-24fps. On my HTC Desire, running Android 2.2, it's more like 60fps.
You'll also notice that I'm not allocating anything in the onDraw method. I'm not creating new Paint objects either. I really don't see how this is running so, so, so much slower on the my Galaxy Nexus devices. There's a lot of stuttering and the ball moves very slowly.
Does anyone know whether there is a setting I can undo or a known issue with re-drawing on the Galaxy Nexus? This is happening to my Galaxy Nexus that's running 4.0 and the one that's running 4.2, so I'm not sure it's OS-specific. I have turned off the window and transition animations in Developer Options. It doesn't make a difference whether I force 2D acceleration.
Have you tested android:hardwareAccelerated="true|false" flag in application?
Android Dev Doc: http://developer.android.com/guide/topics/graphics/hardware-accel.html
You can add before setContentView:
if (android.os.Build.VERSION.SDK_INT >= 11) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
}
I've been thinking about this, as I'm still having performance issues on my Nexus 10 with surfaceview, like I said previously, I increased my performance a lot my removing the use of paint objects, it's a long shot as you are only using one, but you could try to remove the text drawing section from your onDraw() just to see if it makes any difference to your drawing speed.
Other than that, I think it's really a case of trying to pin down the problem.
I've noticed that even if I remove my onDraw and Logic updating methods completely from the equation, it's still taking sometimes up to 25ms just to lock and unlock/post the canvas! So it might be that the problem actually doesn't lie in onDraw method - give it a go, comment onDraw() out from your Run() method and see what speeds you get (Use logging to Logcat to see the figures, remember that ironically, displaying a frame / time count on the screen could affect the very thing you're measuring). :-)
What's your targetSdk set to?
If targetSdk is set to 8 (2.2), but you're running it on a 4.x device (15+), the 4.x device will run in compatibility mode, meaning that it will virtualize all the calls so that they return exactly the same as if they're running on a version 8 device.
This virtualization might explain some of the slowness. Try changing targetSdk to 17 (for your 4.2 device) and see if it makes a difference.
I has experienced the same thing on Kindle Fire, Sony Xperia Z and Samsung S4 (all with android 4.2).
The fix is: at App Manifest file remove "android:supportsRtl="true"".
Hope it will save your time. I spend 4 hours on tests and merging before i got it.
My image is bigger, but just a part of it have to be displayed on the screen. I have done this. My problem is that, the image has to be rotated by finger. The code below works as it should on a small image. But on my image it has a wrong behave, repositioning the image up and down and rotate it weirdly. How to make my image to rotate normally?, as a small image. Then I have to be able to get the color touched. I get an answer here, but I still have to work at it. But first, please tell me how could I solve the problem with rotation, and if you have ideas about the second issue, with getting the color touched I would like to hear them.
this is the image:
this is how it should appear on the screen:
XML Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ImageView android:id="#+id/imag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="#drawable/roata"
android:layout_alignParentBottom="true"
android:layout_marginBottom="-200dp"
android:scaleType="center"/>
</RelativeLayout>
Java code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
picture = (ImageView)findViewById(R.id.imag);
picture.setOnTouchListener(onTableTouched);
}
public android.view.View.OnTouchListener onTableTouched = new android.view.View.OnTouchListener()
{
public boolean onTouch(View v, MotionEvent evt)
{
double r = Math.atan2(evt.getX() - picture.getWidth() / 2, picture.getHeight() / 2 - evt.getY());
int rotation = (int) Math.toDegrees(r);
if (evt.getAction() == MotionEvent.ACTION_DOWN)
{
//
}
if (evt.getAction() == MotionEvent.ACTION_MOVE)
{
updateRotation(rotation);
}
if (evt.getAction() == MotionEvent.ACTION_UP)
{
//
}
return true;
}
};
private void updateRotation(double rot)
{
float newRot = new Float(rot);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roata);
Matrix matrix = new Matrix();
matrix.postRotate(newRot - 50);
Bitmap redrawnBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
picture.setImageBitmap(redrawnBitmap);
}
UPDATE:
Because I got nowhere with Bitmap and Matrix I tried to adjust the RotateAnimtaion() to get something as similar as I could. I think modifying the second param and the duration of the animation dynamic, we can get a similar result without changing the Y position. Now, the problem is that the image is crop, I think because of scaleType="center". I will post code and a screen shoot. Could this be improved?
Animation a = new RotateAnimation(0.0f, param,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
a.setFillEnabled(true);
a.setFillAfter(true);
a.setDuration(param);
picture.startAnimation(a);
just declare Bitmap as globally as:
private WeakReference<Bitmap> bitmap;
and use
bitmap =new WeakReference(BitmapFactory.decodeResource(getResources(), R.drawable.roata));
instead of
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roata);
I think the problem is , you are trying to redraw the same Bitmap in every updateRotation() call.
How about removing this line,
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roata);
from updateRotation(double rot) and have it declared globally , so that you need not create the same Bitmap again and again which is why you are getting the OutOfMemory.
Try this,
Bitmap bitmap=null;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
picture = (ImageView)findViewById(R.id.imag);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.roata);
picture.setOnTouchListener(onTableTouched);
}
public android.view.View.OnTouchListener onTableTouched = new android.view.View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent evt) {
double r = Math.atan2(evt.getX() - picture.getWidth() / 2, picture.getHeight() / 2 - evt.getY());
int rotation = (int) Math.toDegrees(r);
if (evt.getAction() == MotionEvent.ACTION_DOWN) {
//
}
if (evt.getAction() == MotionEvent.ACTION_MOVE) {
updateRotation(rotation);
}
if (evt.getAction() == MotionEvent.ACTION_UP) {
//
}
return true;
}
};
private void updateRotation(double rot) {
float newRot = new Float(rot);
Matrix matrix = new Matrix();
matrix.postRotate(newRot - 50);
Bitmap redrawnBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
picture.setImageBitmap(redrawnBitmap);
}
My app is a basic drawing app. User can draw on a Canvas and save the image as a PNG. He can load the earlier drawn images and edit them.
I was able to do the first part. that is, the user can draw and save the image on the sdcard. I'm having trouble loading the saved png file on to the Canvas and drawing on it.
here is the run method in my SurfaceView class.
public void run() {
Canvas canvas = null;
while (running) {
try {
canvas = holder.lockCanvas(null);
synchronized (holder) {
if(mBitmap == null){
mBitmap = Bitmap.createBitmap (1, 1, Bitmap.Config.ARGB_8888);;
}
final Canvas c = new Canvas (mBitmap);
c.drawColor(Color.WHITE);
//pad.onDraw(canvas);
Paint p = new Paint();
p.setColor(Color.GRAY);
for(double x = 0.5;x < c.getWidth();x += 30) {
c.drawLine((float)x, 0, (float)x, c.getHeight(), p);
}
for(double y= 0.5;y < c.getHeight();y += 30) {
c.drawLine(0, (float)y, c.getWidth(), (float)y, p);
}
pad.onDraw(c);
canvas.drawBitmap (mBitmap, 0, 0, null);
}
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
}
I tried loading the png to the 'mBitmap', but it didn't work.
Any help appreciated.
thank you!
In your code you are not loading the image from sd card at all, is this intentional? This is how you open an image form SD card:
mBitmap = BitmapFactory.decodeFile("/sdcard/test.png");
In order to make a simple game, I used a template that draws a canvas with bitmaps like this:
private void doDraw(Canvas canvas) {
for (int i=0;i<8;i++)
for (int j=0;j<9;j++)
for (int k=0;k<7;k++) {
canvas.drawBitmap(mBits[allBits[i][j][k]], i*50 -k*7, j*50 -k*7, null); } }
(The canvas is defined in "run()" / the SurfaceView lives in a GameThread.)
My first question is how do I clear (or redraw) the whole canvas for a new layout?
Second, how can I update just a part of the screen?
// This is the routine that calls "doDraw":
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
updateGame();
doDraw(c); }
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c); } } } }
Draw transparent color with PorterDuff clear mode does the trick for what I wanted.
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
How do I clear (or redraw) the WHOLE canvas for a new layout (= try at the game) ?
Just call Canvas.drawColor(Color.BLACK), or whatever color you want to clear your Canvas with.
And: how can I update just a part of the screen ?
There is no such method that just update a "part of the screen" since Android OS is redrawing every pixel when updating the screen. But, when you're not clearing old drawings on your Canvas, the old drawings are still on the surface and that is probably one way to "update just a part" of the screen.
So, if you want to "update a part of the screen", just avoid calling Canvas.drawColor() method.
Found this in google groups and this worked for me..
Paint clearPaint = new Paint();
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(0, 0, width, height, clearPaint);
This removes drawings rectangles etc. while keeping set bitmap..
use the reset method of Path class
Path.reset();
I tried the answer of #mobistry:
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
But it doesn't worked for me.
The solution, for me, was:
canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);
Maybe some one has the same problem.
mBitmap.eraseColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);
please paste below code on surfaceview extend class constructor.............
constructor coding
SurfaceHolder holder = getHolder();
holder.addCallback(this);
SurfaceView sur = (SurfaceView)findViewById(R.id.surfaceview);
sur.setZOrderOnTop(true); // necessary
holder = sur.getHolder();
holder.setFormat(PixelFormat.TRANSPARENT);
xml coding
<com.welcome.panelview.PanelViewWelcomeScreen
android:id="#+id/one"
android:layout_width="600px"
android:layout_height="312px"
android:layout_gravity="center"
android:layout_marginTop="10px"
android:background="#drawable/welcome" />
try above code...
Here is the code of a minimal example showing that you always have to redraw every pixel of the Canvas at each frame.
This activity draw a new Bitmap every second on the SurfaceView, without clearing the screen before.
If you test it, you will see that the bitmap is not always written to the same buffer, and the screen will alternate between the two buffers.
I tested it on my phone (Nexus S, Android 2.3.3), and on the emulator (Android 2.2).
public class TestCanvas extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TestView(this));
}
}
class TestView extends SurfaceView implements SurfaceHolder.Callback {
private TestThread mThread;
private int mWidth;
private int mHeight;
private Bitmap mBitmap;
private SurfaceHolder mSurfaceHolder;
public TestView(Context context) {
super(context);
mThread = new TestThread();
mBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
mWidth = width;
mHeight = height;
mThread.start();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {/* Do nothing */}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread != null && mThread.isAlive())
mThread.interrupt();
}
class TestThread extends Thread {
#Override
public void run() {
while (!isInterrupted()) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
c.drawBitmap(mBitmap, (int) (Math.random() * mWidth), (int) (Math.random() * mHeight), null);
}
} finally {
if (c != null)
mSurfaceHolder.unlockCanvasAndPost(c);
}
try {
sleep(1000);
} catch (InterruptedException e) {
interrupt();
}
}
}
}
}
For me calling Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) or something similar would only work after I touch the screen. SO I would call the above line of code but the screen would only clear after I then touched the screen. So what worked for me was to call invalidate() followed by init() which is called at the time of creation to initialize the view.
private void init() {
setFocusable(true);
setFocusableInTouchMode(true);
setOnTouchListener(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPaths = new LinkedList<>();
addNewPath();
}
Erasing on Canvas in java android is similar erasing HTML Canvas via javascript with globalCompositeOperation. The logic was similar.
U will choose DST_OUT (Destination Out) logic.
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
Note: DST_OUT is more useful because it can erase 50% if the paint color have 50% alpha. So, to clear completely to transparent, the alpha of color must be 100%. Apply paint.setColor(Color.WHITE) is recommended. And make sure that the canvas image format was RGBA_8888.
After erased, go back to normal drawing with SRC_OVER (Source Over).
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
Update small area display literally will need to access graphic hardware, and it maybe not supported.
The most close for highest performance is using multi image layer.
With the following approach, you can clear the whole canvas or just a part of it.
Please do not forget to disable Hardware acceleration since PorterDuff.Mode.CLEAR doesn’t work with hardware acceleration and finally call setWillNotDraw(false) because we override the onDraw method.
//view's constructor
setWillNotDraw(false);
setLayerType(LAYER_TYPE_SOFTWARE, null);
//view's onDraw
Paint TransparentPaint = new Paint();
TransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(0, 0, width, height, TransparentPaint);
Don't forget to call invalidate();
canvas.drawColor(backgroundColor);
invalidate();
path.reset();
I found my solution.
PaintView class:
public void clear() {
mPath.reset();
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
paths.clear();
}
And MainActivity:
clear_canvas_.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
paintView.clear();
}
});
Your first requirement, how to clear or redraw whole canvas - Answer - use canvas.drawColor(color.Black) method for clearing the screen with a color of black or whatever you specify .
Your second requirement, how to update part of the screen - Answer - for example if you want to keep all other things unchanged on the screen but in a small area of screen to show an integer(say counter) which increases after every five seconds. then use canvas.drawrect method to draw that small area by specifying left top right bottom and paint. then compute your counter value(using postdalayed for 5 seconds etc., llike Handler.postDelayed(Runnable_Object, 5000);) , convert it to text string, compute the x and y coordinate in this small rect and use text view to display the changing counter value.
Try to remove the view at onPause() of an activity and add onRestart()
LayoutYouAddedYourView.addView(YourCustomView);
LayoutYouAddedYourView.removeView(YourCustomView);
The moment you add your view, onDraw() method would get called.
YourCustomView, is a class which extends the View class.
In my case, I draw my canvas into linearlayout.
To clean and redraw again:
LinearLayout linearLayout = findViewById(R.id.myCanvas);
linearLayout.removeAllViews();
and then, I call the class with the new values:
Lienzo fondo = new Lienzo(this,items);
linearLayout.addView(fondo);
This is the class Lienzo:
class Lienzo extends View {
Paint paint;
RectF contenedor;
Path path;
ArrayList<Items>elementos;
public Lienzo(Context context,ArrayList<Items> elementos) {
super(context);
this.elementos=elementos;
init();
}
private void init() {
path=new Path();
paint = new Paint();
contenedor = new RectF();
paint.setStyle(Paint.Style.FILL);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
contenedor.left = oneValue;
contenedor.top = anotherValue;
contenedor.right = anotherValue;
contenedor.bottom = anotherValue;
float angulo = -90; //starts drawing at 12 o'clock
//total= sum of all element values
for (int i=0;i<elementos.size();i++){
if (elementos.get(i).angulo!=0 && elementos.get(i).visible){
paint.setColor(elementos.get(i).backColor);
canvas.drawArc(contenedor,angulo,(float)(elementos.get(i).value*360)/total,true,paint);
angulo+=(float)(elementos.get(i).value*360)/total;
}
} //for example
}
}
In my case, creating canvas every time worked for me, even though it's not memory-friendly
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.image);
imageBitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
canvas = new Canvas(imageBitmap);
canvas.drawBitmap(bm, 0, 0, null);
I had to use a separate drawing pass to clear the canvas (lock, draw and unlock):
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas == null) {
// exit drawing thread
break;
}
canvas.drawColor(colorToClearFromCanvas, PorterDuff.Mode.CLEAR);
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
The following worked for me:
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SCREEN);
Just call
canvas.drawColor(Color.TRANSPARENT)