Newbie question about 2D texture drawing

Hi,

I’m new to JOGL, and I’m looking for some guidance on drawing 2d textures. I’ve built an app to display a background image that shifts as you approach the edges. My code’s working, but the shifting isn’t so smooth; occasionally you see jumps. Am I following the right approach in drawing my image?

The texture rendering:


gl.glEnable(GL.GL_TEXTURE_2D);
backgroundImage.bind();   
// replace the quad colours with the texture
gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);
gl.glBegin(GL.GL_QUADS); {
	gl.glTexCoord2f(newLft, newTop);
	gl.glVertex2f(verLft, verTop);
	gl.glTexCoord2f(newRit, newTop);
	gl.glVertex2f(verRit, verTop);
	gl.glTexCoord2f(newRit, newBtm);
	gl.glVertex2f(verRit, verBtm);
	gl.glTexCoord2f(newLft, newBtm);
	gl.glVertex2f(verLft, verBtm);
}
gl.glEnd();
gl.glDisable(GL.GL_TEXTURE_2D);
// switch back to modulation of quad colours and texture
gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);

And my test application:

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.net.*;
import javax.media.opengl.*;
import com.sun.opengl.util.*;
import com.sun.opengl.util.texture.*;

public class JOGLTest {
	public static final Dimension BK_SIZE = new Dimension(1459, 1100);
	public static final Dimension VIEW_SIZE = new Dimension(300, 300);
	public static final Dimension FRAME_SIZE = new Dimension(600, 600);
	public static void main(String[] args) {
		final Frame f = new Frame();
		f.setSize(FRAME_SIZE);
		f.setMinimumSize(FRAME_SIZE);
		f.setLayout(null);
		GLCapabilities glCaps = new GLCapabilities();
		glCaps.setDoubleBuffered(true);
		glCaps.setHardwareAccelerated(true);
		glCaps.setRedBits(8);
		glCaps.setBlueBits(8);
		glCaps.setGreenBits(8);
		glCaps.setAlphaBits(8);
		MapCanvas panel = new MapCanvas(glCaps, VIEW_SIZE);
		panel.setSize(VIEW_SIZE);
		panel.setMinimumSize(VIEW_SIZE);
		panel.setMaximumSize(VIEW_SIZE);
		panel.setBounds(new Rectangle(new Point(150,150), VIEW_SIZE));
		final Animator animator = new Animator(panel);
		f.add(panel);
		f.addWindowListener(new WindowListener() {
			public void windowActivated(WindowEvent e) { }
			public void windowClosed(WindowEvent e) {
				animator.stop();
				f.dispose();
				System.exit(0);
			}
			public void windowClosing(WindowEvent e) {
				animator.stop();
				f.dispose();
				System.exit(0);
			}
			public void windowDeactivated(WindowEvent e) { }
			public void windowDeiconified(WindowEvent e) { }
			public void windowIconified(WindowEvent e) { }
			public void windowOpened(WindowEvent e) { }
		});
		f.setVisible(true);
		animator.start();
	}
}
class MapCanvas extends GLCanvas {
	private Dimension viewSize;
	public MapCanvas(final GLCapabilities c, final Dimension s) {
		super(c);
		viewSize = s;
		super.addGLEventListener(new MapCanvasListener(this));
	}
	public Dimension getViewSize() { return viewSize; }   
}
class MapCanvasListener implements GLEventListener {
	private MapCanvas canvas;
	private float dx;
	private float dy;
	int backgroundImageWidth = 1200;
	int backgroundImageHeight = 824;
	private Point2D.Float topLeft;
	private GL gl;
	private GLDrawable glDrawable;
	private static final int EDGE = 64;
	private static float scrollSpeed = 0.25f;
	private boolean shiftPossible;
	private int updates;
	int updatesBeforeShift = 10;
	private Texture backgroundImage;
	public MapCanvasListener(final MapCanvas map) {
		canvas = map;
		topLeft = new Point2D.Float(0.0f, 0.0f);
	}
	public final void stopShifting() {
		shiftPossible = false;
		updates = 0;
	}
	private void calculateShift() {
		dx = 0;
		dy = 0;
		int bWidth = backgroundImageWidth;
		int bHeight = backgroundImageHeight;
		int vWidth = JOGLTest.VIEW_SIZE.width;      
		int vHeight = JOGLTest.VIEW_SIZE.height;
		Point mouseLoc = canvas.getMousePosition();
		if (mouseLoc == null) {
			stopShifting();
		} else {
			if (mouseLoc.getX() < EDGE) {
				if (mouseLoc.getY() < EDGE) {
					if (topLeft.x <= 0
							&& topLeft.y <= 0) {
						stopShifting();
					} else {
						dx = -scrollSpeed;
						dy = -scrollSpeed;
						shiftPossible = true;
					}
				} else if (mouseLoc.getY() > vHeight - EDGE) {
					if (topLeft.x <= 0
							&& topLeft.y >= bHeight - vHeight) {
						stopShifting();
					} else {
						dx = -scrollSpeed;
						dy = scrollSpeed;
						shiftPossible = true;
					}
				} else {
					if (topLeft.x <= 0) {
						stopShifting();
					} else {
						dx = -scrollSpeed;
						shiftPossible = true;
					}
				}
			} else if (mouseLoc.getX() > vWidth - EDGE) {
				if (mouseLoc.getY() < EDGE) {
					if (topLeft.x >= bWidth - vWidth
							&& topLeft.y <= 0) {
						stopShifting();
					} else {
						dx = scrollSpeed;
						dy = -scrollSpeed;
						shiftPossible = true;
					}
				} else if (mouseLoc.getY() > vHeight - EDGE) {
					if (topLeft.x >= bWidth - vWidth
							&& topLeft.y >= bHeight - vHeight) {
						stopShifting();
					} else {
						dx = scrollSpeed;
						dy = scrollSpeed;
						shiftPossible = true;
					}
				} else {
					if (topLeft.x >= bWidth - vWidth) {
						stopShifting();
					} else { 
						dx = scrollSpeed;
						shiftPossible = true;
					}
				}
			} else if (mouseLoc.getY() < EDGE) {
				if (topLeft.y <= 0) {
					stopShifting();
				} else { 
					dy = -scrollSpeed;
					shiftPossible = true;
				}
			} else if (mouseLoc.getY() > vHeight - EDGE) {
				if (topLeft.y >= bHeight - vHeight) {
					stopShifting();
				} else { 
					dy = scrollSpeed;
					shiftPossible = true;
				}
			} else { 
				stopShifting();
			}
		}
	}
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) { }
	public void init(GLAutoDrawable drawable) {
		// load background
		try {
			backgroundImage = 
				TextureIO.newTexture(new URL("http://media.maps.com/images/downloads/World-Map-1200.gif"),
					false, 
					"gif");
			backgroundImage.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, 
					GL.GL_NEAREST);
			backgroundImage.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, 
					GL.GL_NEAREST);
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
		this.gl = drawable.getGL();
		// set the clear color here
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		this.glDrawable = drawable;

		drawable.setGL(new DebugGL(drawable.getGL()));

		// set anti-aliasing
		float[] values = new float[2];
		gl.glGetFloatv(GL.GL_LINE_WIDTH_GRANULARITY, values, 0);
		gl.glGetFloatv(GL.GL_LINE_WIDTH_RANGE, values, 0);
		gl.glEnable(GL.GL_LINE_SMOOTH);
		gl.glEnable(GL.GL_BLEND);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
		gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_DONT_CARE);
		gl.glLineWidth(1.5f);
	}
	private void applyShift() {
		if (shiftPossible) {
			updates++;
			if (updates >= updatesBeforeShift) {
				topLeft.x += dx;
				if (topLeft.x > backgroundImageWidth - canvas.getViewSize().width) {
					topLeft.x = backgroundImageWidth - canvas.getViewSize().width;
				}
				if (topLeft.x < 0) {
					topLeft.x = 0;
				}
				topLeft.y += dy;
				if (topLeft.y > backgroundImageHeight - canvas.getViewSize().height) {
					topLeft.y = backgroundImageHeight - canvas.getViewSize().height;
				}
				if (topLeft.y < 0) {
					topLeft.y = 0;
				}
			}
		}		
	}
	public void display(GLAutoDrawable drawable) {
		calculateShift();
		applyShift();
		// clear the screen
		gl.glClearDepth(0.0);
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity();
		//*******************************
		// DRAW THE TEXTURE
		//*******************************
		// the  background covers all 4 corners of the viewing area
		float verTop = 1.0f;
		float verLft = -1.0f;
		float verBtm = -1.0f;
		float verRit = 1.0f;
		// translate the map coordinates displayed
		float texBtm = backgroundImage.getImageTexCoords().bottom();
		float texRit = backgroundImage.getImageTexCoords().right();
		float verticalScale = (float) backgroundImageHeight / texBtm;
		float horizontalScale = (float) backgroundImageWidth / texRit;
		float newTop = (0.0f + topLeft.y) / verticalScale;
		float newLft = (0.0f + topLeft.x) / horizontalScale;
		float newBtm = 
			((float) JOGLTest.VIEW_SIZE.height + topLeft.y) / verticalScale;
		float newRit = 
			((float) JOGLTest.VIEW_SIZE.width + topLeft.x) / horizontalScale;
		// bind to the appropriate texture for this sprite
		// enable texturing
		gl.glEnable(GL.GL_TEXTURE_2D);
		backgroundImage.bind();   
		// replace the quad colours with the texture
		gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE,
				GL.GL_REPLACE);
		gl.glBegin(GL.GL_QUADS); {
			gl.glTexCoord2f(newLft, newTop);
			gl.glVertex2f(verLft, verTop);
			gl.glTexCoord2f(newRit, newTop);
			gl.glVertex2f(verRit, verTop);
			gl.glTexCoord2f(newRit, newBtm);
			gl.glVertex2f(verRit, verBtm);
			gl.glTexCoord2f(newLft, newBtm);
			gl.glVertex2f(verLft, verBtm);
		}
		gl.glEnd();
		gl.glDisable(GL.GL_TEXTURE_2D);
		// switch back to modulation of quad colours and texture
		gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE,
				GL.GL_MODULATE);
	}
	public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3, int arg4) { }
}

Thanks for any help I get.