How NOT to draw in Java...

Posted on January 10, 2008 07:15 by Brian Knoblauch

I ran across a floppy disk (remember those?) a short while back that had some of my old assembly language code.  Thought it'd be fun to convert some of those old programs to Java.  Started with the easiest one, my "Hello World" of the graphics world.  Basic idea is that it does a Sierpinski gasket by randomly hopping about.  A no-brainer to code, so it lets one get familiar with the graphics environment.  Here's the core of the original assembly code (leaving out the random number library code and the stack frame):

.public         main
.proc           main            auto
                mov     ax,0012h
                int     10h
                call    rand_init
                mov     cx,319
                mov     dx,239
again:          mov     bx,3
                call    randr_w
                cmp     ax,1
                jb      zero
                je      one
                add     cx,639
                shr     cx,1
                add     dx,479
                shr     dx,1
                mov     al,4
                jmp     continue
zero:           add     cx,319
                shr     cx,1
                shr     dx,1
                mov     al,1
                jmp     continue
one:            shr     cx,1
                add     dx,479
                shr     dx,1
                mov     al,2
continue:       mov     ah,0ch
                int     10h
                mov     ah,01h
                int     16h
                jz      again
                mov     ax,0003h
                int     10h
                xor     ah,ah
                int     16h
                ret                     ;exit to DOS with ERRORLEVEL=0
.endp           main

So, I grab my various Java books and start banging out some quick code.  I rapidly end up with the following (after following the suggestions/guidelines in the various books):

package chaos;

import  java.awt.*;
import  java.awt.event.*;

public class Main extends Frame {
    public Main() {
        addWindowListener(new MyWindowAdapter());
    }
    @Override
    public void paint(Graphics g) {             
        Dimension d=this.getSize();       
        int x=(d.width-1)/2;
        int y=(d.height-1)/2;               
        int minx=this.getInsets().left;
        int maxx=d.width-this.getInsets().right-1;
        int miny=this.getInsets().top;
        int maxy=d.height-this.getInsets().bottom-1;       
        g.setColor(new Color(0,0,0));
        g.fillRect(minx,miny,maxx,maxy);
        for (int count=0; count <=500000; count++) {
            switch((int) Math.floor(Math.random()*3)) {
                case 0:
                    x=(x+((d.width-1)/2))/2;
                    y=(y+miny)/2;
                    g.setColor(new Color(255,0,0));
                    break;
                case 1:
                    x=(x+maxx)/2;
                    y=(y+maxy)/2;
                    g.setColor(new Color(0,255,0));
                    break;
                case 2:
                    x=(x+minx)/2;
                    y=(y+maxy)/2;
                    g.setColor(new Color(0,0,255));
                    break;
            }
            g.drawLine(x,y,x,y);
        }
    }
    public static void main(String[] args) {
        Main output = new Main();
        output.setSize(new Dimension(800, 600));
        output.setResizable(false);
        output.setTitle("Chaos");       
        output.setVisible(true);
    }
}
   
class MyWindowAdapter extends WindowAdapter {
    @Override
    public void windowClosing(WindowEvent we) {
        System.exit(0);
    }
}

So far, so good.  Is reasonably simple (although I'm somewhat dismayed by the number of lines of code compared to assembly and annoyed by having to calculate insets to avoid painting UNDER the window borders and title bar).  Runs great.  EXCEPT, it doesn't redraw properly on window resizes (unless you cover up the window or do a minimize/maximize sequence)!  Despite the paint code being run again, the actual screen updates for the portion that was already visible never happen!  It appears that the JVM is optimizing them away, despite them being in the paint code.  Many attempts to avoid that behaviour (in all kinds of different ways) fail.  I finally give up, disallow resizing in that code (to just avoid the situation) and ask a Java guru at work.  He did some research and tweaked my code to look like this:

import java.awt.*;
import java.awt.event.*;

public class Chaos {
        public static void main( String[] args ) {
                Frame f = new Frame("Chaos");
                f.addWindowListener( new WindowAdapter() {
                        public void windowClosing( WindowEvent e ) {
                                System.exit(0);
                        }
                });
                f.add(new ChaosCanvas());
                f.pack();
                f.setVisible(true);
                f.setSize( new Dimension(800, 600) );
        }
}

class ChaosCanvas extends Canvas {
        public void paint(Graphics g) {
               Dimension d=this.getSize();
               int x=(d.width-1)/2;
               int y=(d.height-1)/2;
               int minx=0;
               int maxx=d.width;
               int miny=0;
               int maxy=d.height;
               g.setColor(new Color(0,0,0));
               g.fillRect(minx,miny,maxx,maxy);
               for (int count=0; count <=500000; count++) {
                   switch((int) Math.floor(Math.random()*3)) {
                       case 0:
                           x=(x+((d.width-1)/2))/2;
                           y=(y+miny)/2;
                           g.setColor(new Color(255,0,0));
                           break;
                       case 1:
                           x=(x+maxx)/2;
                           y=(y+maxy)/2;
                           g.setColor(new Color(0,255,0));
                           break;
                       case 2:
                           x=(x+minx)/2;
                           y=(y+maxy)/2;
                           g.setColor(new Color(0,0,255));
                           break;
                   }
                   g.drawLine(x,y,x,y);
               }
           }
}

Now it also works properly on resizes!  Not only that, but the whole insets problem goes away too!  The major difference being that he paints into a Canvas that's packed into the Frame, NOT directly into the Frame.  In hindsight it sounds VERY obvious, however, all those books that I have that talk about graphics have you painting directly into a Frame.  Not a single one of them on my shelf even mentions the Canvas object!  Thank you David, and hopefully this helps out someone else that's using one of those millions of Java books that only talks about doing graphics inside empty Frames!

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Related posts

Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

August 28. 2008 03:29