Chapter 4.  NetPaint -- A networked paint program.

Joseph Czechowski

Ted Choc

Adrian Abraham

Revision History
Revision 1.02004.01.16JC4
Initial drafting of the lab, importing of parts of tools section from previous semesters.

Table of Contents

1. Overview of NetPaint
2. Tools
2.1. UI's In Java
2.2. Threading In Java
2.3. Introduction to Networking
3. Project Requirements
3.1. Overview
3.2. Networking
3.3. Draw Tools
3.4. Other Required Features
4. Suggested Design
5. Deliverables
5.1. Deliverables for Part 1
5.2. Deliverables for Part 2
5.3. Deliverables for Part 3
6. Extra Credit
[Important]Important

Part 1 is due on WebCT before 2004.01.30 08:00:00 (A.M.)!

Part 2 is due on WebCT before 2004.02.06 08:00:00 (A.M.)!

Part 3 is due on WebCT before 2004.02.13 08:00:00 (A.M.)!

***DO NOT WAIT UNTIL THE LAST MINUTE TO SUBMIT YOUR ASSIGNMENT!!!***

1. Overview of NetPaint

Everyone is familiar with the basic paint programs that are typically bundled with their favorite/least-favorite OS. NetPaint is similar to those programs, but with the (obvious) added twist that it is networked.

2. Tools

2.1. UI's In Java

2.1.1. Introduction

In the past, you have had experience using Swing components to develop Graphical User Interfaces (GUIs). This lab will expand these skills and give you the ability to create customized graphics by using the Graphics[2D] class. By the end of the lab, you will have created a simple paint program using these new GUI creation skills.

2.1.2. Introduction to Graphics

In Java, manipulation of Graphics objects is not a particularly difficult task. Since the Graphics class is abstract, you cannot directly create one by calling its constructor. You must instead obtain them from previously defined Graphics objects or by calling the getGraphics() method on a Component(java.awt.Component) or any of its subclasses. Before you call the getGraphics() method on a component, it must be displayable (i.e. the [J]Frame to which the component is added must be visible - [J]Frame.setVisible(true)). Once you have a Graphics object, you can begin to manipulate it. Graphics objects have methods used to draw basic shapes, such as characters, strings, polygons, but anything more complex will require you to combine shapes to achieve the desired output. Look at the Java API (java.awt.Graphics) for the exact commands necessary to do this. Unfortunately, accessing graphics in this manner causes some problems (mainly every time the window is changed - minimized, resized, overlapped by another window, etc... - the graphic you just made will be erased). To correct this problem, you need to override the paintComponent() method in the Component whose Graphic you wish to access. This method is called every time the window needs to be redrawn, so by doing all of your Graphics changes in this method you will fix the aforementioned problem of losing your Graphic.

The Graphics2D class in Java allows for more flexibility when rendering Graphical Objects (Graphics objects can be cast to Graphics2D objects). Check out this Graphics2D tutorial for more information on this subject. Other sections of the Java tutorial may be of additional assistance.

We've provided a simple Graphics example, BasicGraphics.java, that shows you what can be done with basic calls to a Graphics object.

2.1.3. Introduction to Animation

Although you will not be required to animate anything in this lab, it might be helpful to look over this section for help in future labs (wink, wink).

Making something animated is as simple as creating a Timer that periodically repaints the components to be animated.Java implements a Timer(javax.swing.Timer) to be used in animations. A Timer requires an actionListener and a delay. After you call start() on the Timer, the actionListener will be called every "delay" milliseconds. The actionListener will make the changes necessary to create the next frame and then call repaint on the component, which actually renders it.

Drawing straight to the screen is a bad idea - it looks really bad, since the individual objects will be drawn to the screen in a delayed fashion, instead of all at once. To avoid this problem, a technique called double buffering comes into play. Double buffering is a method that renders the next image to be displayed in an off-screen buffer and then swaps it with the current image (do a search on Google for double buffering in Java for some good examples). However, thanks to the helpful people at sun, if you use Swing Components, double buffering is done for you.

We've also provided you with an Animation example, SimpleAnimation.java.

2.1.4. Graphics Optimizations

Since its nearly impossible to get the complete desired graphical effects and animations by simply using Graphics2D, images often need to come into play. While there are numerous image subclasses, and practically infinite combination of ways to load them, for the sake of simplicity and speed its recommended to use BufferedImages. A BufferedImage can be drawn from a Graphics(2D) object's drawImage(...) methods, the tricky part is efficiently loading an image.

To load a BufferedImage from a file, the class you should be interested in is javax.imageio.ImageIO and more specifically the read(URL ...) method You'll notice the method is static, so there is no need to init the class and can simply be used as such below:

BufferedImage toLoad = javax.imageio.ImageIO.read( getClass().getResource("myImage.png"));

The getClass().getResource(...) method calls are simply a way to return a URL from path, referenced from the current classes location. The read(...) method used as such above, well return a fully loaded BufferedImage, meaning that you will NOT have worry about using something such as the MediaTracker to ensure the image is ready to be drawn.

If after using BufferedImages, you find your frame-rate is still being hindered, you can attempt to pass off some of the work to the graphics card by making the images hardware accelerated.

	import java.awt.*;
	import java.awt.image.*;
	public class ImageLoader
	{
	   final GraphicsConfiguration gc;

	   public ImageLoader(GraphicsConfiguration gc)
	   {
	      if(gc==null)
	      {
        	 gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
	      }
	      this.gc = gc;
	   }

	   public BufferedImage loadImage(String resource)
	   {
	      try
	      {
	         BufferedImage src = javax.imageio.ImageIO.read(getClass().getResource(resource));
	         BufferedImage dst = gc.createCompatibleImage(src.getWidth(),src.getHeight(),src.getColorModel().getTransparency());
	         Graphics2D g2d = dst.createGraphics();
	         g2d.setComposite(AlphaComposite.Src);
	         g2d.drawImage(src,0,0,null);
	         g2d.dispose();
	         return dst;
	      }
	      catch(java.io.IOException e)
	      {
	         return null;
	      }
	   }
	}
	

With a class similar to above, using the loadImage(...) method will return a fully accelerated BufferedImage ready to be rendered. If you look more carefully at the loadImage(...) method you should already see concepts that are/will be familiar to you. A BufferedImage has Graphics2D object that can be directly drawn upon just like any other rendering surface. The loadImage(...) method simply loads an image through the method explained above, then creates another "blank" BufferedImage with acceleration, and then proceeds to draw the first image onto the second. If desired, you could add further code below the g2d.drawImage(...) to draw lines/text/etc that would be permanently stored upon the image.

2.2. Threading In Java

2.2.1. Introduction

Threads allow multiple concurrent paths of execution. In Java, there are different ways to create a new thread:

  1. Implement the Runnable interface, and pass an instance of the class to the Thread constructor, or
  2. Extend Thread

When running on a machine with multiple processors, multiple threads can actually run at the same time. Under normal circumstances, on uniprocessor machines, the threads are time-shared, rapidly switching back and forth, simulating concurrent execution.

A class which is going to run as its own thread needs to have a method called run declared as public and void, with no arguments. When the thread is started with start(), run will be called and will run separately from the rest of the program.

We've provided an extremely simple threaded example called ThreadExample.java.

If you try this out, you'll see that "String One" and "String Two" will be printed on the console in some random order, since both threads are running at once and sleeping random amounts between messages.

Threads are important in a wide variety of applications. One of their most important applications is networking. Traditional networking makes heavy use of threads, because traditional networking uses blocking I/O. In other words, when you call a function, it doesn't return until it's finished. When you wait for a client to connect, you can't continue until you receive a connection. When you want to read from a connection, you can't continue until you've filled your buffer. When you write to a socket, you can't continue until all of the data has been written. Without threads, it would be nearly impossible to do anything useful since you would always be waiting on one client, unable to handle all the others.

This is where threads come in. On the server end, each client will get its own thread. That way, the blocking operations each block in their own threads, without affecting any of the other threads. On the client end, each client-server and client-client connection gets its own thread. In addition, the user interface gets its own thread, so that the user can interact with the program even when all of the network connections are blocking.

2.3. Introduction to Networking

In Java, networking is fairly simple. On the server end, the server creates a ServerSocket. This socket repeatedly accept()s, getting a Socket back from each call and spawning a thread to handle each new Socket.

On the client end, it's even easier. Just create a socket and connect() it to a listening server.

The most useful Socket functions are getInputStream() and getOutputStream(). By wrapping these streams in other IO classes (such as PrintWriter and BufferedReader), you can do all of the reading and writing you'll ever need.

We've provided you with a simple client and server pair, to see what a threaded networking program looks like. These are the ClientExample.java and ServerExample.java.

Server

  • Create a Server
  • Bind to a port
  • Wait for client connections (accept)
  • When a new connection comes in, get the socket and create and start a new thread to communicate with the client. Be sure to properly terminate the thread when you're done with it!
  • Meanwhile, go back and wait for another connection request.
  • When the server exits, close server socket and gracefully shut down all threads.

Client

  • Create a networking thread and connect to server.
  • Send/Recv Messages.
  • If you receive a client-to-client connection request, spawn a new thread and create a new socket to handle that.
  • Close Connection.

[Note]Note

Connection to the server must be threaded - otherwise, interaction with the UI will not be possible.

Here is a link to Java's Networking Tutorial: http://java.sun.com/docs/books/tutorial/networking/index.html

3. Project Requirements

3.1. Overview

NetPaint is a networked paint program where two users may interact with a single drawing canvas. Here both users can see the image and both can interact with it (e.g. drawing new parts of the image).

3.2. Networking

Before programming you team should define a protocol that will be used throughout the project. This will keep the confusion to a minimum. There are many, many different ways to define a protocol, but here is a simple one that should give you an idea of how to do it. Also for the most part, your server will be broadcasting all of the messages directly to all of the clients without any modification.

          -------------- Initial Sign-on ---------------
          > HELO: [username]

          ------------ Send a chat message ------------
          > CHAT: [username]:TEXT

          --------------- Draw a line -----------------
          > LINE: [username]:StartX,StartY:EndX,EndY:RGB Color

          ------------- Draw a Rectangle --------------
          > RECT: [username]:StartX,StartY:Height:Width:RGB Color:Fill (Y/N)

          -------------- Undo previous step -----------
          > UNDO: [username]

          ----------------- Sign-off ------------------
          > QUIT: [username]

          Also note that by using the above protocol, you need to prevent users
          from using a ':' in their username.  This protocol was done as an example,
          and does not represent the best solution or a complete one.
        

Your server must support at least 5 simultaneous clients, which means that the server must be multi-threaded. Also, the server must prevent two users from specifying the same username. All of the networking in this lab must be handled by using TCP Sockets, which are described very well in Java's networking tutorial (linked in section 2.3).

Special Note: Your team must determine a method for handling the "ordering of events." This means that it is your choice as to how "simultaneous events" (occurring on the two systems) are handled. You may select an "absolute ordering" of events which prevents inconsistencies between what each user sees **OR** you may select to use "receive ordering" where each system processes the event in the order it is received this is probably the easiest to implement but may result in minor inconsistencies between what each of the users sees.

3.3. Draw Tools

NetPaint should be able to do the following:

  • Draw Shapes: The user should be able to draw:
    • Rectangles (See java.awt.Rectangle)
    • Polygons (made up of several points) (See java.awt.Polygon)
    • Triangles (a polygon of three points) (See java.awt.Polygon)
    • Lines (See java.awt.geom.Line2D)
    • Quadratic Curves (See java.awt.geom.QuadCurve2D)
    • Ellipses (See java.awt.geom.Ellipse2D)
    Except for the lines and curves all of the above should be able to be inserted in "filled" or "un-filled" form (it is your team's option whether or not the "filled" versions have a border or not). Additionally the user should be able to choose the color of the item to be inserted (See javax.swing.JColorChooser, javax.swing.colorchooser.AbstractColorChooserPanel) and pick the appropriate control points.
  • Insert Images: Users should be able to select an image to load from their system and insert into the picture. Additionally, the user should be able to select the position of insertion and the insertion size. (Your team may decide to allow only scaling of the image where the X:Y ratio is maintained or your team may decide to permit resizing where the image's ratio is not maintained.) (See javax.swing.ImageIcon)
    [Note]Note

    You are only required to support the use of images that are available via the internet (See javax.swing.ImageIcon(java.net.URL)) and will require you to transfer only the text of the URL (e.g. "http://www.cc.gatech.edu/classes/AY2004/cs2335_spring/header.gif") between the two systems and let both independently download it from the internet.

    EXTRA CREDIT OPTION: For extra credit your team may support the use of an image that located on a local disk of one of the computers, here it is YOUR responsibility for transferring the image to the other computer. Do not assume that the other system has any other way of accessing the image (e.g. it's not on the internet or a networked drive).

  • Text Insert Text: Users should be able to insert text onto the drawing space. When inserting text, a user should have the ability to change the font of the text, along with the size and color of the text. The user will select a point on the screen which will be the starting point for displaying the text. Then the user should be able to type such that the text is constantly being updated on the graphics panel (i.e. you can not just have them type into a pop-up window). There should also be a button that allows the user to finalize their text and send it to everyone else. NOTE: When inserting text, you can assume that for each piece of text added there will be only one font type, size, and color. But the user should be able to change the font, size, and color anytime prior to pressing the "finalize" button.

TIPS: Your team may find it helpful to visit the Graphics2D tutorial at http://java.sun.com/docs/books/tutorial/2d/display/index.html

3.4. Other Required Features

  • Save Session: At any point in the editing session any user should be able save the current image to their local system. The format of the save file is up to you, but it is suggested that it be something simple (like a text file where the first thing on a line is the name of the shape added and then each parameter is listed after it).
  • Load Session: At the start of an editing session the user should be prompted as to whether they wish to start a new image or load an existing one. If the user chooses to load an image it should be loaded in and treated as if each of the loaded commands were drawn at the start of the editing session by the person who loaded the file. NOTE: Your program MUST be able to load any of the images that were saved by your program (i.e. you must support your own file format, but do not need to support any other).
  • Undo: At any point in time a user should be able to "undo" their previous command (if they just drew a line they would "undo" the whole line, not just the last point). Users should not be able to "undo" any command issued by another user (i.e. you can't undo a line drawn by someone else).
  • Super Undo (EXTRA CREDIT OPTION): For extra credit, you can support the ability to "undo" any previous command that was done at any time (if the drawing was started from a saved file, this would include any of the commands used in creating that file). You will need to provide some method for the user to view a listing of all of the previous commands and easily selecting the one to undo. Unlike the normal "undo" function (listed above) this one permits any user to "undo" any command, even if they were not the one who originally issued it.
  • Chat: The program must have a chat feature that works similar to the the basic chat programs we all know and love. :-) When displaying the messages it should display the user's name of "yourself" in one color and should display everyone else's user names in another color (red and blue are typical for this, but you are free to select any color that looks good). (See javax.swing.JTextPane, it permits the programmer to pass it HTML formatted text and then displays it as is appropriate.)

    EXTRA CREDIT OPTION: Provide support for using the most common emoticons (e.g. the user's typing :-P in the text input area will be translated into the appropriate icon when displayed in the received test area.

  • Help Screen: Describes how to use your program.
  • About Screen: Displays the name of the program and the names and GTNum's of the programmers.

4. Suggested Design

  • ServerMain -
    • Listens for new client new connections, and spawns new ServerThreads for each new incoming client connection.
    • Maintain a list of all currently connected clients
    • Must have to ability to broadcast messages to all connected clients
    • Handle client disconnects
  • ServerThread - Implement Runnable interface -OR- Extend Thread
    • Pass messages from the client to the ServerMain to be broadcast
    • Inform ServerMain of client disconnects
  • ClientNetworking - Implement Runnable interface =OR= Extend Thread
    • Open a connection to the server
    • Receive messages from the server and pass them to ClientMain. This will be the major/only functionality of the run method in the thread spawned in the ClientNetworking class.
    • Send messages to the server
    • Inform client main of server failure
  • ClientMain -
    • Initializes and displays all graphics components, associating all the components with the proper listeners.
      
         Here is an example of how your program could look:
      
        +-------------------------------------------------------------------------------------+
        | File | Options | Help                                                               |
        +-------------------------------------------------------------------------------------+
        |                                                                       |             |
        |                                                                       | Rectangle   |
        |                                                                       |             |
        |                                                                       |-------------|
        |                                                                       |             |
        |                                                                       | Circle      |
        |                                                                       |             |
        |                                                                       |-------------|
        |                    Graphics Display Panel                             |             |
        |                                                                       | Text        |
        |                                                                       |             |
        |                                                                       |-------------|
        |                                                                       |             |
        |                                                                       | Polygons    |
        |                                                                       |             |
        |                                                                       |-------------|
        |                                                                       |             |
        |                                                                       |   etc       |
        |                                                                       |    .        |
        |-----------------------------------------------------------------------|    .        |
        | Option Bar - Change Color, Font Size, Font Type, etc...               |    .        |
        +-----------------------------------------------------------------------+-------------+
        |                                                                                     |
        |                             Received Text Area                                      |
        |                                                                                     |
        +-------------------------------------------------------------------------------------+
        |                              Text Input Area                 | Send Text Button     |
        +-------------------------------------------------------------------------------------+
                      
    • Creates the ClientNetworking class and then initializes it
    • Has a method that accepts a message from the ClientNetworking and passes it to the proper component.
  • GraphicsPanel - Extend JPanel (or if you are brave, Panel)
    • Displays all of the drawn objects
  • ClientEventListener - Implements MouseListener, ActionListener, KeyListener, MouseMotionListener
    • Handle all keyboard, mouse, and button events.
  • RenderableObject - <<Abstract>> representation of an object that can be drawn and an association with its owner. Should declare an abstract method "render" which must be overridden by all subclasses.
    • RenderableImage - Contains reference to a picture (.jpeg, .gif, etc...) and have the ability to render it to a graphics object
    • RenderableText - Contains a string as well as any modifiers to the text (Size, Font Type, etc...) and have the ability to render it to a graphics object.
    • RenderableShape - Contains an object that implements the java.awt.Shape interface and have the ability to render it to a graphics object.
  • RenderableObjectList - Implements List Interface
    • Maintains a list of RenderableObjects
    • Ability to return a java.util.ListIterator
    • Must be able to save and load the list to/from a file

Suggested Package Structure:

        - lab4
          - server
            * ServerMain (ex: lab4.server.ServerMain)
            * ServerThread
          - client
            * ClientMain
            - networking
              * ClientNetworking
            - model
              * RenderableObjectList
              * RenderableImage
              * RenderableShape
              * RenderableText
              * RenderableObject
              * ClientEventListener
            - gui
              * GraphicsPanel

          Legend:
          "*" - Denotes a class
          "-" - Denotes a package
        

[Note]Note

Your team is free to come up with an entirely new design, however partial-credit opportunities will be limited if you use another design.

5. Deliverables

[Note]Note

We will not be checking PMD or Checkstyle for Part 1 or 2, but we STRONGLY recommend you keep both tools in mind when programming.

5.1. Deliverables for Part 1

For the first part of the lab, you need to fully implement the server component of your system. You should be able to ssh into your sever and perform any commands, which should then be properly broadcast to all other connected clients. Although your server must support all message types, you may still change them for the further deliverables if necessary.

One of your group members must turn in the following:

  • All source code needed to run your program.
  • The Ant build-file (build.xml), which needs to contain the following targets (with the appropriate dependencies):
    • run - Runs your program and should be the default target
    • build - Compiles your program
    • checkstyle - Runs Checkstyle on your source code
    • pmd - Runs PMD on your source code
    • jar - Creates an executable Jar of your program
    • javadoc - Creates the JavaDoc for your program (NOTE: JavaDoc must not produce any errors/warning to receive credit)
    • clean - Removes all files created from all other targets
  • The completed Part 1 readme file ("P1_README.txt").

Please submit an archive of all these files in one of the following formats: zip, tar, jar, or gzip.

5.2. Deliverables for Part 2

For the second part of the project, you must submit everything from Part 1 and the basic client, which needs to support the following features:

  • Chat
  • Drawing of a simple black line
  • Undo
  • Must properly communicate with the Server and receive updates from other clients
  • About screen

One of your group members must turn in the following:

  • All source code needed to run your program.
  • The Ant build-file (build.xml)
  • The completed Part 2 readme file ("P2_README.txt").

Please submit an archive of all these files in one of the following formats: zip, tar, jar, or gzip.

5.3. Deliverables for Part 3

For the final part of the lab, you must submit everything from Part 1 and Part 2, but now everything must be fully functional.

One of your group members must turn in the following:

  • All source code needed to run your program.
  • The Ant build-file (build.xml)
  • The completed Part 3 readme file ("P3_README.txt").

Please submit an archive of all these files in one of the following formats: zip, tar, jar, or gzip.

6. Extra Credit

You may receive up to 20 points of extra credit for Part 3 of this lab. You may attempt the following for extra credit (and their point values):

  • Amazing UI (to be determined by your TA) - 5pt
  • Image File Transfer (Non-URL) - 10pt
  • Super Undo - 5pt
  • Emoticons - 2pt
  • File Saving/Loading from (non-trivial) XML - 5pt

[Important]Important

Unless arranged in advance, your TA will be grading this assignment on the RedHat systems available in the States Lab and will be using the Java tools provided in the "~cs2335/" directory. It is YOUR responsibility to verify that your program will work on these systems prior to submitting it.