| <!doctype html public "-//w3c//dtd html 4.0 transitional//en"> |
| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
| <meta name="GENERATOR" content="Mozilla/4.74 [en] (Windows NT 5.0; U) [Netscape]"> |
| <meta name="ProgId" content="FrontPage.Editor.Document"> |
| <title>Introduction to SWT Graphics</title> |
| <link |
| href="../default_style.css" |
| rel=stylesheet> |
| </head> |
| <body link="#0000FF" vlink="#800080"> |
| <div align="right"><font size="-2">Copyright © 2003 International Business Machines |
| Corp.</font> |
| <table border=0 cellspacing=0 cellpadding=2 width="100%"> |
| <tr> |
| <td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font face="Arial,Helvetica" color="#FFFFFF"> Eclipse |
| Corner Article</font></b></td> |
| </tr> |
| </table> |
| </div> |
| <h1> <img src="images/Idea.jpg" height=86 width=120 align=CENTER></h1> |
| <center> |
| <h1> Graphics Context - Quick on the draw</h1> |
| </center> |
| <blockquote><b>Summary</b> <br> |
| The package <code>org.eclipse.swt.graphics</code> contains classes that allows |
| management of graphics resources. Graphics can be drawn on anything that implements |
| <code>org.eclipse.swt.graphics.Drawable, </code>which includes <code>org.eclipse.swt.widgets.Control</code> |
| and <code>org.eclipse.swt.graphics.Image</code>. The class <code>org.eclipse.swt.graphics.GC |
| </code>encapsulates all of the drawing API, including how to draw lines and |
| shapes, draw text and images and fill shapes. This article shows how to |
| use a GC to draw onto an Image, or onto a control through its paintEvent callback. |
| The Canvas control, specifically designed for drawing operations, has a number |
| of constructor style bits that allow you to determine when and how painting |
| occurs, and the article shows how to use these. |
| <p><b>By Joe Winchester, IBM</b> <br> |
| July 3, 2003</blockquote> |
| <hr width="100%"> |
| <br> |
| Table of contents: |
| <ul> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Graphics Context">Graphics |
| Context</a></li> |
| <ul> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Graphics Context">Drawing |
| on an Image</a></li> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Graphics Context">Drawing |
| on a Control</a></li> |
| </ul> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Clipping">Clipping</a></li> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Canvas">Canvas</a></li> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Drawing lines and">Drawing |
| lines and shapes</a></li> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Drawing Text">Drawing |
| text</a></li> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Filling shapes">Filling |
| shapes</a></li> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#XOR">XOR</a></li> |
| <li> <a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Drawing Images">Drawing |
| images</a></li> |
| </ul> |
| The SWT graphics system uses the same coordinate convention used for controls, |
| where the origin is at 0,0 is the top left corner, and the x axis increases to |
| the right while the y axis increases downwards. The Point class is used |
| to represent both a location (a position in the coordinate system) and also an |
| offset (there is no Dimension class in SWT - the size of a rectangle is represented |
| by a Point capturing the x and y offset from its origin). |
| <h1> <a NAME="Graphics Context"></a>Graphics Context</h1> |
| Graphics can be drawn on anything that implements <code>org.eclipse.swt.graphics.Drawable</code>. |
| This includes a Control, an Image, a Display device or a Printer device. |
| The class <code>org.eclipse.swt.graphics.GC</code> is a graphics context that encapsulates |
| the drawing operations that can be performed. There are two common ways |
| to use a GC; either by creating one using the Drawable instance as a constructor |
| argument, or else using a GC that's given to you as part of a paintEvent callback. |
| <h2> Drawing on an Image</h2> |
| The code below creates a GC with an image as its argument and draws two lines |
| on it, <img src="images/tag_1.gif" height=13 width=24> one from the top left (0,0) |
| to the bottom right, and <img src="images/tag_2.gif" height=13 width=24> one from |
| the top right to the bottom left. |
| <p><code> Image image = new Image(display,"C:/devEclipse_02/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");</code> |
| <br> |
| <code> GC gc = new GC(image);</code> <br> |
| <code> Rectangle bounds = image.getBounds();</code> <br> |
| <img src="images/tag_1.gif" height=13 width=24><code> gc.drawLine(0,0,bounds.width,bounds.height);</code> |
| <br> |
| <img src="images/tag_2.gif" height=13 width=24><code> gc.drawLine(0,bounds.height,bounds.width,0);</code> |
| <br> |
| <img src="images/tag_3.gif" height=13 width=24><code> gc.dispose();</code> <br> |
| <code> image.dispose();</code> <br> |
| |
| <table BORDER > |
| <caption> |
| <TBODY> |
| <br></TBODY> |
| </caption> |
| <tr> |
| <td> <i>Original image </i></td> |
| <td><i>Image after the GC draws across it</i></td> |
| </tr> |
| <tr> |
| <td><img src="images/image_lg_orig.gif" height=164 width=115></td> |
| <td><img src="images/image_lg_lines.gif" height=164 width=115></td> |
| </tr> |
| </table> |
| <p>When creating a GC you must be responsible for disposing it <img src="images/tag_3.gif" height=13 width=24> |
| by calling its dispose() method. For more information on how to manage SWT resources |
| see <a href="http://www.eclipse.org/articles/swt-design-2/swt-design-2.html">SWT: |
| The Standard Widget Toolkit.</a> A GC that's instantiated by the program |
| should be drawn upon and then disposed as soon as possible. This is because |
| each GC requires an underlying platform resource, and on some operating systems |
| these may be scarce, such as Windows 98 that only allows 5 GC objects to be |
| created before it runs out resources. |
| <h2> Drawing on a Control</h2> |
| The class <code>org.eclipse.swt.widgets.Control</code> is a Drawable, so you could |
| draw onto a Control the same way you draw onto an Image (passing the Control as |
| the argument of GC to draw onto it), however unlike drawing on an image (that |
| permanently changes the data making up the graphic), if you use a GC to draw onto |
| a control you must be aware that when the operating system itself draws the control |
| it will overwrite your changes. The correct way to draw onto a control is |
| by adding a listener to its paint event. The listener class is <code>org.eclipse.swt.events.PaintListener</code>, |
| and the callback method argument is an instance of <code>org.eclipse.swt.events.PaintEvent</code>. |
| The PaintEvent includes a GC that you can send message to, that is already prepared |
| to draw onto the control and includes the damaged area. |
| <p>The following code <img src="images/tag_1.gif" height=13 width=24> adds a paint |
| listener to a Shell, and in the paintControl callback method <img src="images/tag_2.gif" height=13 width=24> |
| draws a line from the origin to bottom right corner. |
| <p><code> Shell shell = new Shell(display);</code> <br> |
| <img src="images/tag_1.gif" height=13 width=24><code> shell.addPaintListener(new PaintListener(){</code> |
| <br> |
| <code> public void paintControl(PaintEvent |
| e){</code> <br> |
| <code> Rectangle |
| clientArea = shell.getClientArea();</code> <br> |
| <img src="images/tag_2.gif" height=13 width=24><code> |
| e.gc.drawLine(0,0,clientArea.width,clientArea.height);</code> <br> |
| <code> }</code> <br> |
| <code> });</code> <br> |
| <code> shell.setSize(150,150)</code> |
| <p><img src="images/ShellDrawnLine.gif" height=215 width=267> |
| <p>Although the size of the Shell is set to (150,150), the area that can be drawn |
| onto is smaller. This is known as the client area, and takes into account |
| any trim or borders; for a Shell this includes its edges, titlebar and menubar. |
| To determine the clientArea of any Composite use the method <code>getClientArea().</code> |
| <p>The application always get a paint event after the underlying OS has drawn |
| the control, so any drawing done to the paint event's GC will be shown on top |
| of the control. There are some exceptions to this, such as for a ToolBar |
| where on certain platforms the items are heavyweight controls that can't be |
| drawn on top of, however this is not considered normal behavior. For general-purpose |
| drawing the control <code><a href="http://eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html#Canvas">org.eclipse.swt.widgets.Canvas</a></code> |
| can be used that is optimized for graphics operations. |
| <h1> <a NAME="Clipping"></a>Clipping</h1> |
| The clipping area of a GC is the portion onto which visible drawing occurs. By |
| default a GC is clipped to the bounds of the drawable that it was constructed |
| with. Altering the clipping area of a GC allows you to create graphic effects. |
| An example of this is if you wanted to fill a triangle shape that had a portion |
| of its edges missing. One way to do this would be to draw multiple polygons |
| rectangles making up the shape, another way however is to fill the shape, but |
| to clip the GC so that the edges are outside the clipping area. |
| <p><code> shell.addPaintListener(new PaintListener() {</code> <br> |
| <code> public void paintControl(PaintEvent |
| e) {</code> <br> |
| <code> Rectangle |
| clientArea = shell.getClientArea();</code> <br> |
| <code> int width |
| = clientArea.width;</code> <br> |
| <code> int height |
| = clientArea.height;</code> <br> |
| <img src="images/tag_2.gif" height=13 width=24><code> |
| e.gc.setClipping(20,20,width - 40, height - 40);</code> <br> |
| <code> e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));</code> |
| <br> |
| <img src="images/tag_1.gif" height=13 width=24><code> |
| e.gc.fillPolygon(new int[] {0,0,width,0,width/2,height});</code> <br> |
| <code> }</code> <br> |
| <code> });</code> |
| <p>This code draws a triangle on a Shell as a polygon from the <img src="images/tag_1.gif" height=13 width=24> |
| top left, top right, and center of the bottom edge. Beforehand however, the |
| GC is clipped with a rectangle <img src="images/tag_2.gif" height=13 width=24> |
| that reduces the client area with a margin of 20 pixels wide, so only the clipped |
| rectangle is draw. |
| <p><img src="images/ClippedTriangles.gif" height=191 width=562> |
| <p>When a paintEvent occurs for a control the GC is always clipped to just the |
| area that needs repainting. For example, if another window is moved in front |
| of an SWT shell and then moved away, the newly revealed portion of the GUI is |
| marked as damaged and a paint event is queued. When the paint event occurs, |
| the <code>paintControl(PaintEvent evt)</code> method argument contains the area |
| of the Control that requires redrawing in its x, y, width and height fields. |
| The damage to the control can be complex containing multiple disjoint rectangles, |
| and if there is more than one damaged area of a control when the paint event |
| occurs the damaged areas are merged into a single rectangle representing the |
| union of the damaged areas. This step is performed by the underlying platform |
| and is designed to aid performance as multiple paint events are processed in |
| a single callback. |
| <p>In the example above that fills a clipped triangle each time the <code>paintControl(PaintEvent)</code> |
| method is called, an optimization could be to look at the PaintEvent's area. |
| It maybe that the paint event doesn't even intersect the shape being drawn in |
| which case no drawing need occur, or if only a portion of the drawing is needs |
| repainting then only this is drawn. Depending on the type of drawing, |
| working out which portion of a GC to selectively redraw however can be more |
| expensive than just relying on the fact the GC is clipped, and in practice code |
| inside paint events often just ignores the damaged area and re-does all of the |
| GC drawing, relying on clipping to ensure only the damaged area is refreshed. |
| <p>If a program needs to manually damage an area of a control this can be done |
| using <code>Control.redraw(int x, int y, int width, int height)</code>, or <code>Control.redraw()</code> |
| to damage the entire client area. The area is marked as damaged and will |
| be included in the next paint event that occurs. To cause a synchronous |
| and immediate paint event to occur use the method <code>Control.update()</code> |
| that will force all outstanding paint requests to be processed for the control. |
| If there are no paint request (i.e. none of the client area is damaged), |
| then <code>update()</code> will do nothing. |
| <h1> <a NAME="Canvas"></a>Canvas</h1> |
| Although any Control can be drawn onto through its paintEvent, the subclass org.eclipse.swt.widgets.<code>Canvas</code> |
| is specifically designed for graphics operations. This can be done either by using |
| a Canvas and adding a paint listener, or through subclassing to create a re-usable |
| Custom Control. Canvas has a number of style bits that can be used to affect |
| how painting occurs. |
| <p>The default behavior for a Canvas is that before it paints itself the entire |
| client area is filled with the current background color. This can create |
| screen flicker, because if the paintEvent also draws onto the GC, then the user |
| sees a flash between the original background being filled, and the drawing occurring. |
| One way to avoid this is to use the style bit SWT.NO_BACKGROUND when creating |
| the Canvas. This prevents the native background from being drawn, however |
| it means the program must be responsible for drawing every pixel of the client |
| area. |
| <p>When a widget is resized a paint event occurs. This can create screen |
| flicker as repeated repainting of the client area occurs. Flicker is also |
| known as flash, and the style bit SWT.NO_REDRAW_RESIZE can be used to reduce |
| this. When NO_REDRAW_RESIZE is used and the control's size is reduced |
| no paint event is generated. This means there will be no flash as the |
| control is not unnecessarily redrawn, and if the size is increased then the |
| paint event's GC is clipped set to just the area that needs repainting. |
| This is the newly revealed bottom and right edge rectangles in a shape like |
| a backwards L. |
| <p>The style bit NO-REDRAW_RESIZE works well to reduce flicker if a fixed-size |
| drawing is being done on the GC, and each newly exposed area generates a paint |
| event onto which a fresh piece of the drawing occurs. Incorrectly used |
| however, NO_REDRAW_RESIZE can lead to a graphic effect known as cheese. |
| Cheese is a general term that applies when a portion of a widget is not updated |
| correctly following a resize. An example of is shown below where the paint |
| event needs to update the entire client area, such as <img src="images/tag_1.gif" height=13 width=24> |
| fill it with an oval shape. Because no paint event occurs when the window |
| is reduced in size the shape is not redrawn when the size is decreased, and |
| when the window is made larger because the GC is clipped to just the damaged |
| area Cheese occurs in the newly expanded area because the previous drawing is |
| not erased ( as the paint event is clipped to only the newly exposed area ). |
| <p><code> shell.setLayout(new FillLayout());</code> <br> |
| <img src="images/tag_2.gif" height=13 width=24><code> final Canvas canvas = new Canvas(shell,SWT.NO_REDRAW_RESIZE);</code> |
| <p><code> canvas.addPaintListener(new PaintListener() {</code> <br> |
| <code> public void paintControl(PaintEvent |
| e) {</code> <br> |
| <code> Rectangle |
| clientArea = canvas.getClientArea();</code> <br> |
| <code> e.gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));</code> |
| <br> |
| <img src="images/tag_1.gif" height=13 width=24><code> |
| e.gc.fillOval(0,0,clientArea.width,clientArea.height);</code> <br> |
| <code> }</code> <br> |
| <code> });</code> |
| <p>When the canvas size is increased the GC is clipped to just the area that needs |
| repainting and cheese occurs. |
| <p><img src="images/Cheese_Complete.gif"> |
| <p>To correct this problem in line <img src="images/tag_2.gif" height=13 width=24> |
| the style bit of SWT.NONE should be used so that the GC is not clipped on expansion, |
| and a paint event occurs even when the shell size is reduced so the full oval |
| can be repainted. |
| <p><img src="images/tag_2.gif" height=13 width=24><code> final Canvas canvas = new Canvas(shell,SWT.NONE);</code> |
| <p>For any SWT widget, if more than one rectangle is damaged then the platform |
| merges these into a single damaged area that is the union of all damaged rectangles |
| so the SWT program only processes a single paint event. The style bit NO_MERGE_PAINTS |
| on Canvas can be used to override this behavior which means the listener will |
| be called with a single paint event for every separate damaged rectangle. |
| <p>The style bits NO_BACKGROUND, NO_REDRAW_RESIZE and NO_MERGE_PAINTS can be used |
| with any Composite or subclass, including Canvas, Shell, or Group. Although |
| this is allowed by SWT ( no exceptions will be thrown ) the Composite class's |
| Javadoc includes the following style bit warning <i>"... their behavior is undefined |
| if they are used with subclasses of <code>Composite</code> other than <code>Canvas</code>". |
| </i>The Canvas class is therefore the preferred control to use when performing |
| arbitrary drawing operations. |
| <p>Another way to reduce flicker is to batch up the drawing that occurs on the |
| display by double buffering. Double buffering is a technique where the |
| drawing occurs onto a GC that is not the one used on the paintEvent, and the |
| contents are then copied across. To do this you can create an Image with the |
| same size as the Canvas' client area and then draw onto this with a <code>GC(Image)</code>; |
| The final image is then transferred across to the paint event's GC with a single |
| <code>drawImage(Image image, int x, int y) </code>method call. When using |
| this technique be aware that some platforms perform native double buffering |
| for you already, so you may in fact be triple buffering. |
| <h1> <a NAME="Drawing lines and"></a>Drawing lines and shapes</h1> |
| A GC has a number of methods that allow lines to be drawn on it, either between |
| two points, a series of interconnected points, or a pre-defined shape. Lines |
| are drawn in the foreground color of the GC and can be drawn in a number of styles |
| and with different thickeness. For a paint event the GC has the same attributes |
| as the control that fired the event (foreground / background color and font), |
| and the default line style is solid with a width of 1 pixel. |
| <h2> <code>GC.drawLine(int x1, int y1, int x2, int y2);</code></h2> |
| Draw a line between two points on the drawable surface beginning at x1,y1 and |
| ending at x2,y2. The end points are included in the line, and if they are |
| the same then a single pixel dot will be drawn. |
| <h2> <code>GC.drawPolyline(int[] pointArray);</code></h2> |
| Draw a series of interconnecting lines with the int[] representing x and y positions |
| for consecutive points. The statement: <br> |
| <code>gc.drawPolyline(new int[] { 25,5,45,45,5,45 });</code> <br> |
| draws a line from 25,5 to 45,45 and then from 45,45 onto 5,45. |
| <p><img src="images/PolygonClient.gif" height=94 width=161> |
| <h2> <code>GC.drawPolygon(int[] pointArray);</code></h2> |
| Similar to <code>drawPolyline(int[])</code> except that the last point is joined to |
| the first <br> |
| <code>gc.drawPolygon(new int[] { 25,5,45,45,5,45 });</code> <br> |
| This draws the three corners of the triangle and, by joining the point at 5,45 |
| with 25,5 completes the shape by creating a contained area <br> |
| <img src="images/PolygonClient.gif" height=94 width=161> |
| <h2> <code>GC.drawRectangle(int x, int y, int width, int height);</code></h2> |
| Draw a rectangle with the top left corner at x,y with the specified with and height |
| <br> |
| <code>gc.drawRectangle(5,5,90,45);</code> <br> |
| draws a rectangle with one corner at 5,5 and the opposite corner at 95,50; <br> |
| <img src="images/RectangleClient.gif" height=97 width=174> |
| <p>When a rectangle is drawn the bottom and right edges are drawn and it is conceptually |
| similar to lines being drawn between its four corners. As well as passing |
| the x,y,width and height as individual arguments the method is overloaded so |
| you can pass in the entire Rectangle - <code>GC.drawRectangle(Rectangle);</code> |
| <h2> <code>GC.drawRoundedRectangle(int x, int y, int width, int height, int arcWidth, |
| int arcHeight);</code></h2> |
| A rounded rectangle differs from a standard rectangle in that its corners are |
| rounded. The rounding on each corner can be thought of as 1/4 of an oval, |
| and the arcWidth and arcHeight specify the width and height of the full oval. |
| <br> |
| <code>gc.drawRoundedRectangle(5,5,90,45,25,15); </code>draws a rounded rectangle with |
| a top left corner of 5,5. The figure below shows a zoomed picture of the |
| bottom right corner together with an imaginary oval shown with a width of 25 and |
| a height of 15 to show how the rounded corner is created from 1/4 of the oval. |
| <p><img src="images/RoundedRectangleClient.gif" height=121 width=449> |
| <p>The implementation of the rounded rectangle could be achieved with 4 calls |
| to <code>drawArc() </code>and 4 to <code>drawLine(). </code>However on some platforms |
| such as Windows or Photon there is an optimized platform API that SWT uses. |
| <h2> <code>GC.drawOval(int x, int y, int width, int height);</code></h2> |
| An oval is draw within the rectangle defined by its top left corner (x,y) and |
| its width and height. To draw a circle set the width and height to be the |
| same value. <br> |
| <code>gc.drawOval(5,5,90,45);</code> |
| <p><img src="images/OvalClient.gif" height=95 width=188> |
| <h2> <code>GC.drawArc(int x, int y, int width, int height, int startAngle, int endAngle);</code></h2> |
| An arc is drawn within the area bounded by the rectangle whose top left corner |
| is x,y and has the specified width and height. The startAngle is on the |
| horizontal x axis, so 0 degrees does not represent North but instead can be thought |
| of as pointing East. The arc is drawn anticlockwise for the number of degrees |
| specified by the endAngle <br> |
| <code>gc.drawArc(5,5,90,45,90,200);</code> <br> |
| This draws an arc beginning at 90 degrees, which is vertical ( 0 degrees being |
| horizontal ), and the arc continues for 200 degrees. The arc does not stop |
| when it reaches the endAngle of 200 degrees relative to the 0 axis, rather it |
| stops when it has traversed 200 degrees relative to its starting position. |
| <p><img src="images/ArcClient.gif" height=87 width=161> |
| <h2> <code>GC.setLineStyle(int style);</code></h2> |
| Lines can be drawn in a number of styles that are defined as static constants |
| on the class <code>org.eclipse.swt.SWT </code>beginning LINE_. <br> |
| |
| <table BORDER > |
| <caption> |
| <TBODY> |
| <br></TBODY> |
| </caption> |
| <tr> |
| <td>SWT.LINE_SOLID</td> |
| <td><img src="images/Line_SOLID.gif" height=16 width=292></td> |
| </tr> |
| <tr> |
| <td>SWT.LINE_DOT</td> |
| <td><img src="images/LINE_DOT.gif" height=16 width=292></td> |
| </tr> |
| <tr> |
| <td>SWT.LINE_DASH</td> |
| <td><img src="images/LINE_DASH.gif" height=16 width=292></td> |
| </tr> |
| <tr> |
| <td>SWT.LINE_DASHDOT</td> |
| <td><img src="images/LINE_DASHDOT.gif" height=16 width=292></td> |
| </tr> |
| <tr> |
| <td>SWT.LINE_DASHDOTDOT</td> |
| <td><img src="images/LINE_DASHDOTDOT.gif" height=16 width=292></td> |
| </tr> |
| </table> |
| <h2> <code>GC.setLineWidth(int width);</code></h2> |
| The default width for a line is 1 pixel, however this can be changed by setting |
| the lineWidth of the GC. <br> |
| |
| <table BORDER > |
| <caption> |
| <TBODY> |
| <br></TBODY> |
| </caption> |
| <tr> |
| <td>gc.setLineWidth(2);</td> |
| <td><img src="images/Width_2.gif" height=16 width=292></td> |
| </tr> |
| <tr> |
| <td>gc.setLineWidth(4);</td> |
| <td><img src="images/Width_4.gif" height=16 width=292></td> |
| </tr> |
| </table> |
| <p>Because line styles and width affect all of the draw operations, allowing you |
| to achieve effects such as a dotted rectangle or an oval with a thick line. |
| <p><code>gc.setLineWidth(3);</code> <br> |
| <code>gc.drawOval(5,5,40,40);</code> <br> |
| <code>gc.setLineWidth(1);</code> <br> |
| <code>gc.setLineStyle(SWT.LINE_DOT);</code> <br> |
| <code>gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));</code> <br> |
| <code>gc.drawRectangle(60,5,60,40);</code> |
| <p><img src="images/LineShapes.gif" height=91 width=172> <br> |
| When a property such as a line width, line style or color is changed on a GC |
| this affects all subsequent drawing operations. The code snippet above |
| changes the width to 3 to draw the oval, but it then changes it back to 1 before |
| drawing the dotted blue rectangle. Forgetting to revert values in a GC |
| after modifying them is a common bug when doing SWT graphics programming. |
| <h1> <a NAME="Drawing Text"></a>Drawing text</h1> |
| Text can be drawn onto a GC, the glyphs are drawn in the GC's foreground color |
| and font, and the area it occupies is drawn with the GC's background color. |
| To draw text you define its top left corner, width and height. There are |
| two sets of methods to draw text, the first set of which have <b><code>Text</code></b> |
| in their method name, and will handle line delimiters and tabs and are used to |
| implement an emulated Label. The second set of API methods have <b><code>String</code></b> |
| in their method name, and no tab expansion or carriage return processing occurs, |
| and are used for more sophisticated controls like the <code>StyledText </code>used |
| by the Eclipse Java editor. |
| <h2> <code>GC.drawText(String text, int x, int y);</code></h2> |
| <code>Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC);</code> <br> |
| <code>// ...</code> <br> |
| <code>gc.drawText("Hello World",5,5);</code> <br> |
| <code>gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));</code> <br> |
| <code>gc.setFont(font);</code> <br> |
| <code>gc.drawText("Hello\tThere\nWide\tWorld",5,25);</code> <br> |
| <code>// ...</code> <br> |
| <code>font.dispose();</code> |
| <p><img src="images/TextHelloWorld.gif" height=108 width=200> |
| <p>The drawText API processes the control characters \t as tab \n as a carriage |
| return. |
| <h2> <code>GC.drawString(String text, int x, int y);</code></h2> |
| <code>Font font = new Font(display,"Arial",14,SWT.BOLD | SWT.ITALIC);</code> <br> |
| <code>// ...</code> <br> |
| <code>gc.drawString("Hello World",5,5);</code> <br> |
| <code>gc.setForeground(display.getSystemColor(SWT.COLOR_BLUE));</code> <br> |
| <code>gc.setFont(font);</code> <br> |
| <code>gc.drawString("Hello\tThere\nWide\tWorld",5,25);</code> <br> |
| <code>// ...</code> <br> |
| <code>font.dispose()</code> |
| <p><img src="images/StringHelloWorld.gif" height=106 width=304> <br> |
| When drawString is used, the tab and carriage return characters are not processed |
| <p>The size that a String occupies when it is drawn onto a GC is based on its |
| contents and the font of the GC. To determine the area that a String will |
| occupy when it is drawn use the methods <code>GC.stringExtent(String text), </code>or<code> |
| GC.textExtent(String text). </code>These return a Point whose x and y are the |
| width and height required to render the String argument. |
| <h2> <code>GC.drawText(String text, int x, int y, boolean isTransparent);</code></h2> |
| When drawText(String text, int x, int y) is used the text is drawn in the GC's |
| current foreground color. If you are drawing on top of an area where you |
| want the existing background to show through, then you can set the isTransparent |
| argument to true. This is useful if you are drawing on top of an image and |
| you don't want the text to obscure the image's background. |
| <p><code>Font font = new Font(display,"Arial",12,SWT.BOLD | SWT.ITALIC);</code> <br> |
| <code>Image image = new Image(display,"C:/devEclipse_02/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");</code> |
| <br> |
| <code>GC gc = new GC(image);</code> <br> |
| <code>gc.drawText("Hello World",5,5);</code> <br> |
| <code>gc.setFont(font);</code> <br> |
| <code>gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));</code> <br> |
| <code>gc.drawText("Hello World",5,25,true);</code> <br> |
| <code>gc.dispose();</code> <br> |
| <code>image.dispose();</code> <br> |
| <code>font.dispose();</code> |
| <p><img src="images/TextHelloWorldTransparent.gif" height=182 width=159> <br> |
| |
| <h2> <code>GC.drawText(String text, int x, int y, int flags);</code></h2> |
| The flags are a bitmask of the constants SWT.DRAW_DELIMITER, SWT.DRAW_TAB, SWT.DRAW_TRANSPARENT |
| and SWT.DRAW_MNEMONIC. These determine whether or not \n is processed as |
| a carriage return, \t as a tab expansion, whether background transparency is used, |
| and whether & is processed as a mnemonic or a literal. |
| <p><code>gc.drawImage(image,0,0);</code> <br> |
| <code>gc.drawText("Hello\t&There\nWide\tWorld",5,5,SWT.DRAW_TRANSPARENT);</code> |
| <br> |
| <code>gc.drawText("Hello\t&There\nWide\tWorld",5,25,SWT.DRAW_DELIMITER | SWT.DRAW_TAB |
| | SWT.DRAW_MNEMONIC );</code> |
| <p><img src="images/TextFlags.gif" height=166 width=192> <br> |
| <br> |
| |
| <h1> <a NAME="Filling shapes"></a>Filling shapes</h1> |
| Whereas lines are drawn in the GC's foreground color, shapes are filled with the |
| GC's background color. |
| <h2> <code>GC.fillPolygon(int[]);</code></h2> |
| <code>gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));</code> <br> |
| <code>gc.fillPolygon(new int[] { 25,5,45,45,5,45 })</code> <br> |
| <img src="images/PolygonFillClient.gif" height=96 width=164> |
| <h2> <code>GC.fillRectangle(int x, int y, int width, int height);</code></h2> |
| <code>gc.fillRectangle(5,5,90,45);</code> |
| <p><img src="images/RectangleFillClient.gif" height=98 width=165> |
| <p>When a rectangle is filled, the bottom and right edges are not included. |
| With the code above the origin point 5,5 is included in the filled blue rectangle, |
| however the argument rectangle's bottom right corner of 95,50 (5+90 , 45+5) |
| is outside the filled area. The bottom right corner of the filled rectangle |
| is at 94,49. This is unlike the behavior for <code>drawRectangle(5,5,90,45)</code>, |
| where the full shape is outlined so the bottom right corner is 95,50. |
| <p>To illustrate this, the code below strokes a rectangle whose corners are 5,5 |
| and 90,45 before filling it in cyan. To ensure that the filling of the |
| rectangle doesn't overwrite its drawn outline, the origin is offset to 6,6. |
| This moves the bottom right corner of the filled rectangle, however because |
| it has already excluded the bottom and right edges it only needs to be inset |
| by one to not overlap with the drawn rectangle. |
| <p><code>gc.drawRectangle(5,5,90,45);</code> <br> |
| <code>gc.setBackground(display.getSystemColor(SWT.COLOR_CYAN));</code> <br> |
| <code>gc.fillRectangle(6,6,89,44);</code> |
| <p><img src="images/RectangleStrokeFill.gif" height=111 width=195> |
| <h2> <code>GC.fillRoundedRectangle(int x, int y, int width, int height, int arcWidth, |
| int arcHeight);</code></h2> |
| <code>gc.fillRoundRectangle(5,5,90,45,25,15);</code> |
| <p><img src="images/RoundedRectangleFilledClient.gif" height=95 width=166> |
| <p>Like the <code>GC.fillRectangle(...) </code>method, the bottom and right edges |
| are excluded from the filled shape so the bottom right corner becomes 94,49 |
| rather than 95,50. |
| <h2> <code>GC.fillOval(int x, int y, int width, int height);</code></h2> |
| <code>gc.fillOval(5,5,90,45);</code> |
| <p><img src="images/OvalFilledClient.gif" height=93 width=167 align=ABSBOTTOM> |
| <p>Like the other fill APIs, the bounding rectangle's bottom right corner is inset |
| by one. |
| <h2> <code>GC.fillArc(int x, int y, int widt4h., int height, int startAngle, int |
| endAngle);</code></h2> |
| <code>gc.fillArc(5,5,90,45,90,200);</code> <br> |
| <img src="images/ArcFilledClient.gif" height=97 width=196> <br> |
| The arguments to the <code>fillArc(...)</code> method are similar to the <code>drawArc(...)</code>, |
| with the offset from an East axis to begin filling and the number of degrees to |
| continue the arc in an anti-clockwise direction. However unlike the <code>drawArc(...)</code> |
| whose bottom right corner of the bounding rectangle is the origin offset by the |
| width and height, <code>fillArc(...)</code> follows the same pattern used by the other |
| fill methods where the bottom and right edge are excluded and the corner inset |
| by one. |
| <h2> <code>GC.fillGradientRectangle(int x, int y, int width. int height, vertical |
| boolean);</code></h2> |
| This allows a rectangle to be filled with a gradient of coloring between the GC's |
| foreground and background color. The gradient can either be horizontal or |
| vertical |
| <p><code>gc.setBackgrouind(display,getSystemColor(SWT.COLOR_BLUE));</code> <br> |
| <code>gc.fillGradientRectangle(5,5,90,45,false);</code> |
| <p>This creates a horizontal gradient fill starting with the foreground color |
| (black) on the left, and finishing with the background color (blue) on the right. |
| As with the other fill methods, the bottom and right edges are excluded so the |
| bottom right corner becomes inset by 1. |
| <p><img src="images/GradientFillFalse.gif" height=97 width=166> |
| <p><code>gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));</code> <br> |
| <code>gc.setForeground(display.getSystemColor(SWT.COLOR_CYAN));</code> <br> |
| <code>gc.fillGradientRectangle(5,5,90,45,true);</code> |
| <p>The vertical gradient begins at the top with the foreground color (cyan) and |
| finishes at the bottom with the blue background color. |
| <p><img src="images/GradientFillTrue.gif" height=100 width=166> |
| <h1> <a NAME="XOR"></a>XOR</h1> |
| When drawing occurs on a GC you are altering its pixel values that make up the |
| graphic on the Drawable surface. If you set the GC's XOR mode to true, then |
| what occurs is that for each pixel the red, green and blue values of the source |
| being drawn are XOR'd with the existing red, green and blue values and the result |
| becomes the new destination pixel. <br> |
| |
| <p><code>shell.setBackground(display.getSystemColor(SWT.COLOR_WHITE));</code> <br> |
| <code>// ...</code> <br> |
| <code>gc.setBackground(display.getSystemColor(SWT.COLOR_BLUE));</code> <br> |
| <code>gc.fillRectangle(5,5,90,45);</code> <br> |
| <code>gc.setXORMode(true);</code> <br> |
| <code>gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));</code> <br> |
| <code>gc.fillRectangle(20,20,50,50);</code> <br> |
| <code>gc.setBackground(display.getSystemColor(SWT.COLOR_RED));</code> <br> |
| <code>gc.fillOval(80,20,50,50);</code> |
| <p><img src="images/XORFills.gif" height=117 width=276> |
| <p>The filled rectangle has a background color of white (255,255,255). When |
| this is drawn over the blue (0,0,255) the XOR'd result is yellow (255,255,0). |
| The portion of the rectangle drawn over the white background (255,255,255) creates |
| an XOR result of black (0,0,0). The filled oval has a background color or red |
| (255,0,0). When this is drawn over blue the resulting XOR value is purple |
| (255,0,255), and when it is drawn over white the result is cyan (0,255,255); |
| <h1> <a NAME="Drawing Images"></a>Drawing Images</h1> |
| The class <code>org.eclipse.swt.graphics.Image</code> represents a graphic that has |
| been prepared ready to be displayed on a device such as a Display or Printer. |
| The simplest way to create an image is to load it from a recognized file format. |
| Supported file formats are GIF, BMP (Windows format bitmap), JPG, PNG, and in |
| newer Eclipse releases TIFF. |
| <p><code>Image image = new Image(display,"C:/eclipse/eclipse/plugins/org.eclipse.platform_2.0.2/eclipse_lg.gif");</code> |
| <h2> <code>GC.drawImage(Image image, int x, int y);</code></h2> |
| Every image has a size that is determined by its bounds. For example, the |
| size of the image <code>eclipse_lg.gif </code>is 115,164 and can be determined by |
| using the method <code>image.getBounds(). </code>When an image is drawn it will be |
| shown at the width and height defined by its bounds.. |
| <p><code>gc.drawImage(image,5,5);</code> <br> |
| <img src="images/ImageClient.jpg" height=221 width=175> |
| <h2> <code>GC.drawImage(Image image, int srcX, int srcY, int srcWidth, int srcHeight, |
| int dstX, int dstY, int dstWidth, int dstHeight);</code></h2> |
| Images can be drawn with different sizes from their original width and height, |
| and also portions of images can be drawn. |
| <p>The src coordinates are relative to image itself, so to draw the entire image |
| use 0,0 and width and height set to the image bound's width and height. |
| The dst arguments represent where to draw the image on the GC, and what size |
| to draw it at. The original image is 115 by 164, so to stretch it to twice |
| the width and half the height use the statement |
| <p><code>gc.drawImage(image,0,0,115,164,5,5,230,82);</code> <br> |
| <img src="images/ImageClientStretched.jpg" height=136 width=289> |
| <p>Using the src coordinates allows you to specify that you want only a portion |
| of the image drawn. For example, if you only want a section from the top |
| right area of the image drawn, you can use src coordinates of 20,0 and a width |
| and height of 95,82. The code below draws the destination rectangle with |
| the same width and height (95,82) as the cropped area, however a different size |
| could be used to stretch or shrink the image portion. |
| <p><code>gc.drawImage(image,20,0,95,82,5,5,95,82);</code> |
| <p><img src="images/ImageClipped.jpg" height=219 width=405> |
| <p>Other images effects can be achieved, such as transparency, animation and alpha |
| channel blending of images. These are outside the scope of this article, |
| however I hope to cover these in a future article. |
| <h1> <a NAME="Understanding Images"></a>Conclusion</h1> |
| This article has shown how a GC can be used to draw lines, text, and to fill shapes. |
| A GC can be created by passing the drawable into its constructor, such as an Image, |
| or for a Control by using the <code>paintEvent</code> callback. The API of a |
| GC allows lines and shapes to be drawn in its foreground color and filled in its |
| background color. The general purpose Canvas control is optimized to allow |
| drawing through its <code>paintEvent</code>, and it has a number of constructor style |
| bits to control when paint events occur. GC clipping allows you to control |
| the region of the GC that visible drawing occurs on, and when drawing different |
| line styles can be used, and text and images can be shown. |
| <p><font size="-2">Java and all Java-based trademarks and logos are trademarks |
| or registered trademarks of Sun Microsystems, Inc. in the United States, other |
| countries, or both. </font> |
| <p><font size="-2">Other company, product, and service names may be trademarks |
| or service marks of others. </font> |
| </body> |
| </html> |