35
Jan SmrcinaReferences: Slides with help from Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing http://courses.coreservlets.com/Course-Materials/pdf/java 5/12-Java-2D.pdf Java Drawing in 2D Animations with Timer

Jan SmrcinaReferences: Slides with help from Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

  • Upload
    york

  • View
    38

  • Download
    2

Embed Size (px)

DESCRIPTION

Java Drawing in 2D Animations with Timer. Jan SmrcinaReferences: Slides with help from Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing http://courses.coreservlets.com/Course-Materials/pdf/java5/12-Java-2D.pdf. Drawing Basics. - PowerPoint PPT Presentation

Citation preview

Page 1: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Jan SmrcinaReferences:Slides with help from Sun’s Java TutorialsRick Snodgrass’ Slides on Basic and Advanced Swinghttp://courses.coreservlets.com/Course-Materials/pdf/java5/12-Java-2D.pdf

Java Drawing in 2DAnimations with Timer

Page 2: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Drawing BasicsA simple two-dimensional coordinate system exists

for each graphics context or drawing surface such as a JPanel

Points on the coordinate system represent single pixels (no world coordinates exist)

Top left corner of the area is coordinate <0, 0> A drawing surface has a width and height

example: JPanel has getWidth() and getHeight()methods

Anything drawn outside of that area is not visible

Page 3: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

JComponentTo begin drawing we first need a class which

extends JComponent. For this we can use JPanel.

Once we subclass JPanel we can override the paintComponent() method to specify what we want the panel to paint when repainting.

When painting we paint to a Graphics context (which is given to us as an argument to paintComponent). This will be supplied when the method is called but this means that…

Page 4: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

paintComponent(Graphics g)paintComponent() is the method which is

called when repainting a Component.It should never be called explicitly, but

instead repaint() should be invoked which will then call paintComponent()on the approriate Components.

When we override paintComponent()the first line should be super.paintComponent(g)which will clear the panel for drawing.

Page 5: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Graphics vs. Graphics2DWe are passed a Graphics object to paintComponent() which we paint to. We can always cast the object passed to a Graphics2D which has much more functionality.Why didn’t Java just pass Graphics2D in?

Legacy supportCast:public void paintComponent(Graphics g) { // Clear the Component so we can draw to a fresh

canvas super.paintComponent(g); // Cast g to a Graphics2D object Graphics2D g2 = (Graphics2D)g; }

Page 6: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Now for some Drawing…Now that we have our Graphics2D object we

can draw things to the graphics context which will show up on our panel:

public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D)g; g2.drawString("Draw a string to the context...", 20, 20);

}

Note: When drawing strings the coordinates refer to the BOTTOM left corner.

Page 7: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Pulling It TogetherNow that we have a JPanel with an overridden paintComponent()we can add this JPanel to a JFrame and display it to our user.

But drawing is about more than just strings, and Java gives us lots of additional drawing features.

Note: A common pitfall is to try to call paintComponent() explicitly. Make sure never to do this, always use the repaint() function!

Page 8: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Using Java GeometryClasses within java.awt.geom:

AffineTransform, Arc2D, Arc2D.Double, Arc2D.Float, Area, CubicCurve2D, CubicCurve2D.Double, CubicCurve2D.Float, Dimension2D, Ellipse2D,Ellipse2D.Double, Ellipse2D.Float, FlatteningPathIterator, GeneralPath, Line2D, Line2D.Double, Line2D.Float, Path2D, Path2D.Double, Path2D.Float, Point2D, Point2D.Double, Point2D.Float, QuadCurve2D, QuadCurve2D.Double, QuadCurve2D.Float, Rectangle2D, Rectangle2D.Double, Rectangle2D.Float, RectangularShape, RoundRectangle2D, RoundRectangle2D.Double, RoundRectangle2D.Float

What are these useful for? Let’s look at a Code example!

Page 9: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Using Java Geometry (cont.) public void paintComponent(Graphics g) {

super.paintComponent(g);Graphics2D g2 = (Graphics2D)g;// xcoord, ycoord, width, heightRectangle2D body = new Rectangle2D.Double(30.0, 70.0, 200.0, 50.0);g2.draw(body);

}

Note: For all other shapes the coordinates refer to the TOP left corner.

Note: Rectangle2D.Double means that Double is an inner class contained in Rectangle2D.

The difference between g2.draw() and g2.fill() is that draw will draw an outline of the object while fill will fill in the object.

Until now we have been drawing using a basic penstroke (just a plain black line). Now that we know how to draw shapes we can start messing with the pen.

Page 10: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Pen StylesLet’s look at some Pen Styles that we can set:

// Basic functionality// Set the Pen color or pattern (Let’s focus on color)g2d.setPaint(fillColorOrPattern);// Set the Stroke styleg2d.setStroke(penThicknessOrPattern);// Set Alpha (transparency)g2d.setComposite(someAlphaComposite); g2d.setFont(someFont);

// More advanced functionality// Translate to a different pointg2d.translate(...); // Rotate around our origing2d.rotate(...);// Scale everything we drawg2d.scale(...);// Shear what we drawg2d.shear(...);// Use a custom transformationg2d.setTransform(someAffineTransform);

Page 11: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

ColorTo begin modifying the pen we first need to understand

color. The Color class in Java allows us to define and manage the color in which shapes are drawn.

Colors are defined by their RGB (Red, Green, Blue) values (and possibly an alpha value). Any color can be expressed this way (i.e. Conifer is defined as (140, 225, 65)):

The way to tell Java to use a particular color when drawing is by invoking g2.setPaint(Color c) or g2.setColor(Color c) . These two are equivalent.

Page 12: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Color (cont.)There are some predefined colors in Java which

can be accessed through the Color class (i.e. Color.BLUE is blue). Additionally we can define our own colors through the Color constructor:

Color conifer = new Color(140, 225, 65);

The values that go into the constructor are 1 byte large (means that the min is 0 and the max is 255). This allows Java to store a color into 4 bytes (the size of an int) including its alpha value (transparency, more on that later).

Page 13: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Strokesg2.setStroke(…) allows us to create different

strokes and create effects (such as a dotted line).// 30 pxl line, 10 pxl gap, 10 pxl line, 10 pxl gapfloat[] dashPattern = {30.0f , 10.0f , 10.0f, 10.0f};

// float width, int cap, int join, float miterlimit, float[] dash,// float dash_phaseg2d.setStroke(new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, dashPattern, 0));

g2d.draw(getCircle());

Note: Assume getCircle() returns a geometric circle. What is a cap or join?

Page 14: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Cap and Join JOIN_MITER

– Extend outside edges of lines until they meetThis is the default

JOIN_BEVEL– Connect outside corners of outlines with straight line

JOIN_ROUND– Round off corner with a circle that has diameter equal to the pen width

CAP_SQUARE– Make a square cap that extends past the end point by half the pen width

This is the default CAP_BUTT

– Cut off segment exactly at end point Use this one for dashed lines.

CAP_ROUND – Make a circle centered on the end point. Use a diameter equal to the pen width.

What does this all mean!? Let’s look at a graphical example…

Page 15: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Cap and Join (cont.)

Page 16: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Alpha (Transparency)Concept: We want to assign transparency (alpha) values to drawing operations

so that the underlying graphics partially shows through when you draw shapes or images.

Execution: Create an AlphaComposite object using AlphaComposite.getInstance (Singleton anyone?) with a mixing rule.

There are 12 built-in mixing rules but we only care about AlphaComposite.SRC_OVER. See the AlphaComposite API for more details.

Alpha values range from 0.0f to 1.0f, completely transparent and completely opaque respectively.

Finally pass the AlphaCoposite object to g2.setComposite(…) so that it will be used in our rendering.

Let’s see an example…

Page 17: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Alpha (Transparency, cont.) Code:

public void paintComponent(Graphics g){

Composite originalComposite = g2d.getComposite();g2d.setPaint(Color.BLUE);g2d.fill(blueSquare);

AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);

g2d.setComposite(alphaComposite);g2d.setPaint(Color.RED);g2d.fill(redSquare);g2d.setComposite(originalComposite);

}

Note: assume blueSquare and redSquare exist with set positions and sizes

Page 18: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Fonts A Font object is constructed with 3 arguments to indicate the

logical font names such as "SansSerif" style such as Font.PLAIN and Font.BOLD font size

Once we have constructed a font we can tell Java to use it usingg2.setFont(Font f).

Example:Font aFont = new Font("SansSerif", Font.BOLD, 16);g2.setFont(aFont);

Note: Avoid font names like "AvantGuard" or "Book Antiqua" since they may not be installedInstead, use logical font names mapped to fonts actually installed. On windows, SansSerif is Arial. Examples:

"SansSerif" "Serif" "Monospaced" "Dialog" "DialogInput"

Page 19: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Fonts (cont.)Let’s say we want to use a cool font but we aren’t sure

if it’s installed. We can ask the environment for all of the installed Fonts (this is slow so only do it once, i.e. not in paintComponent()). We can then safely use the font if we know it is present.

Example:GraphicsEnvironment env =

GraphicsEnvironment.getLocalGraphicsEnvironment();String[] fontNames = env.getAvailableFontFamilyNames();

System.out.println("Available Fonts:");for(int i = 0; i < fontNames.length; i++)

System.out.println(" " + fontNames[i]);

What happens if we try to use a Font that isn’t installed? Java will use one of the default fonts

Page 20: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Advanced FunctionalityMost of the advanced functionalities shown

earlier (translate, rotate, shear, etc.) are beyond the scope of this class. We will discuss affineTransformations so that you can rotate your images in your program (if you need to).

Before we can talk about rotating images we need to know how to instantiate and draw them!

Page 21: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Images and BufferedImageThe BufferedImage subclass describes an Image with an

accessible buffer of image data.BufferedImages are what we will use to draw images to the

screen. Once we have an image, painting it to our canvas is easy:

public void paintComponent(Graphics g){

super.paintComponent(g);Graphics2D g2 = (Graphics2D)g;

// Image img, int x, int y, ImageObserver observerg2.drawImage(image, 100, 100, null);

}

Note: Assume image is defined. There are many overloaded definitions of drawImage, you can find them all in the API

Now we know how to paint an image, but we don’t have an image!

Page 22: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Drawing to a BufferedImageLet’s say we are writing a JPaint program which

will allow us to paint images, much like MS Paint, and then save them to the disk. How could we do this?We create a blank BufferedImage, paint to it, and

then write it to the disk.Example:

// Construct a blank BufferedImage (width, height, type of image)BufferedImage bufferedImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);// Create a Graphics object for this imageGraphics2D g2dImg = bufferedImage.createGraphics();// Paint whatever we want to it (use g2dImg as you would g2 in paintComponent)g2dImg.drawString(“String on my image”, 100, 100);

Now we want to write our BufferedImage to disk. How can we achieve this?

Page 23: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

ImageIOJava provides a library for reading and writing Images

from and to the hard disk so that we don’t have to deal with coding it (becomes very complex when dealing with multiple types such as PNG and JPEG).

There are methods for reading and writing images to a file.

Reading an image from the disk: BufferedImage img = ImageIO.read(new File(“myimg.png”));

Writing an image to disk:try{// Assume image is from our previous slideImageIO.write(image, "png", outputfile);}catch (IOException e){ ... }

Page 24: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

AnimationAt its simplest, animation is the time-based alteration of

graphical objects through different states, locations, sizes and orientations.

Most animation consists of cycling through a pre-made set of bitmaps at a certain rate.

In general a rate of 24 images (frames) per second is used in film, but many video games do well over this.

Note that most displays only support around 60 refreshes per second.

Advanced Swing F2-24

Page 25: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Animation: TimingSince code execution time may vary (even on the same

machine), animations should take into account how much time has passed from the last refresh to calculate the new values of an animation.

When doing animation – note that System.currentTimeMillis() usually has a resolution of 16 milliseconds.

System.nanoTime()is more precise. (But not to the precision of one nanosecond!)

Advanced Swing F2-25

Page 26: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Animation: Simple Timer ExampleHere we set a timer to wake up every 10 milliseconds,

update a count, and request a repaint.

timer = new javax.swing.Timer(10, new ActionListener() { public void actionPerformed(ActionEvent evt) { count++; // count should be volatile! repaint(); if (count == 100) { timer.stop(); } }});

// The timer is started when animation is started // The code in paintComponent() uses the count to calculate // how far along the animation is. public void paintComponent(Graphics g) { ... double percentage = (count / (double) 100); int h1 = (int) (unitHeight * percentage); }

Page 27: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Animation: Double BufferingDouble buffering is the mechanism of

using a second bitmap (or buffer) which receives all of the updates to a window during an update.

Once all of the objects of a window have been drawn, then the bitmap is copied to the primary bitmap seen on the screen.

This prevents the flashing from appearing to the user that occurs when multiple items are being drawn to the screen.

Page 28: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Animation: Double Buffering In SwingSwing now (as of Java 6) provides true

double buffering. Previously, application programmers had to implement double buffering themselves.

The no-arg constructor of JPanel returns a JPanel with a double buffer (and a flow layout) by default.

"...uses additional memory space to achieve fast, flicker-free updates."

Page 29: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Animation: Double Buffering In Swing Each box

drawnin order might be detectable by user.

The entire backing bitmap is copied at once to the visible bitmap, avoiding any flicker.

Drawn1st

Drawn2nd

Drawn3rd

Drawn4th

Backing Bitmap(not visible to

user)

Window Bitmap

(visible to user)

Page 30: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Animation: Coalescence Of RequestsSwing will coalesce (or merge) a

number of repaint requests into one request if requests occur faster than they can be executed.

Page 31: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Caveat to Animated GIF’sUsing animated GIF’s in your projects is okay, but

there is a better way to animate things.Problems with animated GIF’s:

The timings between frames are set in stone and cannot be controlled by the programmer.

During the first animation of the GIF it will appear that your image is not loaded since the GIF loads each frame as it is displayed (looks ugly, but can be avoided using image loaders)

Programmers also can’t control which frame to display or start/stop on since the GIF just keeps animating.

Instead of Animated GIF’s, most 2D games will use what is called a SpriteSheet and then animate using a Timer or Thread.

Page 32: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Sprite SheetsA sprite sheet is a depiction of

various sprites arranged in one image, detailing the exact frames of animation for each character or object by way of layout.

The programmer can then control how fast frames switch and which frame to display. Additionally the whole sheet is loaded at once as a single image and won’t cause a loading glitch.

In Java we can use the BufferedImage method:getSubimage(int x, int y, int w, int h) Returns a subimage defined by a

specified rectangular region.

Page 33: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Basic Affine Transformations An affine transformation is a transformation which is a

combination of single transformations such as translation or rotation or reflection on an axis

What does this mean for us? It means we can rotate our images with relative ease! More complex affine transformations won’t be discussed here but

there are tutorials widely available on the all-powerful internet. Here we finally get to see a functionality only available in Graphics2D:

drawImage(Image img, AffineTransform xform, ImageObserver obs)

Description: Renders an image, applying a transform from image space into user space before drawing.

Note: When rotating, remember that you are rotating around the your origin, so if you don’t set up your transformation correctly it will rotate your image wrong!

Let’s see an example!

Page 34: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Basic Affine Transformations (cont.)public void paintComponent(Graphics g)  {

Graphics2D g2d = (Graphics2D)g;

  AffineTransform at = new AffineTransform();  // rotate 45 degrees around image center

at.rotate(45.0 * Math.PI / 180.0, image.getWidth() / 2.0,image.getHeight() / 2.0); 

             //draw the image using the AffineTransform g2d.drawImage(image, at, null);

}

Assume image is defined. Why do we need to specify the second and third arguments to rotate?

This specifies our image center as the point that we are rotating around, otherwise we rotate around 0,0 which isn’t what we intended!

Page 35: Jan SmrcinaReferences: Slides with help from  Sun’s Java Tutorials Rick Snodgrass’ Slides on Basic and Advanced Swing

Wrap-upQuestions, Comments, Concerns, etc?Sprite Sheet Example (if time permits)