The magnify applet lets you zoom in on a portion of the image. The window's magnification level is two, which means that the applet doubles the image within the magnify window. Since the size of the magnify window is 100x100 pixels, the portion inside it that gets magnified must be 50x50 pixels. To center the magnify portion, so that the applet always magnifies the 50x50 portion that is in the middle of the 100x100 window, the applet uses the (x,y) coordinates of (25,25) relative to the top-left of the magnify window. In this way, the image is between (25,25) and (75,75) inside the 100x100 window.

Furthermore, if the applet were to draw the background image so that the top-left of the image is at the origin, the applet could not magnify the top 25 pixel lines and the left 25 pixel lines of the image. Therefore, the Magnify applet always positions the image so that the top-left corner is at the (50,50) mark of the applet window, one-half of the box width and height (i.e. the image does not cover the whole applet window).

Let's look at the code, which can be found in Y.java:

To start with, the applet declares variables to store the graphics context, the background image, the image width and height, and a boolean that tells the applet when the image has been loaded:

    Graphics g;
    Image background;
    int width;
    int height;
    boolean done_loading_image = false;
The next few variables are the x and y coordinates of the magnify box as well as the width and height of the box. The size is 100x100 pixels:
    int box_x = 0;
    int box_y = 0;
    int box_width = 100;
    int box_height = 100;

Initializing the Applet

In the init() function, the applet gets the graphics context and the background image, and loads the image off screen. The getGraphics() function is used to get the graphics context. The getGraphics function is a member of the Component class, which is one of the parents of the Applet class:
        g = getGraphics();
Next, the applet uses getImage to retrieve the background image:
        background = getImage(getCodeBase(), "./images/YANKEE.GIF");
The getImage and getCodeBase() functions are methods of the Applet class. The getCodeBase function returns the applet's base URL (Unique Resource Locator), which is the current directory or the directory specified by the codebase PARAMeter in the HTML file.

The getImage function does not actually load the image into the applet window. The function provides, an object the applet can later use to draw the image within the applet window. To display the image, the applet has several choices. First, the applet can draw the image right in the applet window. Unfortunately, drawing the image within the applet window in this way causes considerable flashing. Also, the function that draws the image, drawImage, returns to the applet immediately and does not wait for the image to finish loading. To get around this, the applet creates an image off screen (outside off the applet window), and then uses the drawImage function to display the image after it is complete.

The applet uses createImage to create the off-screen image. The createImage function is a Component class function and takes the image dimensions as parameters:

        Image offscreen_image = createImage(500, 333);
Next, the applet needs the graphics context for the off-screen image. It uses the Image-class getGraphics method:
        Graphics offscreen_GC = offscreen_image.getGraphics();
Finally, the applet uses the drawImage function to display the image within the applet window. The parameters to the drawImage function are the image, the top-left (x,y) coordinates, and the ImageObserver class:
        offscreen_GC.drawImage(background, 0, 0, this);
The ImageObserver is an interface defined in the java.awt.image package. The applet uses the this keyword as the ImageObserver parameter, which means that the applet must contain the ImageObserver interface function, which is called imageUpdate. If, in the drawImage function, you specify null as the last parameter (instead of this), then the imageUpdate function does not get called.

Events: Mouse Movement

When you move the mouse, the Magnify applet moves the magnify box. The (x,y) coordinates of the mouse location are passed as arguments to the mouseMove function:
    public boolean mouseMove(Event evt, int x, int y)
Before, it draws the new box, it must first erase the old box by calling erase_box(). Then it stores the new x and y coordinates in box_x and box_y respectively, so that the applets knows where to erase the box next time (the mouse moves).
        erase_box();

        box_x = x;
        box_y = y;
If you look at the picture right, you notice that the band surrounding the image is half the size of the magnifying box's width and height. Therefore, when the magnify box is at the bottom-right edge of the image, its coordinates will be the width and the height of the image. To prevent the box from going past these coordinates, the function compares the box location to the image's width and height:
        if (box_x > width)
          box_x = width;

        if (box_y > height)
          box_y = height;
Now, using the x and y coordinates ot the top-left corner of the box, the function calls the draw_box function to draw the magnify box and to magnify its contents:
        draw_box();

Drawing the Box

To magnify the image, the applet creates a clip rectangle. Rather than chancing that the clip rectanlge causes problems to the graphics context, the applet creates a copy of the graphics context. Then the applet applies the clip rectanlge to the graphics context copy. The clipRect function takes the coordinates and size of the magnify box:
    void draw_box()
      {
        Graphics g2;
        g2 = g.create();

        g2.clipRect(box_x, box_y, box_width, box_height);
Now for the tricky part, to double the 50x50 box centered inside the magnify box, the applet can give the drawImage function the negative (x,y) coordinates as the image origin. The coordinates that the applet wants to use are such that if you were to double the image starting at those coordinates, you would double the 50x50 inside the magnify box.

If the image was drawn at the origin and you negated the magnify box's (x,y) coordinates, you would double the portion of the image which starts at the top-left corner of the magnify box. Since the image is actually offset by half the width and height of the magnify box. you should add that amount to the (x,y) coordinates of the location at which you want to draw the double image:

        g2.drawImage(background,
                     -box_x+box_width/2, -box_y+box_height/2,
                     width*2, height*2, null);
Finally, draw a red box around the magnify box for clarity, using the original graphics context:
        g.setColor(Color.red);
        g.drawRect(box_x, box_y, box_width-1, box_height-1);
      }

Erasing the old Box

To erase the magnify box, you create a clip rectangle and redraw the background in its normal size:
    void erase_box()
      {
        Graphics g2;
        g2 = g.create();

        g2.clipRect(box_x, box_y, box_width, box_height);

        g2.drawImage(background, box_width/2, box_height/2, null);
However, since the image is slightly offset from the origin, this code will not erase the light-gray band that surrounds the image. So, for this you do some simple checking. If the box_x or box_y value fell inside a gray area left, top, right, bottom), then refill that rectangle grey.
        if (box_x < box_width/2)
          {
            g.setColor(Color.lightGray);
            g.fillRect(0, 0, box_width/2, height+box_height);
          }

        if (box_y < box_height/2)
          {
            g.setColor(Color.lightGray);
            g.fillRect(0, 0, width+box_width, box_height/2);
          }

        if (box_x > (width-box_width/2))
          {
            g.setColor(Color.lightGray);
            g.fillRect(width+box_width/2, 0,
                       box_width/2, height+box_height);
          }

        if (box_y > (height-box_height/2))
          {
            g.setColor(Color.lightGray);
            g.fillRect(0, height+box_height/2,
                       width+box_width, box_height/2);
          }
      }

Updating the image

As stated before, to use the ImageObserver class, you must use the imageUpdate function. As it turns out, each time the drawImage function runs, it creates a thread that in turn calls imageUpdate. This function takes six parameters: the image, the info flag (which specifies how much of the image has been drawn), the image's top-left (x,y) coordinates, and the width and height of the image:
   public boolean imageUpdate(Image img, int infoflags, int x, int y, int w, int h)
The function uses the infoflag parameter to determine how much of the image has been drawn. When the infoflags parameter is equal to the ALLBITS constant, the image is done, and we can set the done_loading_image boolean to true:
        if (infoflags == ALLBITS)
          {
            width = background.getWidth(this);
            height = background.getHeight(this);

            resize(width+box_width, height+box_height);

            done_loading_image = true;
            repaint();

            return false;
          }
        else
          return true;
When the image is done, the applet determine's the image's width and height. and resizes the applet window to hold the image and the gray band. Then it will repaint() the screen. It returns false so drawImage does not call this function anymore.

NOTE: the resize function only works in the appletviewer. Applets in Web pages cannot be resized, and using the resize function will have no effect when using a browser to see the applet.

Painting the image

When the image is loaded completely, the painting function simply draws the image at an offset and then draws the magnify box. It also displays a message in your browsers status bar. If the image is not loaded yet, then the message "Magnify: loading image..." is put in your browsers statusbar:
    public void paint(Graphics _g)
      {
        if (!done_loading_image)
          showStatus("Magnify:  loading image...");

        else
          {
          showStatus("Magnify:  done");
          g.drawImage(background, box_width/2, box_height/2, this);
          draw_box();
          }
      }



Back to Lecture3