1 /** 2 * Utility Windows GDI code. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.graphics.gdi; 15 16 version(Windows): 17 18 import std.exception; 19 import std.typecons; 20 21 import ae.sys.windows.imports; 22 mixin(importWin32!(q{wingdi}, q{public})); 23 mixin(importWin32!q{winuser}); 24 mixin(importWin32!q{windef}); 25 26 import ae.utils.graphics.color; 27 import ae.utils.graphics.draw; 28 import ae.utils.graphics.view; 29 30 pragma(lib, "gdi32"); 31 32 /// A canvas with added GDI functionality. 33 struct GDICanvas(COLOR) 34 { 35 struct Data 36 { 37 HDC hdc; 38 HBITMAP hbm; 39 40 @disable this(this); 41 42 ~this() nothrow @nogc 43 { 44 alias DeleteDC_t = extern(Windows) void function(HDC ) nothrow @nogc; 45 alias DeleteObject_t = extern(Windows) void function(HBITMAP) nothrow @nogc; 46 auto pDeleteDC = cast(DeleteDC_t )&DeleteDC; 47 auto pDeleteObject = cast(DeleteObject_t)&DeleteObject; 48 pDeleteDC(hdc); hdc = null; 49 pDeleteObject(hbm); hbm = null; 50 } 51 } 52 53 RefCounted!Data data; 54 55 int w, h; 56 COLOR* pixels; 57 58 inout(COLOR)[] scanline(int y) inout 59 { 60 assert(y>=0 && y<h); 61 return pixels[w*y..w*(y+1)]; 62 } 63 64 mixin DirectView; 65 66 this(uint w, uint h) 67 { 68 this.w = w; 69 this.h = h; 70 71 auto hddc = GetDC(null); 72 scope(exit) ReleaseDC(null, hddc); 73 data.hdc = CreateCompatibleDC(hddc); 74 75 BITMAPINFO bmi; 76 bmi.bmiHeader.biSize = bmi.bmiHeader.sizeof; 77 bmi.bmiHeader.biWidth = w; 78 bmi.bmiHeader.biHeight = -h; 79 bmi.bmiHeader.biPlanes = 1; 80 bmi.bmiHeader.biBitCount = COLOR.sizeof * 8; 81 bmi.bmiHeader.biCompression = BI_RGB; 82 void* pvBits; 83 data.hbm = CreateDIBSection(data.hdc, &bmi, DIB_RGB_COLORS, &pvBits, null, 0); 84 enforce(data.hbm, "CreateDIBSection"); 85 SelectObject(data.hdc, data.hbm); 86 pixels = cast(COLOR*)pvBits; 87 } 88 89 auto opDispatch(string F, A...)(A args) 90 if (is(typeof(mixin(F~"(data.hdc, args)")))) 91 { 92 mixin("return "~F~"(data.hdc, args);"); 93 } 94 } 95 96 unittest 97 { 98 alias RGB = ae.utils.graphics.color.RGB; 99 100 // alias BGR COLOR; 101 alias BGRX COLOR; 102 auto b = GDICanvas!COLOR(100, 100); 103 b.fill(COLOR(255, 255, 255)); 104 105 const str = "Hello, world!"; 106 auto f = CreateFont(-11, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Tahoma"); scope(exit) DeleteObject(f); 107 b.SelectObject(f); 108 b.SetBkColor(0xFFFFFF); 109 b.SetTextColor(0x0000FF); 110 b.TextOutA(10, 10, str.ptr, cast(uint)str.length); 111 112 b.SetPixel(5, 5, 0xFF0000); 113 GdiFlush(); 114 b[6, 6] = COLOR(255, 0, 0); 115 116 import ae.utils.graphics.image; 117 auto i = b.copy.colorMap!(c => RGB(c.r,c.g,c.b))(); 118 assert(i[5, 5] == RGB(0, 0, 255)); 119 assert(i[6, 6] == RGB(0, 0, 255)); 120 121 // i.savePNG("gditest.png"); 122 // i.savePNM("gditest.pnm"); 123 }