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