root/dwt/graphics/GC.d

Revision 249:b3dbd786541a, 187.5 kB (checked in by Frank Benoit <benoit@tionex.de>, 6 months ago)

Fix: compile errors

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.graphics.GC;
14
15 import dwt.DWT;
16 import dwt.DWTError;
17 import dwt.DWTException;
18 import dwt.internal.Compatibility;
19 import dwt.internal.gdip.Gdip;
20 import dwt.internal.win32.OS;
21
22 import dwt.graphics.Color;
23 import dwt.graphics.Drawable;
24 import dwt.graphics.Resource;
25 import dwt.graphics.Device;
26 import dwt.graphics.Font;
27 import dwt.graphics.FontMetrics;
28 import dwt.graphics.GCData;
29 import dwt.graphics.Image;
30 import dwt.graphics.ImageData;
31 import dwt.graphics.Path;
32 import dwt.graphics.Pattern;
33 import dwt.graphics.Point;
34 import dwt.graphics.RGB;
35 import dwt.graphics.Rectangle;
36 import dwt.graphics.Region;
37 import dwt.graphics.Resource;
38 import dwt.graphics.Transform;
39 import dwt.graphics.LineAttributes;
40
41 import dwt.dwthelper.utils;
42 import tango.text.convert.Format;
43
44 /**
45  * Class <code>GC</code> is where all of the drawing capabilities that are
46  * supported by DWT are located. Instances are used to draw on either an
47  * <code>Image</code>, a <code>Control</code>, or directly on a <code>Display</code>.
48  * <dl>
49  * <dt><b>Styles:</b></dt>
50  * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
51  * </dl>
52  *
53  * <p>
54  * The DWT drawing coordinate system is the two-dimensional space with the origin
55  * (0,0) at the top left corner of the drawing area and with (x,y) values increasing
56  * to the right and downward respectively.
57  * </p>
58  *
59  * <p>
60  * Application code must explicitly invoke the <code>GC.dispose()</code>
61  * method to release the operating system resources managed by each instance
62  * when those instances are no longer required. This is <em>particularly</em>
63  * important on Windows95 and Windows98 where the operating system has a limited
64  * number of device contexts available.
65  * </p>
66  *
67  * <p>
68  * Note: Only one of LEFT_TO_RIGHT and RIGHT_TO_LEFT may be specified.
69  * </p>
70  *
71  * @see dwt.events.PaintEvent
72  * @see <a href="http://www.eclipse.org/swt/snippets/#gc">GC snippets</a>
73  * @see <a href="http://www.eclipse.org/swt/examples.php">DWT Examples: GraphicsExample, PaintExample</a>
74  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
75  */
76
77 public final class GC : Resource {
78
79     alias Resource.init_ init_;
80
81     /**
82      * the handle to the OS device context
83      * (Warning: This field is platform dependent)
84      * <p>
85      * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
86      * public API. It is marked public only so that it can be shared
87      * within the packages provided by DWT. It is not available on all
88      * platforms and should never be accessed from application code.
89      * </p>
90      */
91     public HDC handle;
92
93     Drawable drawable;
94     GCData data;
95
96     static const int FOREGROUND = 1 << 0;
97     static const int BACKGROUND = 1 << 1;
98     static const int FONT = 1 << 2;
99     static const int LINE_STYLE = 1 << 3;
100     static const int LINE_WIDTH = 1 << 4;
101     static const int LINE_CAP = 1 << 5;
102     static const int LINE_JOIN = 1 << 6;
103     static const int LINE_MITERLIMIT = 1 << 7;
104     static const int FOREGROUND_TEXT = 1 << 8;
105     static const int BACKGROUND_TEXT = 1 << 9;
106     static const int BRUSH = 1 << 10;
107     static const int PEN = 1 << 11;
108     static const int NULL_BRUSH = 1 << 12;
109     static const int NULL_PEN = 1 << 13;
110     static const int DRAW_OFFSET = 1 << 14;
111
112     static const int DRAW = FOREGROUND | LINE_STYLE | LINE_WIDTH | LINE_CAP | LINE_JOIN | LINE_MITERLIMIT | PEN | NULL_BRUSH | DRAW_OFFSET;
113     static const int FILL = BACKGROUND | BRUSH | NULL_PEN;
114
115     static const float[] LINE_DOT_ZERO = [3, 3];
116     static const float[] LINE_DASH_ZERO = [18, 6];
117     static const float[] LINE_DASHDOT_ZERO = [9, 6, 3, 6];
118     static const float[] LINE_DASHDOTDOT_ZERO = [9, 3, 3, 3, 3, 3];
119
120 /**
121  * Prevents uninitialized instances from being created outside the package.
122  */
123 this() {
124 }
125
126 /**
127  * Constructs a new instance of this class which has been
128  * configured to draw on the specified drawable. Sets the
129  * foreground color, background color and font in the GC
130  * to match those in the drawable.
131  * <p>
132  * You must dispose the graphics context when it is no longer required.
133  * </p>
134  * @param drawable the drawable to draw on
135  * @exception IllegalArgumentException <ul>
136  *    <li>ERROR_NULL_ARGUMENT - if the drawable is null</li>
137  *    <li>ERROR_NULL_ARGUMENT - if there is no current device</li>
138  *    <li>ERROR_INVALID_ARGUMENT
139  *          - if the drawable is an image that is not a bitmap or an icon
140  *          - if the drawable is an image or printer that is already selected
141  *            into another graphics context</li>
142  * </ul>
143  * @exception DWTError <ul>
144  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for GC creation</li>
145  *    <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li>
146  * </ul>
147  */
148 public this(Drawable drawable) {
149     this(drawable, DWT.NONE);
150 }
151
152 /**
153  * Constructs a new instance of this class which has been
154  * configured to draw on the specified drawable. Sets the
155  * foreground color, background color and font in the GC
156  * to match those in the drawable.
157  * <p>
158  * You must dispose the graphics context when it is no longer required.
159  * </p>
160  *
161  * @param drawable the drawable to draw on
162  * @param style the style of GC to construct
163  *
164  * @exception IllegalArgumentException <ul>
165  *    <li>ERROR_NULL_ARGUMENT - if the drawable is null</li>
166  *    <li>ERROR_NULL_ARGUMENT - if there is no current device</li>
167  *    <li>ERROR_INVALID_ARGUMENT
168  *          - if the drawable is an image that is not a bitmap or an icon
169  *          - if the drawable is an image or printer that is already selected
170  *            into another graphics context</li>
171  * </ul>
172  * @exception DWTError <ul>
173  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for GC creation</li>
174  *    <li>ERROR_THREAD_INVALID_ACCESS if not called from the thread that created the drawable</li>
175  * </ul>
176  *
177  * @since 2.1.2
178  */
179 public this(Drawable drawable, int style) {
180     if (drawable is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
181     GCData data = new GCData ();
182     data.style = checkStyle(style);
183     auto hDC = drawable.internal_new_GC(data);
184     Device device = data.device;
185     if (device is null) device = Device.getDevice();
186     if (device is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
187     this.device = data.device = device;
188     init_ (drawable, data, hDC);
189     init_();
190 }
191
192 static int checkStyle(int style) {
193     if ((style & DWT.LEFT_TO_RIGHT) !is 0) style &= ~DWT.RIGHT_TO_LEFT;
194     return style & (DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT);
195 }
196
197 void checkGC(int mask) {
198     int state = data.state;
199     if ((state & mask) is mask) return;
200     state = (state ^ mask) & mask;
201     data.state |= mask;
202     auto gdipGraphics = data.gdipGraphics;
203     if (gdipGraphics !is null) {
204         auto pen = data.gdipPen;
205         float width = data.lineWidth;
206         if ((state & FOREGROUND) !is 0 || (pen is null && (state & (LINE_WIDTH | LINE_STYLE | LINE_MITERLIMIT | LINE_JOIN | LINE_CAP)) !is 0)) {
207             if (data.gdipFgBrush !is null) Gdip.SolidBrush_delete(data.gdipFgBrush);
208             data.gdipFgBrush = null;
209             Gdip.Brush brush;
210             Pattern pattern = data.foregroundPattern;
211             if (pattern !is null) {
212                 brush = pattern.handle;
213                 if ((data.style & DWT.MIRRORED) !is 0) {
214                     switch (Gdip.Brush_GetType(brush)) {
215                         case Gdip.BrushTypeTextureFill:
216                             brush = Gdip.Brush_Clone(brush);
217                             if (brush is null) DWT.error(DWT.ERROR_NO_HANDLES);
218                             Gdip.TextureBrush_ScaleTransform( cast(Gdip.TextureBrush) brush, -1, 1, Gdip.MatrixOrderPrepend);
219                             data.gdipFgBrush = cast(Gdip.SolidBrush)brush;
220                         default:
221                     }
222                 }
223             } else {
224                 auto foreground = data.foreground;
225                 int rgb = ((foreground >> 16) & 0xFF) | (foreground & 0xFF00) | ((foreground & 0xFF) << 16);
226                 auto color = Gdip.Color_new(data.alpha << 24 | rgb);
227                 if (color is 0) DWT.error(DWT.ERROR_NO_HANDLES);
228                 brush = cast(Gdip.Brush) Gdip.SolidBrush_new(color);
229                 if (brush is null) DWT.error(DWT.ERROR_NO_HANDLES);
230                 Gdip.Color_delete(color);
231                 data.gdipFgBrush = cast(Gdip.SolidBrush)brush;
232             }
233             if (pen !is null) {
234                 Gdip.Pen_SetBrush(pen, brush);
235             } else {
236                 pen = data.gdipPen = Gdip.Pen_new(brush, width);
237             }
238         }
239         if ((state & LINE_WIDTH) !is 0) {
240             Gdip.Pen_SetWidth(pen, width);
241             switch (data.lineStyle) {
242                 case DWT.LINE_CUSTOM:
243                     state |= LINE_STYLE;
244                 default:
245             }
246         }
247         if ((state & LINE_STYLE) !is 0) {
248             float[] dashes = null;
249             float dashOffset = 0;
250             int dashStyle = Gdip.DashStyleSolid;
251             switch (data.lineStyle) {
252                 case DWT.LINE_SOLID: break;
253                 case DWT.LINE_DOT: dashStyle = Gdip.DashStyleDot; if (width is 0) dashes = LINE_DOT_ZERO; break;
254                 case DWT.LINE_DASH: dashStyle = Gdip.DashStyleDash; if (width is 0) dashes = LINE_DASH_ZERO; break;
255                 case DWT.LINE_DASHDOT: dashStyle = Gdip.DashStyleDashDot; if (width is 0) dashes = LINE_DASHDOT_ZERO; break;
256                 case DWT.LINE_DASHDOTDOT: dashStyle = Gdip.DashStyleDashDotDot; if (width is 0) dashes = LINE_DASHDOTDOT_ZERO; break;
257                 case DWT.LINE_CUSTOM: {
258                     if (data.lineDashes !is null) {
259                         dashOffset = data.lineDashesOffset / Math.max (1, width);
260                         dashes = new float[data.lineDashes.length * 2];
261                         for (int i = 0; i < data.lineDashes.length; i++) {
262                             float dash = data.lineDashes[i] / Math.max (1, width);
263                             dashes[i] = dash;
264                             dashes[i + data.lineDashes.length] = dash;
265                         }
266                     }
267                 }
268                 default:
269             }
270             if (dashes !is null) {
271                 Gdip.Pen_SetDashPattern(pen, dashes.ptr, dashes.length);
272                 Gdip.Pen_SetDashStyle(pen, Gdip.DashStyleCustom);
273                 Gdip.Pen_SetDashOffset(pen, dashOffset);
274             } else {
275                 Gdip.Pen_SetDashStyle(pen, dashStyle);
276             }
277         }
278         if ((state & LINE_MITERLIMIT) !is 0) {
279             Gdip.Pen_SetMiterLimit(pen, data.lineMiterLimit);
280         }
281         if ((state & LINE_JOIN) !is 0) {
282             int joinStyle = 0;
283             switch (data.lineJoin) {
284                 case DWT.JOIN_MITER: joinStyle = Gdip.LineJoinMiter; break;
285                 case DWT.JOIN_BEVEL: joinStyle = Gdip.LineJoinBevel; break;
286                 case DWT.JOIN_ROUND: joinStyle = Gdip.LineJoinRound; break;
287                 default:
288             }
289             Gdip.Pen_SetLineJoin(pen, joinStyle);
290         }
291         if ((state & LINE_CAP) !is 0) {
292             int dashCap = Gdip.DashCapFlat, capStyle = 0;
293             switch (data.lineCap) {
294                 case DWT.CAP_FLAT: capStyle = Gdip.LineCapFlat; break;
295                 case DWT.CAP_ROUND: capStyle = Gdip.LineCapRound; dashCap = Gdip.DashCapRound; break;
296                 case DWT.CAP_SQUARE: capStyle = Gdip.LineCapSquare; break;
297                 default:
298             }
299             Gdip.Pen_SetLineCap(pen, capStyle, capStyle, dashCap);
300         }
301         if ((state & BACKGROUND) !is 0) {
302             if (data.gdipBgBrush !is null) Gdip.SolidBrush_delete(data.gdipBgBrush);
303             data.gdipBgBrush = null;
304             Pattern pattern = data.backgroundPattern;
305             if (pattern !is null) {
306                 data.gdipBrush = pattern.handle;
307                 if ((data.style & DWT.MIRRORED) !is 0) {
308                     switch (Gdip.Brush_GetType(data.gdipBrush)) {
309                         case Gdip.BrushTypeTextureFill:
310                             auto brush = Gdip.Brush_Clone(data.gdipBrush);
311                             if (brush is null) DWT.error(DWT.ERROR_NO_HANDLES);
312                             Gdip.TextureBrush_ScaleTransform( cast(Gdip.TextureBrush)brush, -1, 1, Gdip.MatrixOrderPrepend);
313                             data.gdipBrush = brush;
314                             data.gdipBgBrush = cast(Gdip.SolidBrush) brush;
315                         default:
316                     }
317                 }
318             } else {
319                 auto background = data.background;
320                 int rgb = ((background >> 16) & 0xFF) | (background & 0xFF00) | ((background & 0xFF) << 16);
321                 auto color = Gdip.Color_new(data.alpha << 24 | rgb);
322                 // if (color is null) DWT.error(DWT.ERROR_NO_HANDLES);
323                 auto brush = Gdip.SolidBrush_new(color);
324                 if (brush is null) DWT.error(DWT.ERROR_NO_HANDLES);
325                 Gdip.Color_delete(color);
326                 data.gdipBrush = cast(Gdip.Brush)brush;
327                 data.gdipBgBrush = brush;
328             }
329         }
330         if ((state & FONT) !is 0) {
331             Font font = data.font;
332             OS.SelectObject(handle, font.handle);
333             auto gdipFont = createGdipFont(handle, font.handle);
334             if (data.gdipFont !is null) Gdip.Font_delete(data.gdipFont);
335             data.gdipFont = gdipFont;
336         }
337         if ((state & DRAW_OFFSET) !is 0) {
338             data.gdipXOffset = data.gdipYOffset = 0;
339             auto matrix = Gdip.Matrix_new(1, 0, 0, 1, 0, 0);
340             float[2] point; point[0]=1.0; point[1]=1.0;
341             Gdip.Graphics_GetTransform(gdipGraphics, matrix);
342             Gdip.Matrix_TransformPoints(matrix, cast(Gdip.PointF*)point.ptr, 1);
343             Gdip.Matrix_delete(matrix);
344             float scaling = point[0];
345             if (scaling < 0) scaling = -scaling;
346             float penWidth = data.lineWidth * scaling;
347             if (penWidth is 0 || (cast(int)penWidth % 2) is 1) {
348                 data.gdipXOffset = 0.5f / scaling;
349             }
350             scaling = point[1];
351             if (scaling < 0) scaling = -scaling;
352             penWidth = data.lineWidth * scaling;
353             if (penWidth is 0 || (cast(int)penWidth % 2) is 1) {
354                 data.gdipYOffset = 0.5f / scaling;
355             }
356         }
357         return;
358     }
359     if ((state & (FOREGROUND | LINE_CAP | LINE_JOIN | LINE_STYLE | LINE_WIDTH)) !is 0) {
360         int color = data.foreground;
361         int width = cast(int)data.lineWidth;
362         uint[] dashes = null;
363         int lineStyle = OS.PS_SOLID;
364         switch (data.lineStyle) {
365             case DWT.LINE_SOLID: break;
366             case DWT.LINE_DASH: lineStyle = OS.PS_DASH; break;
367             case DWT.LINE_DOT: lineStyle = OS.PS_DOT; break;
368             case DWT.LINE_DASHDOT: lineStyle = OS.PS_DASHDOT; break;
369             case DWT.LINE_DASHDOTDOT: lineStyle = OS.PS_DASHDOTDOT; break;
370             case DWT.LINE_CUSTOM: {
371                 if (data.lineDashes !is null) {
372                     lineStyle = OS.PS_USERSTYLE;
373                     dashes = new uint[data.lineDashes.length];
374                     for (int i = 0; i < dashes.length; i++) {
375                         dashes[i] = cast(int)data.lineDashes[i];
376                     }
377                 }
378                 break;
379             }
380             default:
381         }
382         if ((state & LINE_STYLE) !is 0) {
383             OS.SetBkMode(handle, data.lineStyle is DWT.LINE_SOLID ? OS.OPAQUE : OS.TRANSPARENT);
384         }
385         int joinStyle = 0;
386         switch (data.lineJoin) {
387             case DWT.JOIN_MITER: joinStyle = OS.PS_JOIN_MITER; break;
388             case DWT.JOIN_ROUND: joinStyle = OS.PS_JOIN_ROUND; break;
389             case DWT.JOIN_BEVEL: joinStyle = OS.PS_JOIN_BEVEL; break;
390             default:
391         }
392         int capStyle = 0;
393         switch (data.lineCap) {
394             case DWT.CAP_ROUND: capStyle = OS.PS_ENDCAP_ROUND; break;
395             case DWT.CAP_FLAT: capStyle = OS.PS_ENDCAP_FLAT; break;
396             case DWT.CAP_SQUARE: capStyle = OS.PS_ENDCAP_SQUARE;break;
397             default:
398         }
399         int style = lineStyle | joinStyle | capStyle;
400         /*
401         * Feature in Windows.  Windows does not honour line styles other then
402         * PS_SOLID for pens wider than 1 pixel created with CreatePen().  The fix
403         * is to use ExtCreatePen() instead.
404         */
405         HPEN newPen;
406         if (OS.IsWinCE || (width is 0 && lineStyle !is OS.PS_USERSTYLE) || style is 0) {
407             newPen = OS.CreatePen(style & OS.PS_STYLE_MASK, width, color);
408         } else {
409             LOGBRUSH logBrush;
410             logBrush.lbStyle = OS.BS_SOLID;
411             logBrush.lbColor = color;
412             /* Feature in Windows. PS_GEOMETRIC pens cannot have zero width. */
413             newPen = OS.ExtCreatePen (style | OS.PS_GEOMETRIC, Math.max(1, width), &logBrush, dashes !is null ? dashes.length : 0, dashes.ptr);
414         }
415         OS.SelectObject(handle, newPen);
416         data.state |= PEN;
417         data.state &= ~NULL_PEN;
418         if (data.hPen !is null) OS.DeleteObject(data.hPen);
419         data.hPen = data.hOldPen = newPen;
420     } else if ((state & PEN) !is 0) {
421         OS.SelectObject(handle, data.hOldPen);
422         data.state &= ~NULL_PEN;
423     } else if ((state & NULL_PEN) !is 0) {
424         data.hOldPen = OS.SelectObject(handle, OS.GetStockObject(OS.NULL_PEN));
425         data.state &= ~PEN;
426     }
427     if ((state & BACKGROUND) !is 0) {
428         auto newBrush = OS.CreateSolidBrush(data.background);
429         OS.SelectObject(handle, newBrush);
430         data.state |= BRUSH;
431         data.state &= ~NULL_BRUSH;
432         if (data.hBrush !is null) OS.DeleteObject(data.hBrush);
433         data.hOldBrush = data.hBrush = newBrush;
434     } else if ((state & BRUSH) !is 0) {
435         OS.SelectObject(handle, data.hOldBrush);
436         data.state &= ~NULL_BRUSH;
437     } else if ((state & NULL_BRUSH) !is 0) {
438         data.hOldBrush = OS.SelectObject(handle, OS.GetStockObject(OS.NULL_BRUSH));
439         data.state &= ~BRUSH;
440     }
441     if ((state & BACKGROUND_TEXT) !is 0) {
442         OS.SetBkColor(handle, data.background);
443     }
444     if ((state & FOREGROUND_TEXT) !is 0) {
445         OS.SetTextColor(handle, data.foreground);
446     }
447     if ((state & FONT) !is 0) {
448         Font font = data.font;
449         OS.SelectObject(handle, font.handle);
450     }
451 }
452
453 /**
454  * Copies a rectangular area of the receiver at the specified
455  * position into the image, which must be of type <code>DWT.BITMAP</code>.
456  *
457  * @param image the image to copy into
458  * @param x the x coordinate in the receiver of the area to be copied
459  * @param y the y coordinate in the receiver of the area to be copied
460  *
461  * @exception IllegalArgumentException <ul>
462  *    <li>ERROR_NULL_ARGUMENT - if the image is null</li>
463  *    <li>ERROR_INVALID_ARGUMENT - if the image is not a bitmap or has been disposed</li>
464  * </ul>