1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 module dfl.drawing;
5 
6 import core.sys.windows.windows;
7 import core.sys.windows.com;
8 import core.sys.windows.ocidl;
9 import core.sys.windows.objfwd;
10 import core.sys.windows.objidl; // IStream
11 import core.sys.windows.olectl; // OleLoadPicture
12 import core.sys.windows.uuid; // IID_IPicture
13 
14 import dfl.internal.dlib;
15 import dfl.internal.d2;
16 
17 //import dfl.internal.winapi;
18 import dfl.exception; // new class!
19 import dfl.internal.utf;
20 import dfl.internal.com;
21 
22 //import dfl.internal.wincom;
23 
24 version (D_Version2) {
25    version = DFL_D2;
26    version = DFL_D2_AND_ABOVE;
27 } else version (D_Version3) {
28    version = DFL_D3;
29    version = DFL_D3_AND_ABOVE;
30    version = DFL_D2_AND_ABOVE;
31 } else version (D_Version4) {
32    version = DFL_D4;
33    version = DFL_D4_AND_ABOVE;
34    version = DFL_D3_AND_ABOVE;
35    version = DFL_D2_AND_ABOVE;
36 } else {
37    version = DFL_D1;
38 }
39 //version = DFL_D1_AND_ABOVE;
40 
41 /// X and Y coordinate.
42 struct Point {
43    union {
44       struct {
45          LONG x;
46          LONG y;
47       }
48 
49       POINT point; // package
50    }
51 
52    /// Construct a new Point.
53    this(int x, int y) pure nothrow {
54       this.x = x;
55       this.y = y;
56    }
57 
58    this(in POINT* pt) pure nothrow {
59       this.x = pt.x;
60       this.y = pt.y;
61    }
62 
63    version (DFL_D2_AND_ABOVE) {
64 
65       Dequ opEquals(ref ConstType!(Point) pt) const {
66          return x == pt.x && y == pt.y;
67       }
68 
69       Dequ opEquals(Point pt) const {
70          return x == pt.x && y == pt.y;
71       }
72    } else {
73 
74       Dequ opEquals(Point pt) {
75          return x == pt.x && y == pt.y;
76       }
77    }
78 
79    Point opAdd(Size sz) const pure nothrow {
80       Point result;
81       result.x = x + sz.width;
82       result.y = y + sz.height;
83       return result;
84    }
85 
86    Point opSub(Size sz) const pure nothrow {
87       Point result;
88       result.x = x - sz.width;
89       result.y = y - sz.height;
90       return result;
91    }
92 
93    void opAddAssign(Size sz) pure nothrow {
94       x += sz.width;
95       y += sz.height;
96    }
97 
98    void opSubAssign(Size sz) pure nothrow {
99       x -= sz.width;
100       y -= sz.height;
101    }
102 
103    Point opNeg() const pure nothrow {
104       return Point(-x, -y);
105    }
106 }
107 
108 /// Width and height.
109 struct Size {
110    int width;
111    int height;
112 
113    SIZE size() const pure nothrow {
114       SIZE sz;
115       sz.cx = width;
116       sz.cy = height;
117       return sz;
118    }
119 
120    void size(SIZE sz) pure nothrow {
121       width = sz.cx;
122       height = sz.cy;
123    }
124 
125    /// Construct a new Size.
126    this(int width, int height) pure nothrow {
127       this.width = width;
128       this.height = height;
129    }
130 
131    /// Construct a new Size.
132    this(in SIZE* size) pure nothrow {
133       this.width = size.cx;
134       this.height = size.cy;
135    }
136 
137    version (DFL_D2_AND_ABOVE) {
138 
139       Dequ opEquals(ref ConstType!(Size) sz) const {
140          return width == sz.width && height == sz.height;
141       }
142 
143       Dequ opEquals(Size sz) const {
144          return width == sz.width && height == sz.height;
145       }
146    } else {
147 
148       Dequ opEquals(Size sz) {
149          return width == sz.width && height == sz.height;
150       }
151    }
152 
153    Size opAdd(Size sz) const pure nothrow {
154       Size result;
155       result.width = width + sz.width;
156       result.height = height + sz.height;
157       return result;
158    }
159 
160    Size opSub(Size sz) const pure nothrow {
161       Size result;
162       result.width = width - sz.width;
163       result.height = height - sz.height;
164       return result;
165    }
166 
167    void opAddAssign(Size sz) pure nothrow {
168       width += sz.width;
169       height += sz.height;
170    }
171 
172    void opSubAssign(Size sz) pure nothrow {
173       width -= sz.width;
174       height -= sz.height;
175    }
176 }
177 
178 /// X, Y, width and height rectangle dimensions.
179 struct Rect {
180    int x, y, width, height;
181 
182    // Used internally.
183    void getRect(RECT* r) pure nothrow { // package
184       r.left = x;
185       r.right = x + width;
186       r.top = y;
187       r.bottom = y + height;
188    }
189 
190    Point location() const pure nothrow @property {
191       return Point(x, y);
192    }
193 
194    void location(Point pt) pure nothrow @property {
195       x = pt.x;
196       y = pt.y;
197    }
198 
199    Size size() const pure nothrow @property {
200       return Size(width, height);
201    }
202 
203    void size(Size sz) pure nothrow @property {
204       width = sz.width;
205       height = sz.height;
206    }
207 
208    int right() const pure nothrow @property {
209       return x + width;
210    }
211 
212    int bottom() const pure nothrow @property {
213       return y + height;
214    }
215 
216    /// Construct a new Rect.
217    this(int x, int y, int width, int height) pure nothrow {
218       this.x = x;
219       this.y = y;
220       this.width = width;
221       this.height = height;
222    }
223 
224    this(Point location, Size size) pure nothrow {
225       x = location.x;
226       y = location.y;
227       width = size.width;
228       height = size.height;
229    }
230 
231    // Used internally.
232    this(in RECT* rect) pure nothrow { // package
233       x = rect.left;
234       y = rect.top;
235       width = rect.right - rect.left;
236       height = rect.bottom - rect.top;
237    }
238 
239    /// Construct a new Rect from left, top, right and bottom values.
240    static Rect fromLTRB(int left, int top, int right, int bottom) pure nothrow {
241       Rect r;
242       r.x = left;
243       r.y = top;
244       r.width = right - left;
245       r.height = bottom - top;
246       return r;
247    }
248 
249    version (DFL_D2_AND_ABOVE) {
250 
251       Dequ opEquals(ref ConstType!(Rect) r) const {
252          return x == r.x && y == r.y && width == r.width && height == r.height;
253       }
254 
255       Dequ opEquals(Rect r) const {
256          return x == r.x && y == r.y && width == r.width && height == r.height;
257       }
258    } else {
259 
260       Dequ opEquals(Rect r) {
261          return x == r.x && y == r.y && width == r.width && height == r.height;
262       }
263    }
264 
265    bool contains(int c_x, int c_y) const pure nothrow {
266       if (c_x >= x && c_y >= y) {
267          if (c_x <= right && c_y <= bottom) {
268             return true;
269          }
270       }
271       return false;
272    }
273 
274    bool contains(Point pos) const pure nothrow {
275       return contains(pos.x, pos.y);
276    }
277 
278    // Contained entirely within -this-.
279    bool contains(Rect r) const pure nothrow {
280       if (r.x >= x && r.y >= y) {
281          if (r.right <= right && r.bottom <= bottom) {
282             return true;
283          }
284       }
285       return false;
286    }
287 
288    void inflate(int i_width, int i_height) pure nothrow {
289       x -= i_width;
290       width += i_width * 2;
291       y -= i_height;
292       height += i_height * 2;
293    }
294 
295    void inflate(Size insz) pure nothrow {
296       inflate(insz.width, insz.height);
297    }
298 
299    // Just tests if there's an intersection.
300    bool intersectsWith(Rect r) const pure nothrow {
301       if (r.right >= x && r.bottom >= y) {
302          if (r.y <= bottom && r.x <= right) {
303             return true;
304          }
305       }
306       return false;
307    }
308 
309    void offset(int x, int y) pure nothrow {
310       this.x += x;
311       this.y += y;
312    }
313 
314    void offset(Point pt) pure nothrow {
315       //return offset(pt.x, pt.y);
316       this.x += pt.x;
317       this.y += pt.y;
318    }
319 
320    /+
321    // Modify -this- to include only the intersection
322    // of -this- and -r-.
323    void intersect(Rect r) {
324    }
325    +/
326 
327    // void offset(Point), void offset(int, int)
328    // static Rect union(Rect, Rect)
329 }
330 
331 unittest {
332    Rect r = Rect(3, 3, 3, 3);
333 
334    assert(r.contains(3, 3));
335    assert(!r.contains(3, 2));
336    assert(r.contains(6, 6));
337    assert(!r.contains(6, 7));
338    assert(r.contains(r));
339    assert(r.contains(Rect(4, 4, 2, 2)));
340    assert(!r.contains(Rect(2, 4, 4, 2)));
341    assert(!r.contains(Rect(4, 3, 2, 4)));
342 
343    r.inflate(2, 1);
344    assert(r.x == 1);
345    assert(r.right == 8);
346    assert(r.y == 2);
347    assert(r.bottom == 7);
348    r.inflate(-2, -1);
349    assert(r == Rect(3, 3, 3, 3));
350 
351    assert(r.intersectsWith(Rect(4, 4, 2, 9)));
352    assert(r.intersectsWith(Rect(3, 3, 1, 1)));
353    assert(r.intersectsWith(Rect(0, 3, 3, 0)));
354    assert(r.intersectsWith(Rect(3, 2, 0, 1)));
355    assert(!r.intersectsWith(Rect(3, 1, 0, 1)));
356    assert(r.intersectsWith(Rect(5, 6, 1, 1)));
357    assert(!r.intersectsWith(Rect(7, 6, 1, 1)));
358    assert(!r.intersectsWith(Rect(6, 7, 1, 1)));
359 }
360 
361 /// Color value representation
362 struct Color {
363    /// Red, green, blue and alpha channel color values.
364    @property ubyte r() nothrow {
365       validateColor();
366       return color.red;
367    }
368 
369    @property ubyte g() nothrow {
370       validateColor();
371       return color.green;
372    }
373 
374    @property ubyte b() nothrow {
375       validateColor();
376       return color.blue;
377    }
378 
379    @property ubyte a() nothrow { /+ validateColor(); +/ return color.alpha;
380    }
381 
382    /// Return the numeric color value.
383    COLORREF toArgb() nothrow {
384       validateColor();
385       return color.cref;
386    }
387 
388    /// Return the numeric red, green and blue color value.
389    COLORREF toRgb() nothrow {
390       validateColor();
391       return color.cref & 0x00FFFFFF;
392    }
393 
394    // Used internally.
395    HBRUSH createBrush() nothrow { // package
396       HBRUSH hbr;
397       if (_systemColorIndex == Color.INVAILD_SYSTEM_COLOR_INDEX) {
398          hbr = CreateSolidBrush(toRgb());
399       } else {
400          hbr = GetSysColorBrush(_systemColorIndex);
401       }
402       return hbr;
403    }
404 
405    Color* Dthisptr(Color* t) pure nothrow {
406       return t;
407    }
408 
409    Color* Dthisptr(ref Color t) pure nothrow {
410       return &t;
411    }
412 
413    Color Dthisval(Color* t) pure nothrow {
414       return *t;
415    }
416 
417    Color Dthisval(Color t) pure nothrow {
418       return t;
419    }
420 
421    deprecated static Color opCall(COLORREF argb) {
422       Color nc;
423       nc.color.cref = argb;
424       return nc;
425    }
426 
427    /// Construct a new color.
428    private this(_color c) pure nothrow {
429       color = c;
430    }
431 
432    /// Construct a new color.
433    this(ubyte alpha, Color c) pure nothrow {
434       this = fromRgb(alpha, c.color.cref);
435    }
436 
437    this(ubyte red, ubyte green, ubyte blue) pure nothrow {
438       this = fromArgb(0xff, red, green, blue);
439    }
440 
441    this(ubyte alpha, ubyte red, ubyte green, ubyte blue) pure nothrow {
442       this = fromArgb(alpha, red, green, blue);
443    }
444 
445    //alias opCall fromArgb;
446    static Color fromArgb(ubyte alpha, ubyte red, ubyte green, ubyte blue) pure nothrow {
447       return Color(_color((alpha << 24) | (blue << 16) | (green << 8) | red));
448    }
449 
450    static Color fromRgb(COLORREF rgb) pure nothrow {
451       if (CLR_NONE == rgb) {
452          return empty;
453       }
454       return Color(_color(cast(COLORREF)(rgb | 0xff000000)));
455    }
456 
457    static Color fromRgb(ubyte alpha, COLORREF rgb) pure nothrow {
458       return Color(_color(rgb | ((cast(COLORREF) alpha) << 24)));
459    }
460 
461    static @property Color empty() pure nothrow {
462       return Color(0, 0, 0, 0);
463    }
464 
465    /// Return a completely transparent color value.
466    static @property Color transparent() nothrow {
467       return Color.fromArgb(0, 0xFF, 0xFF, 0xFF);
468    }
469 
470    deprecated alias blend = blendColor;
471 
472    /// Blend colors; alpha channels are ignored.
473    // Blends the color channels half way.
474    // Does not consider alpha channels and discards them.
475    // The new blended color is returned; -this- Color is not modified.
476    Color blendColor(Color wc) nothrow {
477       if (Dthisval(this) == Color.empty) {
478          return wc;
479       }
480       if (wc == Color.empty) {
481          return Dthisval(this);
482       }
483 
484       validateColor();
485       wc.validateColor();
486 
487       return Color(cast(ubyte)((cast(uint) color.red + cast(uint) wc.color.red) >> 1),
488          cast(ubyte)((cast(uint) color.green + cast(uint) wc.color.green) >> 1),
489          cast(ubyte)((cast(uint) color.blue + cast(uint) wc.color.blue) >> 1));
490    }
491 
492    /// Alpha blend this color with a background color to return a solid color (100% opaque).
493    // Blends with backColor if this color has opacity to produce a solid color.
494    // Returns the new solid color, or the original color if no opacity.
495    // If backColor has opacity, it is ignored.
496    // The new blended color is returned; -this- Color is not modified.
497    Color solidColor(Color backColor) nothrow {
498       //if(0x7F == this.color.alpha)
499       // return blendColor(backColor);
500       //if(Dthisval(this) == Color.empty) // Checked if(0 == this.color.alpha)
501       // return backColor;
502       if (0 == this.color.alpha) {
503          return backColor;
504       }
505       if (backColor == Color.empty) {
506          return Dthisval(this);
507       }
508       if (0xFF == this.color.alpha) {
509          return Dthisval(this);
510       }
511 
512       validateColor();
513       backColor.validateColor();
514 
515       float fa, ba;
516       fa = cast(float) color.alpha / 255.0;
517       ba = 1.0 - fa;
518 
519       Color result;
520       result.color.alpha = 0xFF;
521       result.color.red = cast(ubyte)(this.color.red * fa + backColor.color.red * ba);
522       result.color.green = cast(ubyte)(this.color.green * fa + backColor.color.green * ba);
523       result.color.blue = cast(ubyte)(this.color.blue * fa + backColor.color.blue * ba);
524       return result;
525    }
526 
527    package static Color systemColor(int colorIndex) pure nothrow {
528       Color c;
529       c.sysIndex = cast(ubyte) colorIndex;
530       c.color.alpha = 0xFF;
531       return c;
532    }
533 
534    // Gets color index or INVAILD_SYSTEM_COLOR_INDEX.
535    package @property int _systemColorIndex() pure nothrow {
536       return sysIndex;
537    }
538 
539    package enum ubyte INVAILD_SYSTEM_COLOR_INDEX = ubyte.max;
540 
541 private:
542    union _color {
543       COLORREF cref;
544       struct {
545       align(1):
546          ubyte red;
547          ubyte green;
548          ubyte blue;
549          ubyte alpha;
550       }
551    }
552 
553    static assert(_color.sizeof == uint.sizeof);
554    _color color;
555 
556    ubyte sysIndex = INVAILD_SYSTEM_COLOR_INDEX;
557 
558    void validateColor() nothrow {
559       if (sysIndex != INVAILD_SYSTEM_COLOR_INDEX) {
560          color.cref = GetSysColor(sysIndex);
561          //color.alpha = 0xFF; // Should already be set.
562       }
563    }
564 }
565 
566 unittest {
567    enum red = Color.fromArgb(0xff, 0xff, 0x00, 0x00);
568 }
569 
570 class SystemColors {
571    private this() {
572    }
573 
574 static:
575 
576    @property Color activeBorder() {
577       return Color.systemColor(COLOR_ACTIVEBORDER);
578    }
579 
580    @property Color activeCaption() {
581       return Color.systemColor(COLOR_ACTIVECAPTION);
582    }
583 
584    @property Color activeCaptionText() {
585       return Color.systemColor(COLOR_CAPTIONTEXT);
586    }
587 
588    @property Color appWorkspace() {
589       return Color.systemColor(COLOR_APPWORKSPACE);
590    }
591 
592    @property Color control() {
593       return Color.systemColor(COLOR_BTNFACE);
594    }
595 
596    @property Color controlDark() {
597       return Color.systemColor(COLOR_BTNSHADOW);
598    }
599 
600    @property Color controlDarkDark() {
601       return Color.systemColor(COLOR_3DDKSHADOW); // ?
602    }
603 
604    @property Color controlLight() {
605       return Color.systemColor(COLOR_3DLIGHT);
606    }
607 
608    @property Color controlLightLight() {
609       return Color.systemColor(COLOR_BTNHIGHLIGHT); // ?
610    }
611 
612    @property Color controlText() {
613       return Color.systemColor(COLOR_BTNTEXT);
614    }
615 
616    @property Color desktop() {
617       return Color.systemColor(COLOR_DESKTOP);
618    }
619 
620    @property Color grayText() {
621       return Color.systemColor(COLOR_GRAYTEXT);
622    }
623 
624    @property Color highlight() {
625       return Color.systemColor(COLOR_HIGHLIGHT);
626    }
627 
628    @property Color highlightText() {
629       return Color.systemColor(COLOR_HIGHLIGHTTEXT);
630    }
631 
632    @property Color hotTrack() {
633       return Color(0, 0, 0xFF); // ?
634    }
635 
636    @property Color inactiveBorder() {
637       return Color.systemColor(COLOR_INACTIVEBORDER);
638    }
639 
640    @property Color inactiveCaption() {
641       return Color.systemColor(COLOR_INACTIVECAPTION);
642    }
643 
644    @property Color inactiveCaptionText() {
645       return Color.systemColor(COLOR_INACTIVECAPTIONTEXT);
646    }
647 
648    @property Color info() {
649       return Color.systemColor(COLOR_INFOBK);
650    }
651 
652    @property Color infoText() {
653       return Color.systemColor(COLOR_INFOTEXT);
654    }
655 
656    @property Color menu() {
657       return Color.systemColor(COLOR_MENU);
658    }
659 
660    @property Color menuText() {
661       return Color.systemColor(COLOR_MENUTEXT);
662    }
663 
664    @property Color scrollBar() {
665       return Color.systemColor(CTLCOLOR_SCROLLBAR);
666    }
667 
668    @property Color window() {
669       return Color.systemColor(COLOR_WINDOW);
670    }
671 
672    @property Color windowFrame() {
673       return Color.systemColor(COLOR_WINDOWFRAME);
674    }
675 
676    @property Color windowText() {
677       return Color.systemColor(COLOR_WINDOWTEXT);
678    }
679 }
680 
681 class SystemIcons {
682    private this() {
683    }
684 
685 static:
686 
687    @property Icon application() {
688       return new Icon(LoadIcon(null, IDI_APPLICATION), false);
689    }
690 
691    @property Icon error() {
692       return new Icon(LoadIcon(null, IDI_HAND), false);
693    }
694 
695    @property Icon question() {
696       return new Icon(LoadIcon(null, IDI_QUESTION), false);
697    }
698 
699    @property Icon warning() {
700       return new Icon(LoadIcon(null, IDI_EXCLAMATION), false);
701    }
702 
703    @property Icon information() {
704       return new Icon(LoadIcon(null, IDI_INFORMATION), false);
705    }
706 }
707 
708 /+
709 class ImageFormat {
710    /+
711    this(guid) {
712 
713    }
714 
715 
716    final @property guid() {
717       return guid;
718    }
719    +/
720 
721 
722  static:
723 
724    @property ImageFormat bmp() {
725       return null;
726    }
727 
728 
729    @property ImageFormat icon() {
730       return null;
731    }
732 }
733 +/
734 
735 abstract class Image {
736    //flags(); // getter ???
737 
738    /+
739    final @property ImageFormat rawFormat();
740    +/
741 
742    static Bitmap fromHBitmap(HBITMAP hbm) { // package
743       return new Bitmap(hbm, false); // Not owned. Up to caller to manage or call dispose().
744    }
745 
746    /+
747    static Image fromFile(Dstring file) {
748       return new Image(LoadImageA());
749    }
750    +/
751 
752    void draw(Graphics g, Point pt);
753 
754    void drawStretched(Graphics g, Rect r);
755 
756    @property Size size();
757 
758    @property int width() {
759       return size.width;
760    }
761 
762    @property int height() {
763       return size.height;
764    }
765 
766    int _imgtype(HGDIOBJ* ph) { // internal
767       if (ph) {
768          *ph = HGDIOBJ.init;
769       }
770       return 0; // 1 = bitmap; 2 = icon.
771    }
772 }
773 
774 class Bitmap : Image {
775 
776    // Load from a bmp file.
777    this(Dstring fileName) {
778       this.hbm = cast(HBITMAP) loadImage(null, fileName,
779          IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
780       if (!this.hbm) {
781          throw new DflException("Unable to load bitmap from file '" ~ fileName ~ "'");
782       }
783    }
784 
785    // Used internally.
786    this(HBITMAP hbm, bool owned = true) {
787       this.hbm = hbm;
788       this.owned = owned;
789    }
790 
791    final @property HBITMAP handle() {
792       return hbm;
793    }
794 
795    private void _getInfo(BITMAP* bm) {
796       if (GetObjectA(hbm, BITMAP.sizeof, bm) != BITMAP.sizeof) {
797          throw new DflException("Unable to get image information");
798       }
799    }
800 
801    final override @property Size size() {
802       BITMAP bm;
803       _getInfo(&bm);
804       return Size(bm.bmWidth, bm.bmHeight);
805    }
806 
807    final override @property int width() {
808       return size.width;
809    }
810 
811    final override @property int height() {
812       return size.height;
813    }
814 
815    private void _draw(Graphics g, Point pt, HDC memdc) {
816       HGDIOBJ hgo;
817       Size sz;
818 
819       sz = size;
820       hgo = SelectObject(memdc, hbm);
821       BitBlt(g.handle, pt.x, pt.y, sz.width, sz.height, memdc, 0, 0, SRCCOPY);
822       SelectObject(memdc, hgo); // Old bitmap.
823    }
824 
825    final override void draw(Graphics g, Point pt) {
826       HDC memdc;
827       memdc = CreateCompatibleDC(g.handle);
828       try {
829          _draw(g, pt, memdc);
830       }
831       finally {
832          DeleteDC(memdc);
833       }
834    }
835 
836    // -tempMemGraphics- is used as a temporary Graphics instead of
837    // creating and destroying a temporary one for each call.
838    final void draw(Graphics g, Point pt, Graphics tempMemGraphics) {
839       _draw(g, pt, tempMemGraphics.handle);
840    }
841 
842    private void _drawStretched(Graphics g, Rect r, HDC memdc) {
843       HGDIOBJ hgo;
844       Size sz;
845       int lstretch;
846 
847       sz = size;
848       hgo = SelectObject(memdc, hbm);
849       lstretch = SetStretchBltMode(g.handle, COLORONCOLOR);
850       StretchBlt(g.handle, r.x, r.y, r.width, r.height, memdc, 0, 0, sz.width, sz.height,
851          SRCCOPY);
852       SetStretchBltMode(g.handle, lstretch);
853       SelectObject(memdc, hgo); // Old bitmap.
854    }
855 
856    final override void drawStretched(Graphics g, Rect r) {
857       HDC memdc;
858       memdc = CreateCompatibleDC(g.handle);
859       try {
860          _drawStretched(g, r, memdc);
861       }
862       finally {
863          DeleteDC(memdc);
864       }
865    }
866 
867    // -tempMemGraphics- is used as a temporary Graphics instead of
868    // creating and destroying a temporary one for each call.
869    final void drawStretched(Graphics g, Rect r, Graphics tempMemGraphics) {
870       _drawStretched(g, r, tempMemGraphics.handle);
871    }
872 
873    void dispose() {
874       assert(owned);
875       DeleteObject(hbm);
876       hbm = null;
877    }
878 
879    ~this() {
880       if (owned) {
881          dispose();
882       }
883    }
884 
885    override int _imgtype(HGDIOBJ* ph) { // internal
886       if (ph) {
887          *ph = cast(HGDIOBJ) hbm;
888       }
889       return 1;
890    }
891 
892 private:
893    HBITMAP hbm;
894    bool owned = true;
895 }
896 
897 final class EnhancedMetaFile : Image {
898 private:
899    HENHMETAFILE hemf;
900    ENHMETAHEADER emfh;
901    HDC hdcref;
902    bool owned;
903 public:
904    // Used internally.
905    this(HENHMETAFILE hemf, HDC hdcref = null, bool owned = true) {
906       this.hemf = hemf;
907       GetEnhMetaFileHeader(hemf, ENHMETAHEADER.sizeof, &emfh);
908       assert(hdcref || owned);
909       if (!hdcref) {
910          this.hdcref = GetDC(null);
911          this.owned = true;
912       }
913    }
914 
915    /// Load from a emf file.
916    this(string fileName, HDC hdcref = null) {
917       import std.utf;
918 
919       auto tmp = GetEnhMetaFileW(fileName.toUTF16z());
920       if (!tmp) {
921          throw new DflException("Unable to load EnhanceMetaFile from file '" ~ fileName ~ "'");
922       }
923       this(tmp, hdcref);
924    }
925 
926    void dispose() {
927       DeleteEnhMetaFile(hemf);
928       hemf = null;
929       if (owned) {
930          ReleaseDC(null, hdcref);
931       }
932    }
933 
934    ~this() {
935       dispose();
936    }
937 
938    final HENHMETAFILE handle() @property {
939       return hemf;
940    }
941 
942    Rect bounds() const nothrow @property {
943       with (emfh) {
944          auto rc = RECT(MulDiv(rclBounds.left * 1000,
945             szlDevice.cx * GetDeviceCaps(cast(HDC) hdcref, HORZSIZE),
946             szlMicrometers.cx * GetDeviceCaps(cast(HDC) hdcref, HORZRES)),
947             MulDiv(rclBounds.top * 1000,
948             szlDevice.cy * GetDeviceCaps(cast(HDC) hdcref, VERTSIZE),
949             szlMicrometers.cy * GetDeviceCaps(cast(HDC) hdcref, VERTRES)),
950             MulDiv(rclBounds.right * 1000,
951             szlDevice.cx * GetDeviceCaps(cast(HDC) hdcref, HORZSIZE),
952             szlMicrometers.cx * GetDeviceCaps(cast(HDC) hdcref, HORZRES)),
953             MulDiv(rclBounds.bottom * 1000,
954             szlDevice.cy * GetDeviceCaps(cast(HDC) hdcref, VERTSIZE),
955             szlMicrometers.cy * GetDeviceCaps(cast(HDC) hdcref, VERTRES)));
956          return Rect(&rc);
957       }
958    }
959 
960    override int width() const nothrow @property {
961       with (emfh)
962          return MulDiv(rclFrame.right - rclFrame.left, szlDevice.cx * 10, szlMicrometers.cx);
963    }
964 
965    override int height() const nothrow @property {
966       with (emfh)
967          return MulDiv(rclFrame.bottom - rclFrame.top, szlDevice.cy * 10, szlMicrometers.cy);
968    }
969 
970    override Size size() const nothrow @property {
971       return Size(width, height);
972    }
973 
974    Rect frameRectangle() const nothrow @property {
975       with (emfh) {
976          return Rect(MulDiv(rclFrame.left, szlDevice.cx * 10,
977             szlMicrometers.cx), MulDiv(rclFrame.top, szlDevice.cy * 10,
978             szlMicrometers.cy), width, height);
979       }
980    }
981 
982    override void draw(Graphics g, Point pt) {
983       auto sz = size;
984       RECT rc;
985       Rect(pt.x, pt.y, sz.width, sz.height).getRect(&rc);
986       PlayEnhMetaFile(g.handle, hemf, &rc);
987    }
988 
989    override void drawStretched(Graphics g, Rect r) {
990       RECT rc;
991       r.getRect(&rc);
992       PlayEnhMetaFile(g.handle, hemf, &rc);
993    }
994 }
995 
996 class Picture : Image {
997    // Note: requires OleInitialize(null).
998 
999    // Throws exception on failure.
1000    this(DStream stm) {
1001       this.ipic = _fromDStream(stm);
1002       if (!this.ipic) {
1003          throw new DflException("Unable to load picture from stream");
1004       }
1005    }
1006 
1007    // Throws exception on failure.
1008    this(Dstring fileName) {
1009       this.ipic = _fromFileName(fileName);
1010       if (!this.ipic) {
1011          throw new DflException("Unable to load picture from file '" ~ fileName ~ "'");
1012       }
1013    }
1014 
1015    this(void[] mem) {
1016       this.ipic = _fromMemory(mem);
1017       if (!this.ipic) {
1018          throw new DflException("Unable to load picture from memory");
1019       }
1020    }
1021 
1022    private this(IPicture ipic) {
1023       this.ipic = ipic;
1024    }
1025 
1026    // Returns null on failure instead of throwing exception.
1027    static Picture fromStream(DStream stm) {
1028       auto ipic = _fromDStream(stm);
1029       if (!ipic) {
1030          return null;
1031       }
1032       return new Picture(ipic);
1033    }
1034 
1035    // Returns null on failure instead of throwing exception.
1036    static Picture fromFile(Dstring fileName) {
1037       auto ipic = _fromFileName(fileName);
1038       if (!ipic) {
1039          return null;
1040       }
1041       return new Picture(ipic);
1042    }
1043 
1044    static Picture fromMemory(void[] mem) {
1045       auto ipic = _fromMemory(mem);
1046       if (!ipic) {
1047          return null;
1048       }
1049       return new Picture(ipic);
1050    }
1051 
1052    final void draw(HDC hdc, Point pt) { // package
1053       int lhx, lhy;
1054       int width, height;
1055       lhx = loghimX;
1056       lhy = loghimY;
1057       width = MAP_LOGHIM_TO_PIX(lhx, GetDeviceCaps(hdc, LOGPIXELSX));
1058       height = MAP_LOGHIM_TO_PIX(lhy, GetDeviceCaps(hdc, LOGPIXELSY));
1059       ipic.Render(hdc, pt.x, pt.y + height, width, -height, 0, 0, lhx, lhy, null);
1060    }
1061 
1062    final override void draw(Graphics g, Point pt) {
1063       return draw(g.handle, pt);
1064    }
1065 
1066    final void drawStretched(HDC hdc, Rect r) { // package
1067       int lhx, lhy;
1068       lhx = loghimX;
1069       lhy = loghimY;
1070       ipic.Render(hdc, r.x, r.y + r.height, r.width, -r.height, 0, 0, lhx, lhy, null);
1071    }
1072 
1073    final override void drawStretched(Graphics g, Rect r) {
1074       return drawStretched(g.handle, r);
1075    }
1076 
1077    final @property OLE_XSIZE_HIMETRIC loghimX() {
1078       OLE_XSIZE_HIMETRIC xsz;
1079       if (S_OK != ipic.get_Width(&xsz)) {
1080          return 0; // ?
1081       }
1082       return xsz;
1083    }
1084 
1085    final @property OLE_YSIZE_HIMETRIC loghimY() {
1086       OLE_YSIZE_HIMETRIC ysz;
1087       if (S_OK != ipic.get_Height(&ysz)) {
1088          return 0; // ?
1089       }
1090       return ysz;
1091    }
1092 
1093    final override @property int width() {
1094       Graphics g;
1095       int result;
1096       g = Graphics.getScreen();
1097       result = getWidth(g);
1098       g.dispose();
1099       return result;
1100    }
1101 
1102    final override @property int height() {
1103       Graphics g;
1104       int result;
1105       g = Graphics.getScreen();
1106       result = getHeight(g);
1107       g.dispose();
1108       return result;
1109    }
1110 
1111    final override @property Size size() {
1112       Graphics g;
1113       Size result;
1114       g = Graphics.getScreen();
1115       result = getSize(g);
1116       g.dispose();
1117       return result;
1118    }
1119 
1120    final int getWidth(HDC hdc) { // package
1121       return MAP_LOGHIM_TO_PIX(loghimX, GetDeviceCaps(hdc, LOGPIXELSX));
1122    }
1123 
1124    final int getWidth(Graphics g) {
1125       return getWidth(g.handle);
1126    }
1127 
1128    final int getHeight(HDC hdc) { // package
1129       return MAP_LOGHIM_TO_PIX(loghimY, GetDeviceCaps(hdc, LOGPIXELSX));
1130    }
1131 
1132    final int getHeight(Graphics g) {
1133       return getHeight(g.handle);
1134    }
1135 
1136    final Size getSize(HDC hdc) { // package
1137       return Size(getWidth(hdc), getHeight(hdc));
1138    }
1139 
1140    final Size getSize(Graphics g) {
1141       return Size(getWidth(g), getHeight(g));
1142    }
1143 
1144    void dispose() {
1145       if (HBITMAP.init != _hbmimgtype) {
1146          DeleteObject(_hbmimgtype);
1147          _hbmimgtype = HBITMAP.init;
1148       }
1149 
1150       if (ipic) {
1151          ipic.Release();
1152          ipic = null;
1153       }
1154    }
1155 
1156    ~this() {
1157       dispose();
1158    }
1159 
1160    final HBITMAP toHBitmap(HDC hdc) { // package
1161       HDC memdc;
1162       HBITMAP result;
1163       HGDIOBJ oldbm;
1164       memdc = CreateCompatibleDC(hdc);
1165       if (!memdc) {
1166          throw new DflException("Device error");
1167       }
1168       try {
1169          Size sz;
1170          sz = getSize(hdc);
1171          result = CreateCompatibleBitmap(hdc, sz.width, sz.height);
1172          if (!result) {
1173          bad_bm:
1174             throw new DflException("Unable to allocate image");
1175          }
1176          oldbm = SelectObject(memdc, result);
1177          draw(memdc, Point(0, 0));
1178       }
1179       finally {
1180          if (oldbm) {
1181             SelectObject(memdc, oldbm);
1182          }
1183          DeleteDC(memdc);
1184       }
1185       return result;
1186    }
1187 
1188    final Bitmap toBitmap(HDC hdc) { // package
1189       HBITMAP hbm;
1190       hbm = toHBitmap(hdc);
1191       if (!hbm) {
1192          throw new DflException("Unable to create bitmap");
1193       }
1194       return new Bitmap(hbm, true); // Owned.
1195    }
1196 
1197    final Bitmap toBitmap() {
1198       Bitmap result;
1199       scope Graphics g = Graphics.getScreen();
1200       result = toBitmap(g);
1201       //g.dispose(); // scope'd
1202       return result;
1203    }
1204 
1205    final Bitmap toBitmap(Graphics g) {
1206       return toBitmap(g.handle);
1207    }
1208 
1209    HBITMAP _hbmimgtype;
1210 
1211    override int _imgtype(HGDIOBJ* ph) { // internal
1212       if (ph) {
1213          if (HBITMAP.init == _hbmimgtype) {
1214             scope Graphics g = Graphics.getScreen();
1215             _hbmimgtype = toHBitmap(g.handle);
1216             //g.dispose(); // scope'd
1217          }
1218 
1219          *ph = _hbmimgtype;
1220       }
1221       return 1;
1222    }
1223 
1224    private IPicture ipic = null;
1225 
1226    static IPicture _fromIStream(IStream istm) {
1227       IPicture ipic;
1228       // IID_IPicture from uiid.d.
1229       // if I use directy:
1230       // REFIID refiid = &IID_IPicture;
1231       // compiler complains
1232       // is not a lvalue
1233       const(GUID) X = {
1234          0x7BF80980, 0xBF32, 0x101A, [0x8B, 0xBB, 0x00, 0xAA, 0x00, 0x30, 0x0C, 0xAB]};
1235          REFIID refiid = &X;
1236          switch (OleLoadPicture(istm, 0, FALSE, refiid, cast(void**)&ipic)) {
1237          case S_OK:
1238             return ipic;
1239 
1240             debug (DFL_X) {
1241          case E_OUTOFMEMORY:
1242                debug assert(0, "Picture: Out of memory");
1243                break;
1244          case E_NOINTERFACE:
1245                debug assert(0, "Picture: The object does not support the interface");
1246                break;
1247          case E_UNEXPECTED:
1248                debug assert(0, "Picture: Unexpected error");
1249                break;
1250          case E_POINTER:
1251                debug assert(0, "Picture: Invalid pointer");
1252                break;
1253          case E_FAIL:
1254                debug assert(0, "Picture: Fail");
1255                break;
1256             }
1257 
1258          default:
1259          }
1260          return null;
1261       }
1262 
1263       static IPicture _fromDStream(DStream stm) {
1264          assert(stm !is null);
1265          scope DStreamToIStream istm = new DStreamToIStream(stm);
1266          return _fromIStream(istm);
1267       }
1268 
1269       static IPicture _fromFileName(Dstring fileName) {
1270          // FIX:    alias dfl.internal.winapi.HANDLE HANDLE; // Otherwise, odd conflict with wine.
1271 
1272          HANDLE hf;
1273          HANDLE hg;
1274          void* pg;
1275          DWORD dwsz, dw;
1276 
1277          hf = dfl.internal.utf.createFile(fileName, GENERIC_READ,
1278             FILE_SHARE_READ, null, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, null);
1279          if (!hf) {
1280             return null;
1281          }
1282 
1283          dwsz = GetFileSize(hf, null);
1284          if (0xFFFFFFFF == dwsz) {
1285          failclose:
1286             CloseHandle(hf);
1287             return null;
1288          }
1289 
1290          hg = GlobalAlloc(GMEM_MOVEABLE, dwsz);
1291          if (!hg) {
1292             goto failclose;
1293          }
1294 
1295          pg = GlobalLock(hg);
1296          if (!pg) {
1297             CloseHandle(hf);
1298             CloseHandle(hg);
1299             return null;
1300          }
1301 
1302          if (!ReadFile(hf, pg, dwsz, &dw, null) || dwsz != dw) {
1303             CloseHandle(hf);
1304             GlobalUnlock(hg);
1305             CloseHandle(hg);
1306             return null;
1307          }
1308 
1309          CloseHandle(hf);
1310          GlobalUnlock(hg);
1311 
1312          IStream istm;
1313          IPicture ipic;
1314 
1315          if (S_OK != CreateStreamOnHGlobal(hg, TRUE, &istm)) {
1316             CloseHandle(hg);
1317             return null;
1318          }
1319          // Don't need to CloseHandle(hg) due to 2nd param being TRUE.
1320 
1321          ipic = _fromIStream(istm);
1322          istm.Release();
1323          return ipic;
1324       }
1325 
1326       static IPicture _fromMemory(void[] mem) {
1327          return _fromIStream(new MemoryIStream(mem));
1328       }
1329 
1330    }
1331 
1332    enum TextTrimming : UINT {
1333       NONE = 0,
1334       ELLIPSIS = DT_END_ELLIPSIS,
1335       ELLIPSIS_PATH = DT_PATH_ELLIPSIS,
1336    }
1337 
1338    enum TextFormatFlags : UINT {
1339       NO_PREFIX = DT_NOPREFIX,
1340       DIRECTION_RIGHT_TO_LEFT = DT_RTLREADING,
1341       WORD_BREAK = DT_WORDBREAK,
1342       SINGLE_LINE = DT_SINGLELINE,
1343       NO_CLIP = DT_NOCLIP,
1344       LINE_LIMIT = DT_EDITCONTROL,
1345    }
1346 
1347    enum TextAlignment : UINT {
1348       LEFT = DT_LEFT, ///
1349       RIGHT = DT_RIGHT,
1350       CENTER = DT_CENTER,
1351 
1352       TOP = DT_TOP, /// Single line only alignment.
1353       BOTTOM = DT_BOTTOM,
1354       MIDDLE = DT_VCENTER,
1355    }
1356 
1357    class TextFormat {
1358 
1359       this() {
1360       }
1361 
1362       this(TextFormat tf) {
1363          _trim = tf._trim;
1364          _flags = tf._flags;
1365          _align = tf._align;
1366          _params = tf._params;
1367       }
1368 
1369       this(TextFormatFlags flags) {
1370          _flags = flags;
1371       }
1372 
1373       static @property TextFormat genericDefault() {
1374          TextFormat result;
1375          result = new TextFormat;
1376          result._trim = TextTrimming.NONE;
1377          result._flags = TextFormatFlags.NO_PREFIX | TextFormatFlags.WORD_BREAK
1378             | TextFormatFlags.NO_CLIP | TextFormatFlags.LINE_LIMIT;
1379          return result;
1380       }
1381 
1382       static @property TextFormat genericTypographic() {
1383          return new TextFormat;
1384       }
1385 
1386       final @property void alignment(TextAlignment ta) {
1387          _align = ta;
1388       }
1389 
1390       final @property TextAlignment alignment() {
1391          return _align;
1392       }
1393 
1394       final @property void formatFlags(TextFormatFlags tff) {
1395          _flags = tff;
1396       }
1397 
1398       final @property TextFormatFlags formatFlags() {
1399          return _flags;
1400       }
1401 
1402       final @property void trimming(TextTrimming tt) {
1403          _trim = tt;
1404       }
1405 
1406       final @property TextTrimming trimming() {
1407          return _trim;
1408       }
1409 
1410       // Units of the average character width.
1411 
1412       final @property void tabLength(int tablen) {
1413          _params.iTabLength = tablen;
1414       }
1415 
1416       final @property int tabLength() {
1417          return _params.iTabLength;
1418       }
1419 
1420       // Units of the average character width.
1421 
1422       final @property void leftMargin(int sz) {
1423          _params.iLeftMargin = sz;
1424       }
1425 
1426       final @property int leftMargin() {
1427          return _params.iLeftMargin;
1428       }
1429 
1430       // Units of the average character width.
1431 
1432       final @property void rightMargin(int sz) {
1433          _params.iRightMargin = sz;
1434       }
1435 
1436       final @property int rightMargin() {
1437          return _params.iRightMargin;
1438       }
1439 
1440    private:
1441       TextTrimming _trim = TextTrimming.NONE; // TextTrimming.CHARACTER.
1442       TextFormatFlags _flags = TextFormatFlags.NO_PREFIX | TextFormatFlags.WORD_BREAK;
1443       TextAlignment _align = TextAlignment.LEFT;
1444       package DRAWTEXTPARAMS _params = {DRAWTEXTPARAMS.sizeof, 8, 0, 0};
1445    }
1446 
1447    class Screen {
1448 
1449       static @property Screen primaryScreen() {
1450          version (DFL_MULTIPLE_SCREENS) {
1451             _getScreens();
1452             if (_screens.length > 0) {
1453                if (_screens.length == 1) {
1454                   return _screens[0];
1455                }
1456                MONITORINFO mi;
1457                for (int i = 0; i < _screens.length; i++) {
1458                   _screens[i]._getInfo(mi);
1459                   if (mi.dwFlags & MONITORINFOF_PRIMARY) {
1460                      return _screens[i];
1461                   }
1462                }
1463             }
1464          }
1465          if (!_ps) {
1466             _setPs();
1467          }
1468          return _ps;
1469       }
1470 
1471       @property Rect bounds() {
1472          version (DFL_MULTIPLE_SCREENS) {
1473             if (HMONITOR.init != hmonitor) {
1474                MONITORINFO mi;
1475                _getInfo(mi);
1476                return Rect(&mi.rcMonitor);
1477             }
1478          }
1479          RECT area;
1480          if (!GetWindowRect(GetDesktopWindow(), &area)) {
1481             assert(0);
1482          }
1483          return Rect(&area);
1484       }
1485 
1486       @property Rect workingArea() {
1487          version (DFL_MULTIPLE_SCREENS) {
1488             if (HMONITOR.init != hmonitor) {
1489                MONITORINFO mi;
1490                _getInfo(mi);
1491                return Rect(&mi.rcWork);
1492             }
1493          }
1494          RECT area;
1495          if (!SystemParametersInfoA(SPI_GETWORKAREA, 0, &area, FALSE)) {
1496             return bounds;
1497          }
1498          return Rect(&area);
1499       }
1500 
1501       version (DFL_MULTIPLE_SCREENS) {
1502 
1503          debug {
1504             static @property void fakeMultipleScreens(bool byes) {
1505                if (byes) {
1506                   allScreens(); // Force populating.
1507                   if (_screens.length < 2) {
1508                      _screens ~= new Screen(HMFAKE);
1509                   }
1510                }
1511             }
1512 
1513             static @property bool fakeMultipleScreens() {
1514                return _screens.length > 1 && HMFAKE == _screens[1].hmonitor;
1515             }
1516 
1517             private enum HMONITOR HMFAKE = cast(HMONITOR) 1969253357;
1518          }
1519 
1520          static @property Screen[] allScreens() {
1521             version (DFL_MULTIPLE_SCREENS) {
1522                _getScreens();
1523                if (_screens.length > 0) {
1524                   return _screens;
1525                }
1526             }
1527             if (_screens.length < 1) {
1528                synchronized {
1529                   _screens = new Screen[1];
1530                   if (!_ps) {
1531                      _setPs();
1532                   }
1533                   _screens[0] = _ps;
1534                }
1535             }
1536             return _screens;
1537          }
1538 
1539          static Screen fromHandle(HWND hwnd) {
1540             version (DFL_MULTIPLE_SCREENS) {
1541                version (SUPPORTS_MULTIPLE_SCREENS) {
1542                   alias fromWindow = MonitorFromWindow;
1543                } else {
1544                   auto fromWindow = cast(typeof(&MonitorFromWindow)) GetProcAddress(
1545                      GetModuleHandleA("user32.dll"), "MonitorFromWindow");
1546                   if (!fromWindow) {
1547                      //throw new DflException("Multiple screens not supported");
1548                      goto _def;
1549                   }
1550                }
1551                HMONITOR hm = fromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
1552                debug {
1553                   if (fakeMultipleScreens && hm == _screens[0].hmonitor) {
1554                      RECT rect;
1555                      if (GetWindowRect(hwnd, &rect)) {
1556                         Rect r = Rect(&rect);
1557                         if (_withinFakeScreen(r)) {
1558                            return _screens[1];
1559                         }
1560                      }
1561                   }
1562                }
1563                return _findScreen(hm);
1564             }
1565          _def:
1566             return primaryScreen;
1567          }
1568 
1569          static Screen fromControl(IWindow ctrl) {
1570             return fromHandle(ctrl.handle);
1571          }
1572 
1573          static Screen fromPoint(Point pt) {
1574             version (DFL_MULTIPLE_SCREENS) {
1575                version (SUPPORTS_MULTIPLE_SCREENS) {
1576                   alias fromPoint = MonitorFromPoint;
1577                } else {
1578                   auto fromPoint = cast(typeof(&MonitorFromPoint)) GetProcAddress(
1579                      GetModuleHandleA("user32.dll"), "MonitorFromPoint");
1580                   if (!fromPoint) {
1581                      //throw new DflException("Multiple screens not supported");
1582                      goto _def;
1583                   }
1584                }
1585                HMONITOR hm = fromPoint(pt.point, MONITOR_DEFAULTTOPRIMARY);
1586                debug {
1587                   if (fakeMultipleScreens && hm == _screens[0].hmonitor) {
1588                      Rect r = Rect(pt, Size(0, 0));
1589                      if (_withinFakeScreen(r)) {
1590                         return _screens[1];
1591                      }
1592                   }
1593                }
1594                return _findScreen(hm);
1595             }
1596          _def:
1597             return primaryScreen;
1598          }
1599 
1600          static Screen fromRectangle(Rect r) {
1601             version (DFL_MULTIPLE_SCREENS) {
1602                version (SUPPORTS_MULTIPLE_SCREENS) {
1603                   alias fromRect = MonitorFromRect;
1604                } else {
1605                   auto fromRect = cast(typeof(&MonitorFromRect)) GetProcAddress(
1606                      GetModuleHandleA("user32.dll"), "MonitorFromRect");
1607                   if (!fromRect) {
1608                      //throw new DflException("Multiple screens not supported");
1609                      goto _def;
1610                   }
1611                }
1612                RECT rect;
1613                r.getRect(&rect);
1614                HMONITOR hm = fromRect(&rect, MONITOR_DEFAULTTOPRIMARY);
1615                debug {
1616                   if (fakeMultipleScreens && hm == _screens[0].hmonitor) {
1617                      if (_withinFakeScreen(r)) {
1618                         return _screens[1];
1619                      }
1620                   }
1621                }
1622                return _findScreen(hm);
1623             }
1624          _def:
1625             return primaryScreen;
1626          }
1627 
1628       }
1629 
1630    private:
1631 
1632       static void _setPs() {
1633          synchronized {
1634             if (!_ps) {
1635                _ps = new Screen();
1636             }
1637          }
1638       }
1639 
1640       this() {
1641       }
1642 
1643       this(HMONITOR hmonitor) {
1644          this.hmonitor = hmonitor;
1645       }
1646 
1647       HMONITOR hmonitor;
1648 
1649       static Screen _ps; // Primary screen; might not be used.
1650       static Screen[] _screens;
1651 
1652       version (DFL_MULTIPLE_SCREENS) {
1653 
1654          bool foundThis = true; // Used during _getScreens callback.
1655 
1656          static Screen _findScreen(HMONITOR hm) {
1657             foreach (Screen s; allScreens) {
1658                if (s.hmonitor == hm) {
1659                   return s;
1660                }
1661             }
1662             return primaryScreen;
1663          }
1664 
1665          static void _getScreens() {
1666             // Note: monitors can change, so always enum,
1667             // but update the array by removing old ones and adding new ones.
1668             for (int i = 0; i < _screens.length; i++) {
1669                _screens[i].foundThis = false;
1670                debug {
1671                   if (HMFAKE == _screens[i].hmonitor) {
1672                      _screens[i].foundThis = true;
1673                   }
1674                }
1675             }
1676             version (SUPPORTS_MULTIPLE_SCREENS) {
1677                pragma(msg, "DFL: multiple screens supported at compile time");
1678 
1679                alias enumScreens = EnumDisplayMonitors;
1680             } else {
1681                auto enumScreens = cast(typeof(&EnumDisplayMonitors)) GetProcAddress(
1682                   GetModuleHandleA("user32.dll"), "EnumDisplayMonitors");
1683                if (!enumScreens) {
1684                   //throw new DflException("Multiple screens not supported");
1685                   return;
1686                }
1687             }
1688             if (!enumScreens(null, null, &_gettingScreens, 0)) {
1689                //throw new DflException("Failed to enumerate screens");
1690                return;
1691             }
1692             {
1693                int numremoved = 0;
1694                for (int i = 0; i < _screens.length; i++) {
1695                   if (!_screens[i].foundThis) {
1696                      numremoved++;
1697                   }
1698                }
1699                if (numremoved > 0) {
1700                   Screen[] newscreens = new Screen[_screens.length - numremoved];
1701                   for (int i = 0, nsi = 0; i < _screens.length; i++) {
1702                      if (_screens[i].foundThis) {
1703                         newscreens[nsi++] = _screens[i];
1704                      }
1705                   }
1706                   _screens = newscreens;
1707                }
1708             }
1709          }
1710 
1711          debug {
1712             static bool _withinFakeScreen(Rect r) {
1713                Rect fr = _screens[1].bounds;
1714                //return r.right >= fr.x;
1715                if (r.x >= fr.x) {
1716                   return true;
1717                }
1718                if (r.right < fr.x) {
1719                   return false;
1720                }
1721                {
1722                   // See which side it's in most.
1723                   RECT rect;
1724                   r.getRect(&rect);
1725                   RECT w0 = rect;
1726                   assert(w0.right >= fr.width);
1727                   w0.right = fr.width;
1728                   RECT w1 = rect;
1729                   assert(w1.left <= fr.width);
1730                   w1.left = fr.width;
1731                   return Rect(&w1).width > Rect(&w0).width;
1732                }
1733             }
1734          }
1735 
1736          void _getInfo(ref MONITORINFO info) {
1737             version (SUPPORTS_MULTIPLE_SCREENS) {
1738                alias getMI = GetMonitorInfoA;
1739             } else {
1740                auto getMI = cast(typeof(&GetMonitorInfoA)) GetProcAddress(
1741                   GetModuleHandleA("user32.dll"), "GetMonitorInfoA");
1742                if (!getMI) {
1743                   throw new DflException(
1744                      "Error getting screen information (unable to find GetMonitorInfoA)");
1745                }
1746             }
1747             info.cbSize = MONITORINFO.sizeof;
1748             HMONITOR hm = hmonitor;
1749             int fake = -1;
1750             debug {
1751                if (fakeMultipleScreens) {
1752                   if (HMFAKE == hm) {
1753                      fake = 1;
1754                      hm = _screens[0].hmonitor;
1755                   } else if (hm == _screens[0].hmonitor) {
1756                      fake = 0;
1757                   }
1758                }
1759             }
1760             if (!getMI(hm, &info)) {
1761                throw new DflException("Unable to get screen information");
1762             }
1763             debug {
1764                if (1 == fake) {
1765                   info.dwFlags &= ~MONITORINFOF_PRIMARY;
1766                   {
1767                      Rect r = Rect(&info.rcMonitor);
1768                      int w = r.width >> 1;
1769                      r.x = r.x + w;
1770                      r.width = r.width - w;
1771                      r.getRect(&info.rcMonitor);
1772                   }
1773                   {
1774                      Rect r = Rect(&info.rcWork);
1775                      int w = r.width >> 1;
1776                      r.x = r.x + w;
1777                      r.width = r.width - w;
1778                      r.getRect(&info.rcWork);
1779                   }
1780                } else if (0 == fake) {
1781                   {
1782                      Rect r = Rect(&info.rcMonitor);
1783                      int w = r.width >> 1;
1784                      r.width = r.width - w;
1785                      r.getRect(&info.rcMonitor);
1786                   }
1787                   {
1788                      Rect r = Rect(&info.rcWork);
1789                      int w = r.width >> 1;
1790                      r.width = r.width - w;
1791                      r.getRect(&info.rcWork);
1792                   }
1793                }
1794             }
1795          }
1796 
1797       }
1798    }
1799 
1800    version (DFL_MULTIPLE_SCREENS) {
1801       private extern (Windows) BOOL _gettingScreens(HMONITOR hmonitor,
1802          HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) nothrow {
1803          for (int i = 0; i < Screen._screens.length; i++) {
1804             if (hmonitor == Screen._screens[i].hmonitor) {
1805                Screen._screens[i].foundThis = true;
1806                return TRUE; // Continue.
1807             }
1808          }
1809          // Didn't find it from old list, so add it.
1810          Screen._screens ~= new Screen(hmonitor);
1811          return TRUE; // Continue.
1812       }
1813 
1814    }
1815 
1816    class Graphics {
1817       // Used internally.
1818       this(HDC hdc, bool owned = true) {
1819          this.hdc = hdc;
1820          this.owned = owned;
1821       }
1822 
1823       ~this() {
1824          if (owned) {
1825             dispose();
1826          }
1827       }
1828 
1829       // Used internally.
1830       final void drawSizeGrip(int right, int bottom) { // package
1831          Color light, dark;
1832          int x, y;
1833 
1834          light = SystemColors.controlLightLight;
1835          dark = SystemColors.controlDark;
1836          scope Pen lightPen = new Pen(light);
1837          scope Pen darkPen = new Pen(dark);
1838          x = right;
1839          y = bottom;
1840 
1841          x -= 3;
1842          y -= 3;
1843          drawLine(darkPen, x, bottom, right, y);
1844          x--;
1845          y--;
1846          drawLine(darkPen, x, bottom, right, y);
1847          drawLine(lightPen, x - 1, bottom, right, y - 1);
1848 
1849          x -= 3;
1850          y -= 3;
1851          drawLine(darkPen, x, bottom, right, y);
1852          x--;
1853          y--;
1854          drawLine(darkPen, x, bottom, right, y);
1855          drawLine(lightPen, x - 1, bottom, right, y - 1);
1856 
1857          x -= 3;
1858          y -= 3;
1859          drawLine(darkPen, x, bottom, right, y);
1860          x--;
1861          y--;
1862          drawLine(darkPen, x, bottom, right, y);
1863          drawLine(lightPen, x - 1, bottom, right, y - 1);
1864       }
1865 
1866       // Used internally.
1867       // vSplit=true means the move grip moves left to right; false means top to bottom.
1868       final void drawMoveGrip(Rect movableArea, bool vSplit = true, size_t count = 5) { // package
1869          enum MSPACE = 4;
1870          enum MWIDTH = 3;
1871          enum MHEIGHT = 3;
1872 
1873          if (!count || !movableArea.width || !movableArea.height) {
1874             return;
1875          }
1876 
1877          Color norm, light, dark, ddark;
1878          int x, y;
1879          size_t iw;
1880 
1881          norm = SystemColors.control;
1882          light = SystemColors.controlLightLight.blendColor(norm); // center
1883          //dark = SystemColors.controlDark.blendColor(norm); // top
1884          ubyte ubmin(int ub) {
1885             if (ub <= 0) {
1886                return 0;
1887             }
1888             return cast(ubyte) ub;
1889          }
1890 
1891          dark = Color(ubmin(cast(int) norm.r - 0x10),
1892             ubmin(cast(int) norm.g - 0x10), ubmin(cast(int) norm.b - 0x10));
1893          //ddark = SystemColors.controlDarkDark; // bottom
1894          ddark = SystemColors.controlDark.blendColor(Color(0x10, 0x10, 0x10)); // bottom
1895          //scope Pen lightPen = new Pen(light);
1896          scope Pen darkPen = new Pen(dark);
1897          scope Pen ddarkPen = new Pen(ddark);
1898 
1899          void drawSingleMoveGrip() {
1900             Point[3] pts;
1901 
1902             pts[0].x = x + MWIDTH - 2;
1903             pts[0].y = y;
1904             pts[1].x = x;
1905             pts[1].y = y;
1906             pts[2].x = x;
1907             pts[2].y = y + MHEIGHT - 1;
1908             drawLines(darkPen, pts);
1909 
1910             pts[0].x = x + MWIDTH - 1;
1911             pts[0].y = y + 1;
1912             pts[1].x = pts[0].x;
1913             pts[1].y = y + MHEIGHT - 1;
1914             pts[2].x = x;
1915             pts[2].y = pts[1].y;
1916             drawLines(ddarkPen, pts);
1917 
1918             fillRectangle(light, x + 1, y + 1, 1, 1);
1919          }
1920 
1921          if (vSplit) {
1922             x = movableArea.x + (movableArea.width / 2 - MWIDTH / 2);
1923             //y = movableArea.height / 2 - ((MWIDTH * count) + (MSPACE * (count - 1))) / 2;
1924             y = movableArea.y + (movableArea.height / 2 - ((MWIDTH * count) + (MSPACE * count)) / 2);
1925 
1926             for (iw = 0; iw != count; iw++) {
1927                drawSingleMoveGrip();
1928                y += MHEIGHT + MSPACE;
1929             }
1930          } else { // hSplit
1931             //x = movableArea.width / 2 - ((MHEIGHT * count) + (MSPACE * (count - 1))) / 2;
1932             x = movableArea.x + (movableArea.width / 2 - ((MHEIGHT * count) + (MSPACE * count)) / 2);
1933             y = movableArea.y + (movableArea.height / 2 - MHEIGHT / 2);
1934 
1935             for (iw = 0; iw != count; iw++) {
1936                drawSingleMoveGrip();
1937                x += MWIDTH + MSPACE;
1938             }
1939          }
1940       }
1941 
1942       package final TextFormat getCachedTextFormat() {
1943          static TextFormat fmt = null;
1944          if (!fmt) {
1945             fmt = TextFormat.genericDefault;
1946          }
1947          return fmt;
1948       }
1949 
1950       // Windows 95/98/Me limits -text- to 8192 characters.
1951 
1952       final void drawText(Dstring text, Font font, Color color, Rect r, TextFormat fmt) {
1953          // Should SaveDC/RestoreDC be used instead?
1954 
1955          COLORREF prevColor;
1956          HFONT prevFont;
1957          int prevBkMode;
1958 
1959          prevColor = SetTextColor(hdc, color.toRgb());
1960          prevFont = cast(HFONT) SelectObject(hdc, font ? font.handle : null);
1961          prevBkMode = SetBkMode(hdc, TRANSPARENT);
1962 
1963          RECT rect;
1964          r.getRect(&rect);
1965          dfl.internal.utf.drawTextEx(hdc, text, &rect,
1966             DT_EXPANDTABS | DT_TABSTOP | fmt._trim | fmt._flags | fmt._align, &fmt._params);
1967 
1968          // Reset stuff.
1969          //if(CLR_INVALID != prevColor)
1970          SetTextColor(hdc, prevColor);
1971          //if(prevFont)
1972          SelectObject(hdc, prevFont);
1973          //if(prevBkMode)
1974          SetBkMode(hdc, prevBkMode);
1975       }
1976 
1977       final void drawText(Dstring text, Font font, Color color, Rect r) {
1978          return drawText(text, font, color, r, getCachedTextFormat());
1979       }
1980 
1981       final void drawTextDisabled(Dstring text, Font font, Color color,
1982          Color backColor, Rect r, TextFormat fmt) {
1983          r.offset(1, 1);
1984          //drawText(text, font, Color(24, color).solidColor(backColor), r, fmt); // Lighter, lower one.
1985          //drawText(text, font, Color.fromRgb(~color.toRgb() & 0xFFFFFF), r, fmt); // Lighter, lower one.
1986          drawText(text, font, Color(192,
1987             Color.fromRgb(~color.toRgb() & 0xFFFFFF)).solidColor(backColor), r, fmt); // Lighter, lower one.
1988          r.offset(-1, -1);
1989          drawText(text, font, Color(128, color).solidColor(backColor), r, fmt);
1990       }
1991 
1992       final void drawTextDisabled(Dstring text, Font font, Color color, Color backColor,
1993          Rect r) {
1994          return drawTextDisabled(text, font, color, backColor, r, getCachedTextFormat());
1995       }
1996 
1997       /+
1998       final Size measureText(Dstring text, Font font) {
1999          SIZE sz;
2000          HFONT prevFont;
2001 
2002          prevFont = cast(HFONT)SelectObject(hdc, font ? font.handle : null);
2003 
2004          dfl.internal.utf.getTextExtentPoint32(hdc, text, &sz);
2005 
2006          //if(prevFont)
2007          SelectObject(hdc, prevFont);
2008 
2009          return Size(sz.cx, sz.cy);
2010       }
2011    +/
2012 
2013       private enum int DEFAULT_MEASURE_SIZE = short.max; // Has to be smaller because it's 16-bits on win9x.
2014 
2015       final Size measureText(Dstring text, Font font, int maxWidth, TextFormat fmt) {
2016          RECT rect;
2017          HFONT prevFont;
2018 
2019          rect.left = 0;
2020          rect.top = 0;
2021          rect.right = maxWidth;
2022          rect.bottom = DEFAULT_MEASURE_SIZE;
2023 
2024          prevFont = cast(HFONT) SelectObject(hdc, font ? font.handle : null);
2025 
2026          if (!dfl.internal.utf.drawTextEx(hdc, text, &rect,
2027                DT_EXPANDTABS | DT_TABSTOP | fmt._trim | fmt._flags | fmt._align | DT_CALCRECT | DT_NOCLIP,
2028                &fmt._params)) {
2029             //throw new DflException("Text measure error");
2030             rect.left = 0;
2031             rect.top = 0;
2032             rect.right = 0;
2033             rect.bottom = 0;
2034          }
2035 
2036          //if(prevFont)
2037          SelectObject(hdc, prevFont);
2038 
2039          return Size(rect.right - rect.left, rect.bottom - rect.top);
2040       }
2041 
2042       final Size measureText(Dstring text, Font font, TextFormat fmt) {
2043          return measureText(text, font, DEFAULT_MEASURE_SIZE, fmt);
2044       }
2045 
2046       final Size measureText(Dstring text, Font font, int maxWidth) {
2047          return measureText(text, font, maxWidth, getCachedTextFormat());
2048       }
2049 
2050       final Size measureText(Dstring text, Font font) {
2051          return measureText(text, font, DEFAULT_MEASURE_SIZE, getCachedTextFormat());
2052       }
2053 
2054       /+
2055       // Doesn't work... dfl.internal.utf.drawTextEx uses a different buffer!
2056       // ///
2057       final Dstring getTrimmedText(Dstring text, Font font, Rect r, TextFormat fmt) { // deprecated
2058          switch(fmt.trimming) {
2059             case TextTrimming.ELLIPSIS:
2060             case TextTrimming.ELLIPSIS_PATH: {
2061                                                 char[] newtext;
2062                                                 RECT rect;
2063                                                 HFONT prevFont;
2064 
2065                                                 newtext = text.dup;
2066                                                 r.getRect(&rect);
2067                                                 prevFont = cast(HFONT)SelectObject(hdc, font ? font.handle : null);
2068 
2069                                                 // DT_CALCRECT needs to prevent it from actually drawing.
2070                                                 if(!dfl.internal.utf.drawTextEx(hdc, newtext, &rect, DT_EXPANDTABS | DT_TABSTOP |
2071                                                          fmt._trim | fmt._flags | fmt._align | DT_CALCRECT | DT_MODIFYSTRING | DT_NOCLIP, &fmt._params)) {
2072                                                    //throw new DflException("Text trimming error");
2073                                                 }
2074 
2075                                                 //if(prevFont)
2076                                                 SelectObject(hdc, prevFont);
2077 
2078                                                 for(size_t iw = 0; iw != newtext.length; iw++) {
2079                                                    if(!newtext[iw]) {
2080                                                       return newtext[0 .. iw];
2081                                                    }
2082                                                 }
2083                                                 //return newtext;
2084                                                 // There was no change, so no sense in keeping the duplicate.
2085                                                 delete newtext;
2086                                                 return text;
2087                                              }
2088                                              break;
2089 
2090             default:
2091                                              return text;
2092          }
2093       }
2094 
2095    // ///
2096    final Dstring getTrimmedText(Dstring text, Font font, Rect r, TextTrimming trim) {
2097       scope fmt = new TextFormat(TextFormatFlags.NO_PREFIX | TextFormatFlags.WORD_BREAK |
2098             TextFormatFlags.NO_CLIP | TextFormatFlags.LINE_LIMIT);
2099       fmt.trimming = trim;
2100       return getTrimmedText(text, font, r, fmt);
2101    }
2102    +/
2103 
2104       final void drawIcon(Icon icon, Rect r) {
2105          // DrawIconEx operates differently if the width or height is zero
2106          // so bail out if zero and pretend the zero size icon was drawn.
2107          int width = r.width;
2108          if (!width) {
2109             return;
2110          }
2111          int height = r.height;
2112          if (!height) {
2113             return;
2114          }
2115 
2116          DrawIconEx(handle, r.x, r.y, icon.handle, width, height, 0, null, DI_NORMAL);
2117       }
2118 
2119       final void drawIcon(Icon icon, int x, int y) {
2120          DrawIconEx(handle, x, y, icon.handle, 0, 0, 0, null, DI_NORMAL);
2121       }
2122 
2123       final void fillRectangle(Brush brush, Rect r) {
2124          fillRectangle(brush, r.x, r.y, r.width, r.height);
2125       }
2126 
2127       final void fillRectangle(Brush brush, int x, int y, int width, int height) {
2128          RECT rect;
2129          rect.left = x;
2130          rect.right = x + width;
2131          rect.top = y;
2132          rect.bottom = y + height;
2133          FillRect(handle, &rect, brush.handle);
2134       }
2135 
2136       // Extra function.
2137       final void fillRectangle(Color color, Rect r) {
2138          fillRectangle(color, r.x, r.y, r.width, r.height);
2139       }
2140 
2141       // Extra function.
2142       final void fillRectangle(Color color, int x, int y, int width, int height) {
2143          RECT rect;
2144          int prevBkColor;
2145 
2146          prevBkColor = SetBkColor(hdc, color.toRgb());
2147 
2148          rect.left = x;
2149          rect.top = y;
2150          rect.right = x + width;
2151          rect.bottom = y + height;
2152          ExtTextOutA(hdc, x, y, ETO_OPAQUE, &rect, "", 0, null);
2153 
2154          // Reset stuff.
2155          //if(CLR_INVALID != prevBkColor)
2156          SetBkColor(hdc, prevBkColor);
2157       }
2158 
2159       final void fillRegion(Brush brush, Region region) {
2160          FillRgn(handle, region.handle, brush.handle);
2161       }
2162 
2163       static Graphics fromHwnd(HWND hwnd) {
2164          return new CommonGraphics(hwnd, GetDC(hwnd));
2165       }
2166 
2167       /// Get the entire screen's Graphics for the primary monitor.
2168       static Graphics getScreen() {
2169          return new CommonGraphics(null, GetWindowDC(null));
2170       }
2171 
2172       final void drawLine(Pen pen, Point start, Point end) {
2173          drawLine(pen, start.x, start.y, end.x, end.y);
2174       }
2175 
2176       final void drawLine(Pen pen, int startX, int startY, int endX, int endY) {
2177          HPEN prevPen;
2178 
2179          prevPen = SelectObject(hdc, pen.handle);
2180 
2181          MoveToEx(hdc, startX, startY, null);
2182          LineTo(hdc, endX, endY);
2183 
2184          // Reset stuff.
2185          SelectObject(hdc, prevPen);
2186       }
2187 
2188       // First two points is the first line, the other points link a line
2189       // to the previous point.
2190       final void drawLines(Pen pen, Point[] points) {
2191          assert(points.length >= 2, "Not enough line points.");
2192 
2193          HPEN prevPen;
2194          int i;
2195 
2196          prevPen = SelectObject(hdc, pen.handle);
2197 
2198          MoveToEx(hdc, points[0].x, points[0].y, null);
2199          for (i = 1;;) {
2200             LineTo(hdc, points[i].x, points[i].y);
2201 
2202             if (++i == points.length) {
2203                break;
2204             }
2205          }
2206 
2207          // Reset stuff.
2208          SelectObject(hdc, prevPen);
2209       }
2210 
2211       final void drawArc(Pen pen, int x, int y, int width, int height, int arcX1,
2212          int arcY1, int arcX2, int arcY2) {
2213          HPEN prevPen;
2214 
2215          prevPen = SelectObject(hdc, pen.handle);
2216 
2217          Arc(hdc, x, y, x + width, y + height, arcX1, arcY1, arcX2, arcY2);
2218 
2219          // Reset stuff.
2220          SelectObject(hdc, prevPen);
2221       }
2222 
2223       final void drawArc(Pen pen, Rect r, Point arc1, Point arc2) {
2224          drawArc(pen, r.x, r.y, r.width, r.height, arc1.x, arc1.y, arc2.x, arc2.y);
2225       }
2226 
2227       final void drawBezier(Pen pen, Point[4] points) {
2228          HPEN prevPen;
2229          POINT* cpts;
2230 
2231          prevPen = SelectObject(hdc, pen.handle);
2232 
2233          // This assumes a Point is laid out exactly like a POINT.
2234          static assert(Point.sizeof == POINT.sizeof);
2235          cpts = cast(POINT*) cast(Point*) points;
2236 
2237          PolyBezier(hdc, cpts, 4);
2238 
2239          // Reset stuff.
2240          SelectObject(hdc, prevPen);
2241       }
2242 
2243       final void drawBezier(Pen pen, Point pt1, Point pt2, Point pt3, Point pt4) {
2244          Point[4] points;
2245          points[0] = pt1;
2246          points[1] = pt2;
2247          points[2] = pt3;
2248          points[3] = pt4;
2249          drawBezier(pen, points);
2250       }
2251 
2252       // First 4 points are the first bezier, each next 3 are the next
2253       // beziers, using the previous last point as the starting point.
2254       final void drawBeziers(Pen pen, Point[] points) {
2255          if (points.length < 1 || (points.length - 1) % 3) {
2256             assert(0); // Bad number of points.
2257             //return; // Let PolyBezier() do what it wants with the bad number.
2258          }
2259 
2260          HPEN prevPen;
2261          POINT* cpts;
2262 
2263          prevPen = SelectObject(hdc, pen.handle);
2264 
2265          // This assumes a Point is laid out exactly like a POINT.
2266          static assert(Point.sizeof == POINT.sizeof);
2267          cpts = cast(POINT*) cast(Point*) points;
2268 
2269          PolyBezier(hdc, cpts, points.length);
2270 
2271          // Reset stuff.
2272          SelectObject(hdc, prevPen);
2273       }
2274 
2275       // TODO: drawCurve(), drawClosedCurve() ...
2276 
2277       final void drawEllipse(Pen pen, Rect r) {
2278          drawEllipse(pen, r.x, r.y, r.width, r.height);
2279       }
2280 
2281       final void drawEllipse(Pen pen, int x, int y, int width, int height) {
2282          HPEN prevPen;
2283          HBRUSH prevBrush;
2284 
2285          prevPen = SelectObject(hdc, pen.handle);
2286          prevBrush = SelectObject(hdc, cast(HBRUSH) GetStockObject(NULL_BRUSH)); // Don't fill it in.
2287 
2288          Ellipse(hdc, x, y, x + width, y + height);
2289 
2290          // Reset stuff.
2291          SelectObject(hdc, prevPen);
2292          SelectObject(hdc, prevBrush);
2293       }
2294 
2295       // TODO: drawPie()
2296 
2297       final void drawPolygon(Pen pen, Point[] points) {
2298          if (points.length < 2) {
2299             assert(0); // Need at least 2 points.
2300             //return;
2301          }
2302 
2303          HPEN prevPen;
2304          HBRUSH prevBrush;
2305          POINT* cpts;
2306 
2307          prevPen = SelectObject(hdc, pen.handle);
2308          prevBrush = SelectObject(hdc, cast(HBRUSH) GetStockObject(NULL_BRUSH)); // Don't fill it in.
2309 
2310          // This assumes a Point is laid out exactly like a POINT.
2311          static assert(Point.sizeof == POINT.sizeof);
2312          cpts = cast(POINT*) cast(Point*) points;
2313 
2314          Polygon(hdc, cpts, points.length);
2315 
2316          // Reset stuff.
2317          SelectObject(hdc, prevPen);
2318          SelectObject(hdc, prevBrush);
2319       }
2320 
2321       final void drawRectangle(Pen pen, Rect r) {
2322          drawRectangle(pen, r.x, r.y, r.width, r.height);
2323       }
2324 
2325       final void drawRectangle(Pen pen, int x, int y, int width, int height) {
2326          HPEN prevPen;
2327          HBRUSH prevBrush;
2328 
2329          prevPen = SelectObject(hdc, pen.handle);
2330          prevBrush = SelectObject(hdc, cast(HBRUSH) GetStockObject(NULL_BRUSH)); // Don't fill it in.
2331 
2332          Rectangle(hdc, x, y, x + width, y + height);
2333 
2334          // Reset stuff.
2335          SelectObject(hdc, prevPen);
2336          SelectObject(hdc, prevBrush);
2337       }
2338 
2339       /+
2340       final void drawRectangle(Color c, Rect r) {
2341          drawRectangle(c, r.x, r.y, r.width, r.height);
2342       }
2343 
2344 
2345    final void drawRectangle(Color c, int x, int y, int width, int height) {
2346 
2347    }
2348    +/
2349 
2350       final void drawRectangles(Pen pen, Rect[] rs) {
2351          HPEN prevPen;
2352          HBRUSH prevBrush;
2353 
2354          prevPen = SelectObject(hdc, pen.handle);
2355          prevBrush = SelectObject(hdc, cast(HBRUSH) GetStockObject(NULL_BRUSH)); // Don't fill it in.
2356 
2357          foreach (ref Rect r; rs) {
2358             Rectangle(hdc, r.x, r.y, r.x + r.width, r.y + r.height);
2359          }
2360 
2361          // Reset stuff.
2362          SelectObject(hdc, prevPen);
2363          SelectObject(hdc, prevBrush);
2364       }
2365 
2366       // Force pending graphics operations.
2367       final void flush() {
2368          GdiFlush();
2369       }
2370 
2371       final Color getNearestColor(Color c) {
2372          COLORREF cref;
2373          cref = GetNearestColor(handle, c.toRgb());
2374          if (CLR_INVALID == cref) {
2375             return Color.empty;
2376          }
2377          return Color.fromRgb(c.a, cref); // Preserve alpha.
2378       }
2379 
2380       final Size getScaleSize(Font f) {
2381          // http://support.microsoft.com/kb/125681
2382          Size result;
2383          version (DIALOG_BOX_SCALE) {
2384             enum SAMPLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2385             result = measureText(SAMPLE, f);
2386             result.width = (result.width / (SAMPLE.length / 2) + 1) / 2;
2387             TEXTMETRICA tma;
2388             if (GetTextMetricsA(handle, &tma)) {
2389                result.height = tma.tmHeight;
2390             }
2391          } else {
2392             enum SAMPLE = "Abcdefghijklmnopqrstuvwxyz";
2393             result = measureText(SAMPLE, f);
2394             result.width /= SAMPLE.length;
2395          }
2396          return result;
2397       }
2398 
2399       final bool copyTo(HDC dest, int destX, int destY, int width, int height,
2400          int srcX = 0, int srcY = 0, DWORD rop = SRCCOPY) { // package
2401          return cast(bool) BitBlt(dest, destX, destY,
2402             width, height, this.handle, srcX, srcY, rop);
2403       }
2404 
2405       final bool copyTo(Graphics destGraphics, int destX, int destY, int width,
2406          int height, int srcX = 0, int srcY = 0, DWORD rop = SRCCOPY) {
2407          return copyTo(destGraphics.handle, destX, destY, width, height, srcX, srcY,
2408             rop);
2409       }
2410 
2411       final bool copyTo(Graphics destGraphics, Rect bounds) {
2412          return copyTo(destGraphics.handle, bounds.x, bounds.y, bounds.width, bounds.height);
2413       }
2414 
2415       final @property HDC handle() {
2416          return hdc;
2417       }
2418 
2419       void dispose() {
2420          assert(owned);
2421          DeleteDC(hdc);
2422          hdc = null;
2423       }
2424 
2425    private:
2426       HDC hdc;
2427       bool owned = true;
2428    }
2429 
2430    /// Graphics for a surface in memory.
2431    class MemoryGraphics : Graphics {
2432 
2433       // Graphics compatible with the current screen.
2434       this(int width, int height) {
2435          HDC hdc;
2436          hdc = GetWindowDC(null);
2437          scope (exit)
2438             ReleaseDC(null, hdc);
2439          this(width, height, hdc);
2440       }
2441 
2442       // graphicsCompatible cannot be another MemoryGraphics.
2443       this(int width, int height, Graphics graphicsCompatible) {
2444          if (cast(MemoryGraphics) graphicsCompatible) {
2445             //throw new DflException("Graphics cannot be compatible with memory");
2446             assert(0, "Graphics cannot be compatible with memory");
2447          }
2448          this(width, height, graphicsCompatible.handle);
2449       }
2450 
2451       // Used internally.
2452       this(int width, int height, HDC hdcCompatible) { // package
2453          _w = width;
2454          _h = height;
2455 
2456          hbm = CreateCompatibleBitmap(hdcCompatible, width, height);
2457          if (!hbm) {
2458             throw new DflException("Unable to allocate Graphics memory");
2459          }
2460          scope (failure) {
2461             DeleteObject(hbm);
2462             //hbm = HBITMAP.init;
2463          }
2464 
2465          HDC hdcc;
2466          hdcc = CreateCompatibleDC(hdcCompatible);
2467          if (!hdcc) {
2468             throw new DflException("Unable to allocate Graphics");
2469          }
2470          scope (failure)
2471             DeleteDC(hdcc);
2472 
2473          hbmOld = SelectObject(hdcc, hbm);
2474          scope (failure)
2475             SelectObject(hdcc, hbmOld);
2476 
2477          super(hdcc);
2478       }
2479 
2480       final @property int width() {
2481          return _w;
2482       }
2483 
2484       final @property int height() {
2485          return _h;
2486       }
2487 
2488       final Size size() {
2489          return Size(_w, _h);
2490       }
2491 
2492       final @property HBITMAP hbitmap() { // getter // package
2493          return hbm;
2494       }
2495 
2496       // Needs to copy so it can be selected into other DC`s.
2497       final HBITMAP toHBitmap(HDC hdc) { // package
2498          HDC memdc;
2499          HBITMAP result;
2500          HGDIOBJ oldbm;
2501          memdc = CreateCompatibleDC(hdc);
2502          if (!memdc) {
2503             throw new DflException("Device error");
2504          }
2505          try {
2506             result = CreateCompatibleBitmap(hdc, width, height);
2507             if (!result) {
2508             bad_bm:
2509                throw new DflException("Unable to allocate image");
2510             }
2511             oldbm = SelectObject(memdc, result);
2512             copyTo(memdc, 0, 0, width, height);
2513          }
2514          finally {
2515             if (oldbm) {
2516                SelectObject(memdc, oldbm);
2517             }
2518             DeleteDC(memdc);
2519          }
2520          return result;
2521       }
2522 
2523       final Bitmap toBitmap(HDC hdc) { // package
2524          HBITMAP hbm;
2525          hbm = toHBitmap(hdc);
2526          if (!hbm) {
2527             throw new DflException("Unable to create bitmap");
2528          }
2529          return new Bitmap(hbm, true); // Owned.
2530       }
2531 
2532       final Bitmap toBitmap() {
2533          Graphics g;
2534          Bitmap result;
2535          g = Graphics.getScreen();
2536          result = toBitmap(g);
2537          g.dispose();
2538          return result;
2539       }
2540 
2541       final Bitmap toBitmap(Graphics g) {
2542          return toBitmap(g.handle);
2543       }
2544 
2545       override void dispose() {
2546          SelectObject(hdc, hbmOld);
2547          hbmOld = HGDIOBJ.init;
2548          DeleteObject(hbm);
2549          hbm = HBITMAP.init;
2550          super.dispose();
2551       }
2552 
2553    private:
2554       HGDIOBJ hbmOld;
2555       HBITMAP hbm;
2556       int _w, _h;
2557    }
2558 
2559    final class EmfGraphics : Graphics {
2560    private:
2561       HDC _hdc;
2562       Rect _area;
2563    public:
2564 
2565       this(Graphics refGraphics = null, Rect area = Rect.init,
2566          string filename = null, string description = null) {
2567          _area = area;
2568          RECT rc;
2569          RECT* pRc;
2570          HDC hdcref;
2571          if (refGraphics) {
2572             hdcref = refGraphics.handle;
2573          } else {
2574             hdcref = GetDC(null);
2575          }
2576          scope (exit) {
2577             if (!refGraphics) {
2578                ReleaseDC(null, hdcref);
2579             }
2580          }
2581          if (area != Rect.init) {
2582             auto tmphdc = CreateEnhMetaFileW(hdcref, null, null, null);
2583             auto tmpemf = CloseEnhMetaFile(tmphdc);
2584             ENHMETAHEADER tmphdr;
2585             GetEnhMetaFileHeader(tmpemf, ENHMETAHEADER.sizeof, &tmphdr);
2586             DeleteEnhMetaFile(tmpemf);
2587             rc.left = MulDiv(_area.x, GetDeviceCaps(hdcref, HORZSIZE) * 100, tmphdr.szlDevice.cx);
2588             rc.top = MulDiv(_area.y, GetDeviceCaps(hdcref, VERTSIZE) * 100, tmphdr.szlDevice.cy);
2589             rc.right = MulDiv(_area.right, GetDeviceCaps(hdcref, HORZSIZE) * 100,
2590                tmphdr.szlDevice.cx);
2591             rc.bottom = MulDiv(_area.bottom, GetDeviceCaps(hdcref,
2592                VERTSIZE) * 100, tmphdr.szlDevice.cy);
2593             pRc = &rc;
2594          }
2595          import std.utf;
2596 
2597          _hdc = CreateEnhMetaFileW(hdcref,
2598             filename.length ? filename.toUTF16z() : null, pRc,
2599             description.length ? description.toUTF16z() : null);
2600          super(_hdc, false);
2601       }
2602 
2603       this(Rect area, string filename = null, string description = null) {
2604          this(null, area, filename, description);
2605       }
2606 
2607       this(uint width, uint height, string filename = null, string description = null) {
2608          this(null, Rect(0, 0, width, height), filename, description);
2609       }
2610 
2611       override void dispose() {
2612          super.dispose();
2613          if (_hdc) {
2614             DeleteEnhMetaFile(CloseEnhMetaFile(_hdc));
2615          }
2616       }
2617 
2618       Size size() const pure nothrow @property {
2619          return _area.size;
2620       }
2621 
2622       uint width() const pure nothrow @property {
2623          return _area.width;
2624       }
2625 
2626       uint height() const pure nothrow @property {
2627          return _area.height;
2628       }
2629 
2630       Rect frameRectangle() const pure nothrow @property {
2631          return _area;
2632       }
2633 
2634       EnhancedMetaFile toEnhancedMetaFile() {
2635          return new EnhancedMetaFile(CloseEnhMetaFile(_hdc));
2636       }
2637    }
2638 
2639    // Use with GetDC()/GetWindowDC()/GetDCEx() so that
2640    // the HDC is properly released instead of deleted.
2641    package class CommonGraphics : Graphics {
2642       // Used internally.
2643       this(HWND hwnd, HDC hdc, bool owned = true) {
2644          super(hdc, owned);
2645          this.hwnd = hwnd;
2646       }
2647 
2648       override void dispose() {
2649          ReleaseDC(hwnd, hdc);
2650          hdc = null;
2651       }
2652 
2653    package:
2654       HWND hwnd;
2655    }
2656 
2657    class Icon : Image {
2658       // Used internally.
2659       this(HICON hi, bool owned = true) {
2660          this.hi = hi;
2661          this.owned = owned;
2662       }
2663 
2664       // Load from an ico file.
2665       this(Dstring fileName) {
2666          this.hi = cast(HICON) dfl.internal.utf.loadImage(null, fileName,
2667             IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
2668          if (!this.hi) {
2669             throw new DflException("Unable to load icon from file '" ~ fileName ~ "'");
2670          }
2671       }
2672 
2673       deprecated static Icon fromHandle(HICON hi) {
2674          return new Icon(hi, false); // Not owned. Up to caller to manage or call dispose().
2675       }
2676 
2677       // -bm- can be null.
2678       // NOTE: the bitmaps in -ii- need to be deleted! _deleteBitmaps() is a shortcut.
2679       private void _getInfo(ICONINFO* ii, BITMAP* bm = null) {
2680          if (GetIconInfo(hi, ii)) {
2681             if (!bm) {
2682                return;
2683             }
2684 
2685             HBITMAP hbm;
2686             if (ii.hbmColor) {
2687                hbm = ii.hbmColor;
2688             } else { // Monochrome.
2689                hbm = ii.hbmMask;
2690             }
2691             if (GetObjectA(hbm, BITMAP.sizeof, bm) == BITMAP.sizeof) {
2692                return;
2693             }
2694          }
2695 
2696          // Fell through, failed.
2697          throw new DflException("Unable to get image information");
2698       }
2699 
2700       private void _deleteBitmaps(ICONINFO* ii) {
2701          DeleteObject(ii.hbmColor);
2702          ii.hbmColor = null;
2703          DeleteObject(ii.hbmMask);
2704          ii.hbmMask = null;
2705       }
2706 
2707       final Bitmap toBitmap() {
2708          ICONINFO ii;
2709          BITMAP bm;
2710          _getInfo(&ii, &bm);
2711          // Not calling _deleteBitmaps() because I'm keeping one.
2712 
2713          HBITMAP hbm;
2714          if (ii.hbmColor) {
2715             hbm = ii.hbmColor;
2716             DeleteObject(ii.hbmMask);
2717          } else { // Monochrome.
2718             hbm = ii.hbmMask;
2719          }
2720 
2721          return new Bitmap(hbm, true); // Yes owned.
2722       }
2723 
2724       final override void draw(Graphics g, Point pt) {
2725          g.drawIcon(this, pt.x, pt.y);
2726       }
2727 
2728       final override void drawStretched(Graphics g, Rect r) {
2729          g.drawIcon(this, r);
2730       }
2731 
2732       final override @property Size size() {
2733          ICONINFO ii;
2734          BITMAP bm;
2735          _getInfo(&ii, &bm);
2736          _deleteBitmaps(&ii);
2737          return Size(bm.bmWidth, bm.bmHeight);
2738       }
2739 
2740       final override @property int width() {
2741          return size.width;
2742       }
2743 
2744       final override @property int height() {
2745          return size.height;
2746       }
2747 
2748       ~this() {
2749          if (owned) {
2750             dispose();
2751          }
2752       }
2753 
2754       override int _imgtype(HGDIOBJ* ph) { // internal
2755          if (ph) {
2756             *ph = cast(HGDIOBJ) hi;
2757          }
2758          return 2;
2759       }
2760 
2761       void dispose() {
2762          assert(owned);
2763          DestroyIcon(hi);
2764          hi = null;
2765       }
2766 
2767       final @property HICON handle() {
2768          return hi;
2769       }
2770 
2771    private:
2772       HICON hi;
2773       bool owned = true;
2774    }
2775 
2776    enum GraphicsUnit : ubyte { // docmain ?
2777 
2778       PIXEL,
2779 
2780       DISPLAY, // 1/75 inch.
2781 
2782       DOCUMENT, // 1/300 inch.
2783 
2784       INCH, // 1 inch, der.
2785 
2786       MILLIMETER, // 25.4 millimeters in 1 inch.
2787 
2788       POINT, // 1/72 inch.
2789       //WORLD, // ?
2790       TWIP, // Extra. 1/1440 inch.
2791    }
2792 
2793    /+
2794 // TODO: check if correct implementation.
2795 enum GenericFontFamilies {
2796    MONOSPACE = FF_MODERN,
2797    SANS_SERIF = FF_ROMAN,
2798    SERIF = FF_SWISS,
2799 }
2800 +/
2801 
2802    /+
2803 abstract class FontCollection {
2804    abstract @property FontFamily[] families();
2805 }
2806 
2807 
2808 class FontFamily {
2809    /+
2810       this(GenericFontFamilies genericFamily) {
2811 
2812       }
2813    +/
2814 
2815 
2816       this(Dstring name) {
2817 
2818       }
2819 
2820 
2821    this(Dstring name, FontCollection fontCollection) {
2822 
2823    }
2824 
2825 
2826    final @property Dstring name() {
2827 
2828    }
2829 
2830 
2831    static @property FontFamily[] families() {
2832 
2833    }
2834 
2835 
2836    /+
2837       // TODO: implement.
2838 
2839       static @property FontFamily genericMonospace() {
2840 
2841       }
2842 
2843 
2844    static @property FontFamily genericSansSerif() {
2845 
2846    }
2847 
2848 
2849    static @property FontFamily genericSerif() {
2850 
2851    }
2852    +/
2853 }
2854 +/
2855 
2856    // Flags.
2857    enum FontStyle : ubyte {
2858       REGULAR = 0, ///
2859       BOLD = 1,
2860       ITALIC = 2,
2861       UNDERLINE = 4,
2862       STRIKEOUT = 8,
2863    }
2864 
2865    enum FontSmoothing {
2866       DEFAULT = DEFAULT_QUALITY,
2867       ON = ANTIALIASED_QUALITY,
2868       OFF = NONANTIALIASED_QUALITY,
2869    }
2870 
2871    class Font {
2872       // Used internally.
2873       static void LOGFONTAtoLogFont(ref LogFont lf, LOGFONTA* plfa) { // package // deprecated
2874          lf.lfa = *plfa;
2875          lf.faceName = dfl.internal.utf.fromAnsiz(plfa.lfFaceName.ptr);
2876       }
2877 
2878       // Used internally.
2879       static void LOGFONTWtoLogFont(ref LogFont lf, LOGFONTW* plfw) { // package // deprecated
2880          lf.lfw = *plfw;
2881          lf.faceName = dfl.internal.utf.fromUnicodez(plfw.lfFaceName.ptr);
2882       }
2883 
2884       // Used internally.
2885       this(HFONT hf, LOGFONTA* plfa, bool owned = true) { // package // deprecated
2886          LogFont lf;
2887          LOGFONTAtoLogFont(lf, plfa);
2888 
2889          this.hf = hf;
2890          this.owned = owned;
2891          this._unit = GraphicsUnit.POINT;
2892 
2893          _fstyle = _style(lf);
2894          _initLf(lf);
2895       }
2896 
2897       // Used internally.
2898       this(HFONT hf, ref LogFont lf, bool owned = true) { // package
2899          this.hf = hf;
2900          this.owned = owned;
2901          this._unit = GraphicsUnit.POINT;
2902 
2903          _fstyle = _style(lf);
2904          _initLf(lf);
2905       }
2906 
2907       // Used internally.
2908       this(HFONT hf, bool owned = true) { // package
2909          this.hf = hf;
2910          this.owned = owned;
2911          this._unit = GraphicsUnit.POINT;
2912 
2913          LogFont lf;
2914          _info(lf);
2915 
2916          _fstyle = _style(lf);
2917          _initLf(lf);
2918       }
2919 
2920       // Used internally.
2921       this(LOGFONTA* plfa, bool owned = true) { // package // deprecated
2922          LogFont lf;
2923          LOGFONTAtoLogFont(lf, plfa);
2924 
2925          this(_create(lf), lf, owned);
2926       }
2927 
2928       // Used internally.
2929       this(ref LogFont lf, bool owned = true) { // package
2930          this(_create(lf), lf, owned);
2931       }
2932 
2933       package static HFONT _create(ref LogFont lf) {
2934          HFONT result;
2935          result = dfl.internal.utf.createFontIndirect(lf);
2936          if (!result) {
2937             throw new DflException("Unable to create font");
2938          }
2939          return result;
2940       }
2941 
2942       private static void _style(ref LogFont lf, FontStyle style) {
2943          lf.lf.lfWeight = (style & FontStyle.BOLD) ? FW_BOLD : FW_NORMAL;
2944          lf.lf.lfItalic = (style & FontStyle.ITALIC) ? TRUE : FALSE;
2945          lf.lf.lfUnderline = (style & FontStyle.UNDERLINE) ? TRUE : FALSE;
2946          lf.lf.lfStrikeOut = (style & FontStyle.STRIKEOUT) ? TRUE : FALSE;
2947       }
2948 
2949       private static FontStyle _style(ref LogFont lf) {
2950          FontStyle style = FontStyle.REGULAR;
2951 
2952          if (lf.lf.lfWeight >= FW_BOLD) {
2953             style |= FontStyle.BOLD;
2954          }
2955          if (lf.lf.lfItalic) {
2956             style |= FontStyle.ITALIC;
2957          }
2958          if (lf.lf.lfUnderline) {
2959             style |= FontStyle.UNDERLINE;
2960          }
2961          if (lf.lf.lfStrikeOut) {
2962             style |= FontStyle.STRIKEOUT;
2963          }
2964 
2965          return style;
2966       }
2967 
2968       package void _info(LOGFONTA* lf) { // deprecated
2969          if (GetObjectA(hf, LOGFONTA.sizeof, lf) != LOGFONTA.sizeof) {
2970             throw new DflException("Unable to get font information");
2971          }
2972       }
2973 
2974       package void _info(LOGFONTW* lf) { // deprecated
2975          auto proc = cast(GetObjectWProc) GetProcAddress(GetModuleHandleA("gdi32.dll"),
2976             "GetObjectW");
2977 
2978          if (!proc || proc(hf, LOGFONTW.sizeof, lf) != LOGFONTW.sizeof) {
2979             throw new DflException("Unable to get font information");
2980          }
2981       }
2982 
2983       package void _info(ref LogFont lf) {
2984          if (!dfl.internal.utf.getLogFont(hf, lf)) {
2985             throw new DflException("Unable to get font information");
2986          }
2987       }
2988 
2989       package static LONG getLfHeight(float emSize, GraphicsUnit unit) {
2990          LONG result;
2991          HDC hdc;
2992 
2993          final switch (unit) {
2994          case GraphicsUnit.PIXEL:
2995             result = cast(LONG) emSize;
2996             break;
2997 
2998          case GraphicsUnit.POINT:
2999             hdc = GetWindowDC(null);
3000             result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY),
3001                72 * 100);
3002             ReleaseDC(null, hdc);
3003             break;
3004 
3005          case GraphicsUnit.DISPLAY:
3006             hdc = GetWindowDC(null);
3007             result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY),
3008                75 * 100);
3009             ReleaseDC(null, hdc);
3010             break;
3011 
3012          case GraphicsUnit.MILLIMETER:
3013             hdc = GetWindowDC(null);
3014             result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY),
3015                2540);
3016             ReleaseDC(null, hdc);
3017             break;
3018 
3019          case GraphicsUnit.INCH:
3020             hdc = GetWindowDC(null);
3021             result = cast(LONG)(emSize * cast(float) GetDeviceCaps(hdc, LOGPIXELSY));
3022             ReleaseDC(null, hdc);
3023             break;
3024 
3025          case GraphicsUnit.DOCUMENT:
3026             hdc = GetWindowDC(null);
3027             result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY),
3028                300 * 100);
3029             ReleaseDC(null, hdc);
3030             break;
3031 
3032          case GraphicsUnit.TWIP:
3033             hdc = GetWindowDC(null);
3034             result = MulDiv(cast(int)(emSize * 100), GetDeviceCaps(hdc, LOGPIXELSY),
3035                1440 * 100);
3036             ReleaseDC(null, hdc);
3037             break;
3038          }
3039 
3040          return result;
3041       }
3042 
3043       package static float getEmSize(HDC hdc, LONG lfHeight, GraphicsUnit toUnit) {
3044          float result;
3045 
3046          if (lfHeight < 0) {
3047             lfHeight = -lfHeight;
3048          }
3049 
3050          final switch (toUnit) {
3051          case GraphicsUnit.PIXEL:
3052             result = cast(float) lfHeight;
3053             break;
3054 
3055          case GraphicsUnit.POINT:
3056             result = cast(float) MulDiv(lfHeight, 72,
3057                GetDeviceCaps(hdc, LOGPIXELSY));
3058             break;
3059 
3060          case GraphicsUnit.DISPLAY:
3061             result = cast(float) MulDiv(lfHeight, 75,
3062                GetDeviceCaps(hdc, LOGPIXELSY));
3063             break;
3064 
3065          case GraphicsUnit.MILLIMETER:
3066             result = cast(float) MulDiv(lfHeight, 254,
3067                GetDeviceCaps(hdc, LOGPIXELSY)) / 10.0;
3068             break;
3069 
3070          case GraphicsUnit.INCH:
3071             result = cast(float) lfHeight / cast(float) GetDeviceCaps(hdc,
3072                LOGPIXELSY);
3073             break;
3074 
3075          case GraphicsUnit.DOCUMENT:
3076             result = cast(float) MulDiv(lfHeight, 300,
3077                GetDeviceCaps(hdc, LOGPIXELSY));
3078             break;
3079 
3080          case GraphicsUnit.TWIP:
3081             result = cast(float) MulDiv(lfHeight, 1440,
3082                GetDeviceCaps(hdc, LOGPIXELSY));
3083             break;
3084          }
3085 
3086          return result;
3087       }
3088 
3089       package static float getEmSize(LONG lfHeight, GraphicsUnit toUnit) {
3090          if (GraphicsUnit.PIXEL == toUnit) {
3091             if (lfHeight < 0) {
3092                return cast(float)-lfHeight;
3093             }
3094             return cast(float) lfHeight;
3095          }
3096          HDC hdc;
3097          hdc = GetWindowDC(null);
3098          float result = getEmSize(hdc, lfHeight, toUnit);
3099          ReleaseDC(null, hdc);
3100          return result;
3101       }
3102 
3103       this(Font font, FontStyle style) {
3104          LogFont lf;
3105          _unit = font._unit;
3106          font._info(lf);
3107          _style(lf, style);
3108          this(_create(lf));
3109 
3110          _fstyle = style;
3111          _initLf(font, lf);
3112       }
3113 
3114       this(Dstring name, float emSize, GraphicsUnit unit) {
3115          this(name, emSize, FontStyle.REGULAR, unit);
3116       }
3117 
3118       this(Dstring name, float emSize, FontStyle style = FontStyle.REGULAR,
3119          GraphicsUnit unit = GraphicsUnit.POINT) {
3120          this(name, emSize, style, unit, DEFAULT_CHARSET, FontSmoothing.DEFAULT);
3121       }
3122 
3123       this(Dstring name, float emSize, FontStyle style, GraphicsUnit unit, FontSmoothing smoothing) {
3124          this(name, emSize, style, unit, DEFAULT_CHARSET, smoothing);
3125       }
3126 
3127       //
3128       // This is a somewhat internal function.
3129       // -gdiCharSet- is one of *_CHARSET from wingdi.h
3130       this(Dstring name, float emSize, FontStyle style, GraphicsUnit unit,
3131          ubyte gdiCharSet, FontSmoothing smoothing = FontSmoothing.DEFAULT) {
3132          LogFont lf;
3133 
3134          lf.faceName = name;
3135          lf.lf.lfCharSet = gdiCharSet;
3136          lf.lf.lfQuality = cast(BYTE) smoothing;
3137          lf.lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
3138          lf.lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
3139          lf.lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
3140 
3141          this(lf, emSize, style, unit);
3142       }
3143 
3144       //
3145       // This is a somewhat internal function.
3146       this(ref LogFont lf, float emSize, FontStyle style, GraphicsUnit unit) {
3147          _unit = unit;
3148 
3149          lf.lf.lfHeight = -getLfHeight(emSize, unit);
3150          _style(lf, style);
3151 
3152          this(_create(lf));
3153 
3154          _fstyle = style;
3155          _initLf(lf);
3156       }
3157 
3158       ~this() {
3159          if (owned) {
3160             DeleteObject(hf);
3161          }
3162       }
3163 
3164       final @property HFONT handle() {
3165          return hf;
3166       }
3167 
3168       final @property GraphicsUnit unit() {
3169          return _unit;
3170       }
3171 
3172       final @property float size() {
3173          /+
3174          LOGFONTA lf;
3175       _info(&lf);
3176       return getEmSize(lf.lf.lfHeight, _unit);
3177       +/
3178          return getEmSize(this.lfHeight, _unit);
3179       }
3180 
3181       final float getSize(GraphicsUnit unit) {
3182          /+
3183          LOGFONTA lf;
3184       _info(&lf);
3185       return getEmSize(lf.lf.lfHeight, unit);
3186       +/
3187          return getEmSize(this.lfHeight, unit);
3188       }
3189 
3190       final float getSize(GraphicsUnit unit, Graphics g) {
3191          return getEmSize(g.handle, this.lfHeight, unit);
3192       }
3193 
3194       final @property FontStyle style() {
3195          return _fstyle;
3196       }
3197 
3198       final @property Dstring name() {
3199          return lfName;
3200       }
3201 
3202       final @property ubyte gdiCharSet() {
3203          return lfCharSet;
3204       }
3205 
3206       /+
3207       private void _initLf(LOGFONTA* lf) {
3208          this.lfHeight = lf.lfHeight;
3209          this.lfName = stringFromStringz(lf.lfFaceName.ptr).dup;
3210          this.lfCharSet = lf.lfCharSet;
3211       }
3212    +/
3213 
3214       private void _initLf(ref LogFont lf) {
3215          this.lfHeight = lf.lf.lfHeight;
3216          this.lfName = lf.faceName;
3217          this.lfCharSet = lf.lf.lfCharSet;
3218       }
3219 
3220       /+
3221       private void _initLf(Font otherfont, LOGFONTA* lf) {
3222          this.lfHeight = otherfont.lfHeight;
3223          this.lfName = otherfont.lfName;
3224          this.lfCharSet = otherfont.lfCharSet;
3225       }
3226    +/
3227 
3228       private void _initLf(Font otherfont, ref LogFont lf) {
3229          this.lfHeight = otherfont.lfHeight;
3230          this.lfName = otherfont.lfName;
3231          this.lfCharSet = otherfont.lfCharSet;
3232       }
3233 
3234    private:
3235       HFONT hf;
3236       GraphicsUnit _unit;
3237       bool owned = true;
3238       FontStyle _fstyle;
3239 
3240       LONG lfHeight;
3241       Dstring lfName;
3242       ubyte lfCharSet;
3243    }
3244 
3245    enum PenStyle : UINT {
3246       SOLID = PS_SOLID, ///
3247       DASH = PS_DASH,
3248       DOT = PS_DOT,
3249       DASH_DOT = PS_DASHDOT,
3250       DASH_DOT_DOT = PS_DASHDOTDOT,
3251       NULL = PS_NULL,
3252       INSIDE_FRAME = PS_INSIDEFRAME,
3253    }
3254 
3255    // If the pen width is greater than 1 the style cannot have dashes or dots.
3256    class Pen {
3257       // Used internally.
3258       this(HPEN hp, bool owned = true) {
3259          this.hp = hp;
3260          this.owned = owned;
3261       }
3262 
3263       this(Color color, int width = 1, PenStyle ps = PenStyle.SOLID) {
3264          hp = CreatePen(ps, width, color.toRgb());
3265       }
3266 
3267       ~this() {
3268          if (owned) {
3269             DeleteObject(hp);
3270          }
3271       }
3272 
3273       final @property HPEN handle() {
3274          return hp;
3275       }
3276 
3277    private:
3278       HPEN hp;
3279       bool owned = true;
3280    }
3281 
3282    class Brush {
3283       // Used internally.
3284       this(HBRUSH hb, bool owned = true) {
3285          this.hb = hb;
3286          this.owned = owned;
3287       }
3288 
3289       protected this() {
3290       }
3291 
3292       ~this() {
3293          if (owned) {
3294             DeleteObject(hb);
3295          }
3296       }
3297 
3298       final @property HBRUSH handle() {
3299          return hb;
3300       }
3301 
3302    private:
3303       HBRUSH hb;
3304       bool owned = true;
3305    }
3306 
3307    class SolidBrush : Brush {
3308 
3309       this(Color c) {
3310          super(CreateSolidBrush(c.toRgb()));
3311       }
3312 
3313       /+
3314       final @property void color(Color c) {
3315          // delete..
3316          super.hb = CreateSolidBrush(c.toRgb());
3317       }
3318    +/
3319 
3320       final @property Color color() {
3321          Color result;
3322          LOGBRUSH lb;
3323 
3324          if (GetObjectA(hb, lb.sizeof, &lb)) {
3325             result = Color.fromRgb(lb.lbColor);
3326          }
3327 
3328          return result;
3329       }
3330    }
3331 
3332    // PatternBrush has the win9x/ME limitation of not supporting images larger than 8x8 pixels.
3333    // TextureBrush supports any size images but requires GDI+.
3334 
3335    /+
3336 class PatternBrush: Brush {
3337    //CreatePatternBrush() ...
3338 }
3339 +/
3340 
3341    /+
3342 class TextureBrush: Brush {
3343    // GDI+ ...
3344 }
3345 +/
3346 
3347    enum HatchStyle : LONG {
3348       HORIZONTAL = HS_HORIZONTAL, ///
3349       VERTICAL = HS_VERTICAL,
3350       FORWARD_DIAGONAL = HS_FDIAGONAL,
3351       BACKWARD_DIAGONAL = HS_BDIAGONAL,
3352       CROSS = HS_CROSS,
3353       DIAGONAL_CROSS = HS_DIAGCROSS,
3354    }
3355 
3356    class HatchBrush : Brush {
3357 
3358       this(HatchStyle hs, Color c) {
3359          super(CreateHatchBrush(hs, c.toRgb()));
3360       }
3361 
3362       final @property Color foregroundColor() {
3363          Color result;
3364          LOGBRUSH lb;
3365 
3366          if (GetObjectA(hb, lb.sizeof, &lb)) {
3367             result = Color.fromRgb(lb.lbColor);
3368          }
3369 
3370          return result;
3371       }
3372 
3373       final @property HatchStyle hatchStyle() {
3374          HatchStyle result;
3375          LOGBRUSH lb;
3376 
3377          if (GetObjectA(hb, lb.sizeof, &lb)) {
3378             result = cast(HatchStyle) lb.lbHatch;
3379          }
3380 
3381          return result;
3382       }
3383    }
3384 
3385    class Region {
3386       // Used internally.
3387       this(HRGN hrgn, bool owned = true) {
3388          this.hrgn = hrgn;
3389          this.owned = owned;
3390       }
3391 
3392       ~this() {
3393          if (owned) {
3394             DeleteObject(hrgn);
3395          }
3396       }
3397 
3398       final @property HRGN handle() {
3399          return hrgn;
3400       }
3401 
3402       override Dequ opEquals(Object o) {
3403          Region rgn = cast(Region) o;
3404          if (!rgn) {
3405             return 0; // Not equal.
3406          }
3407          return opEquals(rgn);
3408       }
3409 
3410       Dequ opEquals(Region rgn) {
3411          return hrgn == rgn.hrgn;
3412       }
3413 
3414       private HRGN hrgn;
3415       private bool owned = true;
3416    }
3417 
3418    // from wincom
3419    private LONG MAP_LOGHIM_TO_PIX(LONG x, LONG logpixels) {
3420       return MulDiv(logpixels, x, 2540);
3421    }