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