Advertisement
Help Keep Boards Alive. Support us by going ad free today. See here: https://subscriptions.boards.ie/.
If we do not hit our goal we will be forced to close the site.

Current status: https://keepboardsalive.com/

Annual subs are best for most impact. If you are still undecided on going Ad Free - you can also donate using the Paypal Donate option. All contribution helps. Thank you.
https://www.boards.ie/group/1878-subscribers-forum

Private Group for paid up members of Boards.ie. Join the club.

Double Buffering for Sprites without using Graphics class

  • 07-03-2014 02:49PM
    #1
    Registered Users, Registered Users 2 Posts: 5,753 ✭✭✭


    Hi,

    Im creating an app with a number of movable bitmaps on a canvas. Problem with it is that the Sprites do not all display in one go. More annoyingly, those that do appear flicker/blink constantly.

    After a chat with a mate, there is a known approach to this called double buffering. However, by the looks of this, i would have to use the Graphics class. I'm not sure whether this is particularly interchangeable with what i use for code. See bwlow anyways.



    package x.y.z;
    
    import java.io.ByteArrayOutputStream;
    import java.util.Random;
    
    import android.annotation.SuppressLint;
    import android.graphics.Bitmap;
    import android.graphics.Color;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    
    import android.graphics.Canvas;
    
    import android.graphics.Rect;
    
     
    
    public class Sprite {
    
           // direction = 0 up, 1 left, 2 down, 3 right,
    
           // animation = 3 back, 1 left, 0 front, 2 right
    
           int[] DIRECTION_TO_ANIMATION_MAP = { 3, 1, 0, 2};
    
           private static final int BMP_ROWS = 4;
    
           private static final int BMP_COLUMNS = 3;
    
           private static final int MAX_SPEED = 100;
    
           private GameView gameView;
    
           private Bitmap bmp;
    
           private int x = 0;
    
           private int y = 0;
    
           private int xSpeed;
    
           private int ySpeed;
    
           private int currentFrame = 0;
    
           private int width;
    
           private int height;
    
     
    
           public Sprite(GameView gameView, Bitmap bmp) {
        	   ByteArrayOutputStream out = new ByteArrayOutputStream();
        	   	bmp.compress(Bitmap.CompressFormat.JPEG, 100, out);
    
                 this.width = bmp.getWidth() / BMP_COLUMNS * 4;
    
                 this.height = bmp.getHeight() / BMP_ROWS * 6;
    
                 this.gameView = gameView;
    
                 this.bmp = bmp;
    
     
    
                 Random rnd = new Random();
    
                 x = rnd.nextInt(gameView.getWidth() - width);
    
                 y = rnd.nextInt(gameView.getHeight() - height);
    
                 xSpeed = rnd.nextInt(MAX_SPEED * 2) - MAX_SPEED;
    
                 ySpeed = rnd.nextInt(MAX_SPEED * 2) - MAX_SPEED;
    
           }
    
     
    
           private void update() {
    
                 if (x >= gameView.getWidth() - width - xSpeed || x + xSpeed <= 0) {
    
                        xSpeed = -xSpeed;
    
                 }
    
                 x = x + xSpeed;
    
                 if (y >= gameView.getHeight() - height - ySpeed || y + ySpeed <= 0) {
    
                        ySpeed = -ySpeed;
    
                 }
    
                 y = y + ySpeed;
    
                 currentFrame = ++currentFrame % BMP_COLUMNS;
    
           }
    
     
    
    	public void onDraw(Canvas canvas) {
    
                 update();
                 
                 int width = bmp.getWidth();
                 int height = bmp.getHeight(); 
    
                 int newWidth = width / 2;
                 int newHeight = height / 2;
                 float scaleWidth = ((float) newWidth) / width;
                 float scaleHeight = ((float) newHeight) / height;
                 Matrix matrix = new Matrix();
                 matrix.postScale(scaleWidth, scaleHeight);
                 Bitmap resizedBitmap = Bitmap.createBitmap(bmp, 0, 0, width, height, matrix, true); 
                 
                 int srcX = currentFrame * width;
    
                 int srcY = getAnimationRow() * height;
    
                 Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
    
                 Rect dst = new Rect(x, y, x + width, y + height);
                 
                 canvas.drawBitmap(resizedBitmap, src, dst, null);
                 
     
           }
    
     
    
           private int getAnimationRow() {
    
                 double dirDouble = (Math.atan2(xSpeed, ySpeed) / (Math.PI / 2) + 2);
    
                 int direction = (int) Math.round(dirDouble) % BMP_ROWS;
    
                 return DIRECTION_TO_ANIMATION_MAP[direction];
    
           }
    
     
    
           public boolean isCollision(float x2, float y2) {
    
                 return x2 > x && x2 < x + width && y2 > y && y2 < y + height; 
    
           }
    
    }
    


Comments

  • Closed Accounts Posts: 8,015 ✭✭✭CreepingDeath


    I haven't written any Android code but have experience with a lot of Graphics programming in general and in Java have wrote a bit of Swing graphics components.
    Bitmap resizedBitmap = Bitmap.createBitmap(bmp, 0, 0, width, height, matrix, true);
    

    You don't want to create a new bitmap every time you want to draw something on the screen.

    Ideally you only want to create the one bitmap/canvas, and reuse it.

    This is some old code I had, which created a Swing component which implemented double buffering.

    So "m_VirtScreen" is an invisible bitmap/buffer, that I write too, and only when everything is drawn on this buffer do I draw the entire image to the screen.

    So elsewhere I call Graphics2D getVirtualGraphics() to get the buffer. I write to it, and the component draws the buffer automatically on a paint event.

    package gravity.view;
    
    import java.awt.Color;
    import java.awt.event.ComponentListener; // Resize event listener
    import java.awt.event.ComponentEvent; // Resize event info
    
    import javax.swing.JComponent;
    import java.awt.Image;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Dimension;
    
    
    public abstract class JGraphicsPanel extends JComponent implements ComponentListener
    {
        private static final long serialVersionUID = 1L;
    
        protected Dimension   m_ClientRect;
        protected int         m_width, m_height;
        protected Image       m_VirtScreen   = null;
        protected Graphics2D  m_VirtGraphics = null;
    
        protected int m_MidX = 0;
        protected int m_MidY = 0;
    
        public JGraphicsPanel ( )
        {
            super();
            initialise();
        }
    
        public JGraphicsPanel ( int initHeight, int initWidth )
        {
            super();
            setSize(initHeight, initWidth);
            initialise();
        }
    
    
     
        private void initialise()
        {
            this.setAutoscrolls(false);
            this.setLayout(null);
            this.setToolTipText("");
            this.setMinimumSize(new Dimension(50, 50));
            this.setDoubleBuffered(false);
            this.setBackground(Color.BLACK);
            this.setOpaque(true);
            
            m_ClientRect = getSize();
            m_width = m_ClientRect.width;
            m_height = m_ClientRect.height;
    
            try
            {
                m_VirtScreen = createImage(m_width, m_height);
                m_VirtGraphics = (Graphics2D)m_VirtScreen.getGraphics();
            }
            catch (Exception e)
            {
                // problem creating off-screen memory image of the panel
                m_VirtScreen = null;
                m_VirtGraphics = null;
            }
    
            addComponentListener(this); // resize our memory image with the panel
        }
    
        public abstract void onResize();
    
        public void componentResized(ComponentEvent resizeEvent)
        {
            m_ClientRect = getSize();
    
            // avoid attempts to resize to the current size
            if ((m_width != m_ClientRect.width) || (m_height != m_ClientRect.height))
            {
                m_width = m_ClientRect.width;
                m_height = m_ClientRect.height;
    
                try
                {
                    if (m_VirtScreen != null)
                        m_VirtScreen.flush(); // free allocated memory
    
                    m_VirtScreen = createImage(m_width, m_height);
                    m_VirtGraphics = (Graphics2D)m_VirtScreen.getGraphics();
                }
                catch (Exception e)
                {
                    m_VirtScreen = null;
                    m_VirtGraphics = null;
                }
            }
            
            m_MidX = m_width >> 1;
            m_MidY = m_height >> 1;
            
            onResize();
        }
    
    
        public abstract void onHide();
    
        public void componentHidden(ComponentEvent e)
        {
            onHide();
        }
    
        
        public abstract void onMove();
    
        public void componentMoved(ComponentEvent e)
        {
            onMove();
        }
    
        
        public abstract void onShow();
    
        public void componentShown(ComponentEvent e)
        {
            onShow();
        }
    
    
        public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
        {
            return (true);
        }
    
        public Graphics2D getVirtualGraphics()
        {
            if (m_VirtGraphics != null)
                return (m_VirtGraphics);
            else
                return ((Graphics2D)getGraphics());
        }
        
        public void paint(Graphics g)
        {
            g.drawImage(m_VirtScreen, 0, 0, this);
        }
    
    }
    


Advertisement