The Graphics class
The heart of WFC graphics is the Graphics class, which is similar to the CDC class in MFC. It contains too many methods to list here, so you'll have to check the API documentation for methods we don't use in this article.
The most common way of using Graphics is to use the instance within the PaintEvent passed to your paint method. The code in Listing A and Listing B creates the simple drawing in Figure A using this technique.
Figure A: The WFC classes can be used to create simple drawings.
Listing A:
Drawing a simple text string
private void Form1_paint(Object source, PaintEvent e) {
for (int x = 0; x < 300; x++) {
for (int y = 0; y < 300; y++) {
e.graphics.setPixel(x, y, Color.BLACK);
}
}
e.graphics.drawString("WFC Graphics are fun!", 20,
=>20);
}
Listing B:
Flashing random colors on the screen
private void Form1_paint(Object source, PaintEvent e) {
int r = (int)(Math.random() * 255);
int g = (int)(Math.random() * 255);
int b = (int)(Math.random() * 255);
e.graphics.fill(this.getClientRect(), new Brush(
=>new Color(r, g, b)));
}
private void timer1_timer(Object source, Event e) {
invalidate();
}
Pens and BrushesTo draw with different styles and colors, you need to use different Pens and Brushes. Pen and Brush are the WFC equivalent of CPen and CBrush from MFC. Take a look at Figure B and Listing C for an example of how to use them.
Figure B: The Pen and Brush methods allow you to draw with different styles and colors.
Listing C:
Creating graphics using the WFC Pen and Brush objects
private void Form1_paint(Object source, PaintEvent e) {
e.graphics.fill(getClientRect(),
=>new Brush(Color.WHITE));
e.graphics.setPen(new Pen(Color.BLACK,
=>PenStyle.SOLID, 8));
e.graphics.setBrush(new Brush(Color.RED));
e.graphics.drawEllipse(getClientRect());
}
As you can see in Figure B, the Pen determines the look of the actual drawing, while the Brush fills it in. There are many different styles of Pens and Brushes available. Instead of PenStyle.SOLID, we could have used several other patterns, such as PenStyle.DOT or PenStyle.DASH. Note that these pen styles only really show up using a width of at least one or two (in this example we've used eight).
Double buffering
Double buffering is a very important concept in computer animation. The idea is to do all of your drawing on an off-screen buffer, then draw the buffer to the screen. This eliminates flickering, because the user never sees the screen being cleared and redrawn; the new image appears almost instantly. Try the following experiment: Listing D draws directly to the screen, while >Listing E uses double buffering.
Listing D:
Code for drawing without double buffering
private void Form1_paint(Object source, PaintEvent e) {
int x = getClientSize().x;
int y = getClientSize().y;
for (int i = 1; i <= 100; i++) {
e.graphics.fill(getClientRect(),
=>new Brush(Color.WHITE));
e.graphics.setPen(new Pen(Color.BLACK,
=>PenStyle.SOLID, 8));
e.graphics.setBrush(new Brush(Color.RED));
int w = (int)((((float)i)/100) * x);
int h = (int)((((float)i)/100) * y);
e.graphics.drawRect(0, 0, w, h);
}
}
private void Form1_click(Object source, Event e) {
invalidate();
}
Listing E:
Code for drawing with double buffering
Graphics g;
Bitmap bmp = null;
private void Form1_paint(Object source, PaintEvent e) {
int x = getClientSize().x;
int y = getClientSize().y;
if (bmp != null) {
bmp.dispose();
bmp = null;
}
bmp = new Bitmap(x, y);
g = bmp.getGraphics();
for (int i = 1; i <= 100; i++) {
g.fill(getClientRect(), new Brush(Color.WHITE));
g.setPen(new Pen(Color.BLACK, PenStyle.SOLID, 8));
g.setBrush(new Brush(Color.RED));
int w = (int)((((float)i)/100) * x);
int h = (int)((((float)i)/100) * y);
g.drawRect(0, 0, w, h);
e.graphics.drawImage(bmp, 0, 0);
}
}
private void Form1_click(Object source, Event e) {
invalidate();
}
Both Listing D and Listing E draw a box that grows out of the corner whenever the mouse is clicked. Figure C shows this in action. However, with the code in Listing D, the image flickers and tears violently because the client area is being cleared and redraws at an incredibly high speed.
Figure C: When the mouse is clicked, our code draws a box out of the corner of the screen.
On the other hand, the code in Listing E draws that same box growing very smoothly. That's because the clearing and redrawing is done off-screen in memory, then pasted to the client area almost instantaneously.
Images
The other main use for the Bitmap class is loading and displaying stored graphics, such as a GIF or JPEG file. To load an image, you can either pass the file to the Bitmap constructor, as shown in Listing F and G, or later use Bitmap.loadImage
Listing F:
Using the drawImage method to create an image at its original size
Bitmap bmp = new Bitmap("zd.gif");
private void Form1_paint(Object source, PaintEvent e) {
e.graphics.drawImage(bmp, 0, 0);
}
Listing G:
Using the drawImage method to resize an image
Bitmap bmp = new Bitmap("zd.gif");
private void Form1_paint(Object source, PaintEvent e) {
int x = getClientSize().x;
int y = getClientSize().y;
for (int i = 1; i <= 100; i++) {
int X = (int)((((float)i)/100) * x);
int Y = (int)((((float)i)/100) * y);
e.graphics.drawImage(bmp, 0, 0, X, Y, true);
}
}
private void Form1_click(Object source, Event e) {
invalidate();
}
In Listing F, which creates the image in Figure D, we simply load an image and display it using Graphics.drawImage. This draws the image at its original size. However, in Listing G, which creates the images in Figures E and F, we use a different version of drawImage to animate the image by scaling the image out of the corner to fill the window, as we did in the example in Listing E.
Figure D: We can use the drawImage method to create an image at its original size.
Figure E: Use a different version of drawImage to create a scaled image.
Figure F: Or, use drawImage to severely stretch the image.
You can also use Bitmaps as Brushes by passing the Bitmap to the Brush's constructor. When a Brush is a Bitmap, it tiles the image wherever the Brush is used. See Listing H, which creates Figure G, for an example.
Listing H:
Using Bitmaps as Brushes
Bitmap bmp = new Bitmap("zd.gif");
private void Form1_paint(Object source, PaintEvent e) {
e.graphics.fill(getClientRect(), new Brush(bmp));
}
Figure G: You can use Bitmaps as Brushes to create the kind of tiling effect shown here.
Full-screen graphics If you create your own instance of Graphics using Graphics.createScreenGraphics, you can display your graphics full screen. This can be dangerous, since you can draw over other applications, but you can also have some fun with it. Try running the example in Listing I, or better yet, trick a co-worker into running it. Note: This example assumes you are using a 1024 by 768 display. If you are using a different resolution, modify the loops as needed.
Listing I:
Code to darken the whole screen
public Form1() {
// Required for Visual J++ Form Designer support
initForm();
// TODO: Add any constructor code after initForm call
Graphics g = Graphics.createScreenGraphics();
for (int x = 0; x < 1024; x++) {
for (int y = 0; y < 768; y++) {
Color c = g.getPixel(x, y);
int R = c.getRed();
int G = c.getGreen();
int B = c.getBlue();
g.setPixel(x, y, new Color(R / 2, G / 2, B / 2));
}
}
}
This example will darken your screen to about 50 percent. To go back to normal, maximize a window, and move the Taskbar.
Conclusion In this article, we discussed how to use graphics in WFC using the Graphics, Pen, Brush, and Bitmap classes. C++/MFC veterans will notice that these are very familiar, since they are based on their MFC counterparts CDC, CPen, CBrush, and CBitmap. The examples in this article should help you get started with any graphical projects you may have.
Copyright © 1999, ZD
Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of ZD
Inc. Reproduction in whole or in part in any form or medium without
express written permission of ZD Inc. is prohibited. All other product
names and logos are trademarks or registered trademarks of their
respective owners.