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 public import win32.wingdi; 22 import win32.winuser; 23 import win32.windef; 24 25 import ae.utils.graphics.color; 26 import ae.utils.graphics.draw; 27 import ae.utils.graphics.view; 28 29 /// A canvas with added GDI functionality. 30 struct GDICanvas(COLOR) 31 { 32 struct Data 33 { 34 HDC hdc; 35 HBITMAP hbm; 36 37 @disable this(this); 38 39 ~this() 40 { 41 DeleteDC(hdc); hdc = null; 42 DeleteObject(hbm); hbm = null; 43 } 44 } 45 46 RefCounted!Data data; 47 48 int w, h; 49 COLOR* pixels; 50 51 COLOR[] scanline(int y) 52 { 53 assert(y>=0 && y<h); 54 return pixels[w*y..w*(y+1)]; 55 } 56 57 mixin DirectView; 58 59 this(uint w, uint h) 60 { 61 this.w = w; 62 this.h = h; 63 64 auto hddc = GetDC(null); 65 scope(exit) ReleaseDC(null, hddc); 66 data.hdc = CreateCompatibleDC(hddc); 67 68 BITMAPINFO bmi; 69 bmi.bmiHeader.biSize = bmi.bmiHeader.sizeof; 70 bmi.bmiHeader.biWidth = w; 71 bmi.bmiHeader.biHeight = -h; 72 bmi.bmiHeader.biPlanes = 1; 73 bmi.bmiHeader.biBitCount = COLOR.sizeof * 8; 74 bmi.bmiHeader.biCompression = BI_RGB; 75 void* pvBits; 76 data.hbm = CreateDIBSection(data.hdc, &bmi, DIB_RGB_COLORS, &pvBits, null, 0); 77 enforce(data.hbm, "CreateDIBSection"); 78 SelectObject(data.hdc, data.hbm); 79 pixels = cast(COLOR*)pvBits; 80 } 81 82 auto opDispatch(string F, A...)(A args) 83 if (is(typeof(mixin(F~"(data.hdc, args)")))) 84 { 85 mixin("return "~F~"(data.hdc, args);"); 86 } 87 } 88 89 unittest 90 { 91 alias RGB = ae.utils.graphics.color.RGB; 92 93 // alias BGR COLOR; 94 alias BGRX COLOR; 95 auto b = GDICanvas!COLOR(100, 100); 96 b.clear(COLOR(255, 255, 255)); 97 98 const str = "Hello, world!"; 99 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); 100 b.SelectObject(f); 101 b.SetBkColor(0xFFFFFF); 102 b.SetTextColor(0x0000FF); 103 b.TextOutA(10, 10, str.ptr, cast(uint)str.length); 104 105 b.SetPixel(5, 5, 0xFF0000); 106 GdiFlush(); 107 b[6, 6] = COLOR(255, 0, 0); 108 109 import ae.utils.graphics.image; 110 auto i = b.copy.colorMap!(c => RGB(c.r,c.g,c.b))(); 111 assert(i[5, 5] == RGB(0, 0, 255)); 112 assert(i[6, 6] == RGB(0, 0, 255)); 113 114 // i.savePNG("gditest.png"); 115 // i.savePNM("gditest.pnm"); 116 }