canvas zoom in partially - android

I want to zoom in the canvas board partially as the letter tray button will be fixed and the board will be zoom,
The partially coding is as mention below and the screen is fully draw in canvas which can be viewed as mention at the bottom.. please help and thanks for you concern.
public class BoardView extends SurfaceView implements SurfaceHolder.Callback
{
class DrawingThread extends Thread implements OnTouchListener {
public DrawingThread(SurfaceHolder holder, Handler handler) {
mSurfaceHolder = holder;
public void setRunning(boolean b) {
mRun = b;
}
#Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
// System.gc();
// c.scale(canvasScaleX, canvasScaleY);
// c.save();
// c.translate(canvasTranslateX, canvasTranslateY);
doDraw(c);
}
updateGame();
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
private void doDraw(Canvas canvas) {
if (ge == null)
return;
Rect bRect = new Rect(0, 0, dims.getTotalWidth(),
dims.getScoreHeight() + dims.getBoardheight());
Drawable drawable = getResources().getDrawable(R.drawable.board);
drawable.setBounds(bRect);
drawable.draw(canvas);
Rect tRect = new Rect(0, dims.getScoreHeight()
+ dims.getBoardheight(), dims.getTotalWidth(),
dims.getTotalHeight());
canvas.drawRect(tRect, fillTrayPaint);
int topHeight = dims.getScoreHeight() + dims.getBoardheight();
int bottom = (dims.getTotalHeight() + 5)
- (dims.getTotalWidth() / Tray.TRAY_SIZE);
Rect rect = new Rect(0, topHeight, dims.getTotalWidth(), bottom - 7);
Drawable drawableTray = getResources()
.getDrawable(R.drawable.strip);
drawableTray.setBounds(rect);
drawableTray.draw(canvas);
drawTray(canvas);
drawBoard(canvas);
// drawScore(canvas);
drawMovingTile(canvas);
}
public BoardView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// endTurn=(ImageButton)findViewById(R.id.endturn_button_horizontal);
thread = new DrawingThread(holder, handler);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
final float scale1 = getResources().getDisplayMetrics().density;
defaultFontSize = (int) (MIN_FONT_DIPS * scale1 + 0.5f);
thread.setDefaultFontSize(defaultFontSize);
dimensions = calculateDimensions(getWidth(), getHeight());
thread.setDimensions(dimensions);
thread.setRunning(true);
// if (thread != null && !thread.isAlive())
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}

We can simply do it by canvas.clipRect(rectangle), at this point what we are suppose to do is save the canvas canvas.save() and after canvas.clipRect(rectangle) call canvas.restore().

Consider trying something like this:
Create a bitmap from the canvas you wish to zoom into.
When rendering that bitmap to the screen, you can change the scr rect
(which will "zoom" into the bitmap).
I hope this helps.

I think you need to render your game board into offscreen bitmap and then draw this bitmap at canvas using Matrix, so you will be able to change its position and scale.

I believe the easiest way to do this would be to use Canvas#scale(float scale, int pivotx, int pivoty) method. It essentially grows the entire canvas uniformally which really is what zooming is from a 2D perspective.
canvas.scale(2.0f, getWidth()/2, getHeight()/2) will scale the image by 2 from the center. canvas.scale(1.0f, getWidth()/2, getHeight()/2) will shrink it back.
I have never tried this in this context, so if you decide to try it report back the results please! I'd like to know for future reference.

Related

Android SurfaceView draws bitmap multiple times

I'm trying to draw a bitmap into surfaceView I can successfully draw but I need to move that bitmap around the screen based on some other user movements but when I set
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(bitmap, left, top, null);
It draws same bitmap multiple times in screen.
But when I do this way
canvas.drawColor(Color.GREEN);
canvas.drawBitmap(bitmap, left, top, null);
It works correctly draws just one bitmap and moves it , but I need transparent background not colored.
CODE
public class DotsSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holder;
private boolean created;
Bitmap bitmap;
#Override
public void surfaceCreated(SurfaceHolder holder) {
// draw();
created = true;
}
#Override
// This is always called at least once, after surfaceCreated
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// draw();
created = true;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
public DotsSurfaceView(Context context) {
super(context);
holder = getHolder();
holder.addCallback(this);
holder.setFormat(PixelFormat.TRANSPARENT);
Drawable drawable = ARTrackingActivity.contexti.getDrawable(R.drawable.ic_tune_black_24px);
bitmap =Utils.drawableToBitmap(drawable);
}
public void draw(float left, float top) {
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
synchronized (holder) {
draw2(canvas, left, top);
}
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
public void draw2(Canvas canvas, float left, float top) {
if (created) {
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(bitmap, left, top, null);
}
}
}

Android canvas draw line on camera

I'm working with augmented reality app and I want to draw a line that connects two movable objects on top of the camera. However I get this exception
Process: test.job.reality.augument.job.com.augumentrealitytest, PID: 15056
java.lang.IllegalArgumentException
at android.view.Surface.nativeLockCanvas(Native Method)
at android.view.Surface.lockCanvas(Surface.java:266)
at custom.MyCameraView$MyThread.run(MyCameraView.java:447)
Here's my code as you can see I start my thread in surfaceCreted method. I'm thinking that I can't lockCanvas because it's a;ready locked by camera, am I right? However I to draw line is it possible (I'm using beyondAR augumented reality library)
public class MyCameraView extends SurfaceView implements SurfaceHolder.Callback, Camera.PictureCallback {
private MyThread thread;
public MyCameraView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
#SuppressWarnings("deprecation")
private void init(Context context) {
mIsPreviewing = false;
mHolder = getHolder();
mHolder.addCallback(this);
configureCamera();
if (Build.VERSION.SDK_INT <= 10) {// Android 2.3.x or lower
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
thread=new MyThread(getHolder(),this);
getHolder().addCallback(this);
setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
thread.startrun(true);
thread.start();
try {
if (mCamera == null) {
init(getContext());
if (mCamera == null) {
return;
}
}
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
if (mCamera != null) {
mCamera.release();
}
mCamera = null;
Logger.e(TAG, "CameraView -- ERROR en SurfaceCreated", exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCamera();
thread.startrun(false);
thread.stop();
}
public class MyThread extends Thread{
private SurfaceHolder msurfaceHolder;
private MyCameraView mSurfaceView;
private boolean mrun =false;
public MyThread(SurfaceHolder holder, MyCameraView mSurfaceView) {
this.msurfaceHolder = holder;
this.mSurfaceView=mSurfaceView;
}
public void startrun(boolean run) {
mrun=run;
}
#SuppressLint("WrongCall")
#Override
public void run() {
super.run();
Canvas canvas;
while (mrun) {
canvas=null;
try {
canvas = msurfaceHolder.lockCanvas();
synchronized (msurfaceHolder) {
mSurfaceView.onDraw(canvas);
}
} finally {
if (canvas != null) {
msurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
When a surface is used to display camera preview, you cannot draw on it anymore. You could, in principle, open a separate transparent canvas on top of the preview surface, and draw on that canvas. It may be much easier to switch to OpenGL preview (using SurfaceTexture); then you can use the same OpenGL context for your drawings (but they must be expressed in OpenGL, too). You can find a simple demo project at GitHub to begin with.
But even in this case, there is another problem with your task: even if your CV is very fast, there is no synchornization between your augmentation and the camera frame that goes straight from the sensors to the screen. In the case of OpenGL, there are some simple things that can be done in 0 time (e.g. simple convolution on the received texture). But anything more interesting requires something different.
We usually hide the live preview from the user, get the frames in the onPreviewFrame() callback, process them and draw the frame and whatever additional lines or objects manually. This introduces some delay, but if your algorithms are really efficient, this delay may be negligible.
Again, OpenGL is usually of great help here.
Don't lockCanvas and stop thread call & just put the below code in on Draw method. I hope it will work.
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Paint paint = new Paint();
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
screenHeight = (int) (metrics.heightPixels * 0.6290);
paint.setAntiAlias(true);
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.argb(255, 255, 255, 255));
canvas.drawLine((screenWidth / 4), 0,
(screenWidth / 4), screenHeight + 500, paint);
if(screenWidth == 1280 && screenHeight == 473) // this condition for 10" tab only
canvas.drawLine((screenWidth / 2f), 0,
(screenWidth / 2f), screenHeight + 500, paint);
else
canvas.drawLine((screenWidth / 2.20f), 0,
(screenWidth / 2.20f), screenHeight + 500, paint);
canvas.drawLine(0, (screenHeight / 2.15f) * 2, screenWidth,
(screenHeight / 2.15f) * 2, paint);
canvas.drawLine(0, (screenHeight / 2.15f) , screenWidth,
(screenHeight / 2.15f), paint);
}

Move image left to right using surfaceview android

I am trying to understand using surfaceview. I want an image to move from left to right using surfaceview. Currently image is drawn but I am unable to make it move. Where should I call the variables x1 and y1. Also how could I call the invalidate() method, I want to make the image move from left to right repeatedly. Thanks
public class GameBoardTwo extends SurfaceView {
private Bitmap bitmap1;
private SurfaceHolder holder;
int x1, y1;
public GameBoardTwo(Context context) {
super(context);
holder = getHolder();
bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.blueball);
init();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Canvas c = holder.lockCanvas(null);
drawSomething(c);
holder.unlockCanvasAndPost(c);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
}
private void init(){
x1 = 0;
y1 = 100;
}
protected void drawSomething(Canvas canvas) {
canvas.drawColor(Color.WHITE);
if(x1 < canvas.getWidth()){
x1 += 5;
}
else{
x1 = 0;
}
canvas.drawBitmap(bitmap1, x1, y1, null);
}
}
You need to use thread/runnable inteface and override run(). Something like this:
#Override
protected void run(){
while(true){
drawSomething(c);//c is your locked canvas
try{
Thread.sleep(50)
} catch(Exception e){}
}
}

Smooth Painting in a SurfaceView

I'm working on a paint-like app, but I've run it to some troubles.
I know I have re-draw all the objects each frame, but this in turn makes performance slow further on when many objects are visible.
I've also noticed that clearing the background only once, then adding objects to be drawn to the surface when painting is active makes the screen flash almost to the point of inducing epilepsy.
So what is the best way to make a paint app 100% smooth no matter how many objects that are drawn?
Epilepsy-inducing code:
public void run()
{
while (running)
{
if (!holder.getSurface().isValid())
continue;
if (drawObjectsToAdd.size() == 0)
continue;
drawing = true;
Canvas c = holder.lockCanvas();
if (redrawBackground)
{
c.drawARGB(255, 255, 255, 255);
redrawBackground = false;
}
drawObjects.addAll(drawObjectsToAdd);
drawObjectsToAdd.clear();
for (DrawObject draw : drawObjects)
{
draw.Draw(c, paint);
}
holder.unlockCanvasAndPost(c);
drawing = false;
}
}
this adds a new object once it's been added outside of the thread, but it makes the screen flash so much it gives me headaches - and I only redraw the background once.
Smooth at first but becomes laggy after a while, probably due to having to add hundreds and hundreds of objects in the end:
public void run()
{
while (running)
{
if (!holder.getSurface().isValid())
continue;
if (drawObjectsToAdd.size() > 0)
{
drawObjects.addAll(drawObjectsToAdd);
drawObjectsToAdd.clear();
redraw = true;
}
if (clear)
{
drawObjectsToAdd.clear();
drawObjects.clear();
redraw = true;
clear = false;
}
if (!redraw)
continue;
drawing = true;
Canvas c = holder.lockCanvas();
c.drawARGB(255, 255, 255, 255);
for (DrawObject draw : drawObjects)
{
try
{
draw.Draw(c, paint);
}
catch (Exception ex) { }
}
holder.unlockCanvasAndPost(c);
drawing = false;
redraw = false;
}
}
I, at least for this app wanna store all the objects that are added so it doesn't matter how it's painted as long as it's smooth all the way. Preferably, add a circle - it will render a new Bitmap on to the surface, instead of having to redraw lots of objects each frame - instead store them but do not add objects already drawn.
UPDATE
Pseudo-Code of how I want it to be:
If no background is drawn
Draw background color
If new items have been added
Draw only new items to the background
Store new items in objects list
This way, we'll only draw the background once. When a new item is added, only draw that item to the existing surface. When the objects increases, looping through every item will reduce performance greatly and it will not be pleasant to work with.
UPDATE 2:
private void Draw()
{
while (running)
{
if (!holder.getSurface().isValid())
continue;
if (picture == null)
{
picture = new Picture();
Canvas c = picture.beginRecording(getWidth(), getHeight());
c.drawARGB(255, 255, 255, 255);
picture.endRecording();
}
if (drawObjectsToAdd.size() > 0)
{
drawObjects.addAll(drawObjectsToAdd);
drawObjectsToAdd.clear();
Canvas c = picture.beginRecording(getWidth(), getHeight());
for (DrawObject draw : drawObjects)
{
draw.Draw(c, paint);
}
picture.endRecording();
drawObjects.clear();
}
Canvas c2 = holder.lockCanvas();
c2.drawPicture(picture);
holder.unlockCanvasAndPost(c2);
}
}
This last method from Update 2 makes it render all the lines like the "Snake game" when adding circles. Looks like a moving snake on a background, where some of it's circles disappear one frame and others don't the next. If I skip to redraw each frame, it will instead vary which of these circles that are visible.
what about that Picture implementation? increase MAX_DRAWERS to some reasonable value and see how it works
class SV extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private static final int MAX_DRAWERS = 8;
private boolean mRunning = true;
private List<Picture> mPictures = new LinkedList<Picture>();
private List<Drawer> mDrawers = new LinkedList<Drawer>();
private Paint mPaint;
public SV(Context context) {
super(context);
mPaint = new Paint();
mPaint.setColor(0xffffff00);
getHolder().addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
new Thread(this).start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public synchronized void surfaceDestroyed(SurfaceHolder holder) {
mRunning = false;
notify();
}
#Override
public synchronized boolean onTouchEvent(MotionEvent event) {
Drawer drawer = new Drawer(event.getX(), event.getY());
mDrawers.add(drawer);
if (mDrawers.size() > MAX_DRAWERS) {
Picture picture = new Picture();
Canvas canvas = picture.beginRecording(getWidth(), getHeight());
mPaint.setAlpha(0xbb);
for (Drawer dr : mDrawers) {
dr.draw(canvas, mPaint);
}
picture.endRecording();
mPaint.setAlpha(0xff);
mPictures.add(picture);
mDrawers.clear();
Log.d(TAG, "onTouchEvent new Picture");
}
notify();
return false;
}
#Override
public synchronized void run() {
SurfaceHolder holder = getHolder();
while (mRunning) {
// Log.d(TAG, "run wait...");
try {
wait();
} catch (InterruptedException e) {
}
if (mRunning) {
// Log.d(TAG, "run woke up");
Canvas canvas = holder.lockCanvas();
canvas.drawColor(0xff0000ff);
for (Picture picture : mPictures) {
picture.draw(canvas);
}
for (Drawer drawer : mDrawers) {
drawer.draw(canvas, mPaint);
}
holder.unlockCanvasAndPost(canvas);
}
}
Log.d(TAG, "run bye bye");
}
class Drawer {
private float x;
private float y;
public Drawer(float x, float y) {
this.x = x;
this.y = y;
}
public void draw(Canvas canvas, Paint paint) {
canvas.drawCircle(x, y, 8, paint);
}
}
}
You generate your list of objects each and every frame, just to discard it after drawing. Why? Generate your list of draw objects once, then draw them.
That's all you need for your drawing thread...
public void run() {
while (running)
{
Canvas c = holder.lockCanvas();
c.drawARGB(255, 255, 255, 255);
for (DrawObject draw : drawObjects)
{
try
{
draw.Draw(c, paint);
}
catch (Exception ex) { }
}
holder.unlockCanvasAndPost(c);
}
}

dynamic text in live wallpaper

Welcome. I have a problem. I want to do dynamic wallpaper so that every few seconds to change the text. The program works pretty well because the text is changing, but changing so that the previous text is still visible, and after a few seconds I have a lot of the text. I looked everywhere but I am a beginner and do not know how to solve that problem. There is a code:
private class MyWallpaperEngine extends Engine {
private final Handler handler = new Handler();
private final Runnable drawRunner = new Runnable() {
#Override
public void run() {
draw();
}
};
private Paint paint = new Paint();
private int width;
int height;
private boolean visible = true;
public MyWallpaperEngine() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
handler.post(drawRunner);
}
#Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(drawRunner);
} else {
handler.removeCallbacks(drawRunner);
}
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
this.visible = false;
handler.removeCallbacks(drawRunner);
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
this.width = width;
this.height = height;
super.onSurfaceChanged(holder, format, width, height);
}
private void draw() {
SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas != null)
drawAnimation(canvas);
} finally {
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
if (visible) {
handler.postDelayed(drawRunner, 4000);
}
}
private void drawAnimation(Canvas c){
Random r = new Random();
int i1=r.nextInt(200-50) + 50;
String text = Integer.toString(i1);
c.drawText(text, i1, i1, paint);
}
}
You should clear the canvas before drawing, using something like
c.drawColor(int color);
or draw anything else that covers the entire area, otherwise you will just draw onto what was already on the canvas.

Categories

Resources