]> git.imager.perl.org - imager.git/blob - win32.c
- change the "double-include" protection macro that imio.h uses.
[imager.git] / win32.c
1 #include "image.h"
2 #define STRICT
3 #include <windows.h>
4
5 /*
6 =head1 NAME
7
8 win32.c - implements some win32 specific code, specifically Win32 font support.
9
10 =head1 SYNOPSIS
11
12    int bbox[6];
13    if (i_wf_bbox(facename, size, text, text_len, bbox)) {
14      // we have the bbox
15    }
16    i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa);
17    i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa)
18
19 =head1 DESCRIPTION
20
21 An Imager interface to font output using the Win32 GDI.
22
23 =over
24
25 =cut
26 */
27
28 #define fixed(x) ((x).value + ((x).fract) / 65536.0)
29
30 static void set_logfont(char *face, int size, LOGFONT *lf);
31
32 static LPVOID render_text(char *face, int size, char *text, int length, int aa,
33                           HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm);
34
35 /*
36 =item i_wf_bbox(face, size, text, length, bbox)
37
38 Calculate a bounding box for the text.
39
40 =cut
41 */
42
43 int i_wf_bbox(char *face, int size, char *text, int length, int *bbox) {
44   LOGFONT lf;
45   HFONT font, oldFont;
46   HDC dc;
47   SIZE sz;
48   TEXTMETRIC tm;
49   ABC first, last;
50   GLYPHMETRICS gm;
51   int i;
52   MAT2 mat;
53
54   set_logfont(face, size, &lf);
55   font = CreateFontIndirect(&lf);
56   if (!font) 
57     return 0;
58   dc = GetDC(NULL);
59   oldFont = (HFONT)SelectObject(dc, font);
60
61 #if 1
62   if (!GetTextExtentPoint32(dc, text, length, &sz)
63       || !GetTextMetrics(dc, &tm)) {
64     SelectObject(dc, oldFont);
65     ReleaseDC(NULL, dc);
66     DeleteObject(font);
67     return 0;
68   }
69   /* if there's a way to get a characters ascent/descent reliably, I can't
70      see it.  GetGlyphOutline() seems to return the same size for
71      all characters.
72   */
73   bbox[1] = bbox[4] = tm.tmDescent;
74   bbox[2] = sz.cx;
75   bbox[3] = bbox[5] = tm.tmAscent;
76   
77   if (GetCharABCWidths(dc, text[0], text[0], &first)
78       && GetCharABCWidths(dc, text[length-1], text[length-1], &last)) {
79     bbox[0] = first.abcA;
80     if (last.abcC < 0)
81       bbox[2] -= last.abcC;
82   }
83   else {
84     bbox[0] = 0;
85   }
86 #else
87   for (i = 0; i < length; ++i) {
88     memset(&gm, 0, sizeof(gm));
89     memset(&mat, 0, sizeof(mat));
90     mat.eM11.value = 1;
91     mat.eM22.value = 1;
92     if (GetGlyphOutline(dc, GGO_METRICS, text[i], &gm, 0, NULL, &mat) != GDI_ERROR) {
93       printf("%02X: black (%d, %d) origin (%d, %d) cell(%d, %d)\n",
94              text[i], gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x, 
95              gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY);
96       printf("  : mat [ %-8f  %-8f ]\n", fixed(mat.eM11), fixed(mat.eM12));
97       printf("        [ %-8f  %-8f ]\n", fixed(mat.eM21), fixed(mat.eM22));
98     }
99     else {
100       printf("Could not get metrics for '\\x%02X'\n", text[i]);
101     }
102     if (GetCharABCWidths(dc, text[i], text[i], &first)) {
103       printf("%02X: %d %d %d\n", text[i], first.abcA, first.abcB, first.abcC);
104     }
105   }
106 #endif
107
108   SelectObject(dc, oldFont);
109   ReleaseDC(NULL, dc);
110   DeleteObject(font);
111
112   return 6;
113 }
114
115 /*
116 =item i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa)
117
118 Draws the text in the given color.
119
120 =cut
121 */
122
123 int
124 i_wf_text(char *face, i_img *im, int tx, int ty, i_color *cl, int size, 
125           char *text, int len, int align, int aa) {
126   unsigned char *bits;
127   HBITMAP bm;
128   SIZE sz;
129   int line_width;
130   int x, y;
131   int ch;
132   TEXTMETRIC tm;
133   int top;
134
135   bits = render_text(face, size, text, len, aa, &bm, &sz, &tm);
136   if (!bits)
137     return 0;
138   
139   line_width = sz.cx * 3;
140   line_width = (line_width + 3) / 4 * 4;
141   top = ty;
142   if (align)
143     top -= tm.tmAscent;
144
145   for (y = 0; y < sz.cy; ++y) {
146     for (x = 0; x < sz.cx; ++x) {
147       i_color pel;
148       int scale = bits[3 * x];
149       i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
150       for (ch = 0; ch < im->channels; ++ch) {
151         pel.channel[ch] = 
152           ((255-scale) * pel.channel[ch] + scale*cl->channel[ch]) / 255.0;
153       }
154       i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
155     }
156     bits += line_width;
157   }
158   DeleteObject(bm);
159
160   return 1;
161 }
162
163 /*
164 =item i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa)
165
166 Draws the text in the given channel.
167
168 =cut
169 */
170
171 int
172 i_wf_cp(char *face, i_img *im, int tx, int ty, int channel, int size, 
173           char *text, int len, int align, int aa) {
174   unsigned char *bits;
175   HBITMAP bm;
176   SIZE sz;
177   int line_width;
178   int x, y;
179   int ch;
180   TEXTMETRIC tm;
181   int top;
182
183   bits = render_text(face, size, text, len, aa, &bm, &sz, &tm);
184   if (!bits)
185     return 0;
186   
187   line_width = sz.cx * 3;
188   line_width = (line_width + 3) / 4 * 4;
189   top = ty;
190   if (align)
191     top -= tm.tmAscent;
192
193   for (y = 0; y < sz.cy; ++y) {
194     for (x = 0; x < sz.cx; ++x) {
195       i_color pel;
196       int scale = bits[3 * x];
197       i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
198       pel.channel[channel] = scale;
199       i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
200     }
201     bits += line_width;
202   }
203   DeleteObject(bm);
204
205   return 1;
206 }
207
208 /*
209 =back
210
211 =head1 INTERNAL FUNCTIONS
212
213 =over
214
215 =item set_logfont(face, size, lf)
216
217 Fills in a LOGFONT structure with reasonable defaults.
218
219 =cut
220 */
221
222 static void set_logfont(char *face, int size, LOGFONT *lf) {
223   memset(lf, 0, sizeof(LOGFONT));
224
225   lf->lfHeight = -size; /* character height rather than cell height */
226   lf->lfCharSet = DEFAULT_CHARSET;
227   lf->lfOutPrecision = OUT_TT_PRECIS;
228   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
229   lf->lfQuality = PROOF_QUALITY;
230   strncpy(lf->lfFaceName, face, sizeof(lf->lfFaceName)-1);
231   /* NUL terminated by the memset at the top */
232 }
233
234 /*
235 =item render_text(face, size, text, length, aa, pbm, psz, tm)
236
237 renders the text to an in-memory RGB bitmap 
238
239 It would be nice to render to greyscale, but Windows doesn't have
240 native greyscale bitmaps.
241
242 =cut
243 */
244 static LPVOID render_text(char *face, int size, char *text, int length, int aa,
245                    HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm) {
246   BITMAPINFO bmi;
247   BITMAPINFOHEADER *bmih = &bmi.bmiHeader;
248   HDC dc, bmpDc;
249   LOGFONT lf;
250   HFONT font, oldFont;
251   SIZE sz;
252   HBITMAP bm, oldBm;
253   LPVOID bits;
254   
255   dc = GetDC(NULL);
256   set_logfont(face, size, &lf);
257
258 #ifdef ANTIALIASED_QUALITY
259   /* See KB article Q197076
260      "INFO: Controlling Anti-aliased Text via the LOGFONT Structure"
261   */
262   lf.lfQuality = aa ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
263 #endif
264
265   bmpDc = CreateCompatibleDC(dc);
266   if (bmpDc) {
267     font = CreateFontIndirect(&lf);
268     if (font) {
269       oldFont = SelectObject(bmpDc, font);
270       GetTextExtentPoint32(bmpDc, text, length, &sz);
271       GetTextMetrics(bmpDc, tm);
272       
273       memset(&bmi, 0, sizeof(bmi));
274       bmih->biSize = sizeof(*bmih);
275       bmih->biWidth = sz.cx;
276       bmih->biHeight = sz.cy;
277       bmih->biPlanes = 1;
278       bmih->biBitCount = 24;
279       bmih->biCompression = BI_RGB;
280       bmih->biSizeImage = 0;
281       bmih->biXPelsPerMeter = 72 / 2.54 * 100;
282       bmih->biYPelsPerMeter = bmih->biXPelsPerMeter;
283       bmih->biClrUsed = 0;
284       bmih->biClrImportant = 0;
285       
286       bm = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
287
288       if (bm) {
289         oldBm = SelectObject(bmpDc, bm);
290         SetTextColor(bmpDc, RGB(255, 255, 255));
291         SetBkColor(bmpDc, RGB(0, 0, 0));
292         TextOut(bmpDc, 0, 0, text, length);
293         SelectObject(bmpDc, oldBm);
294       }
295       else {
296         i_push_errorf(0, "Could not create DIB section for render: %ld",
297                       GetLastError());
298         SelectObject(bmpDc, oldFont);
299         DeleteObject(font);
300         DeleteDC(bmpDc);
301         ReleaseDC(NULL, dc);
302         return NULL;
303       }
304       SelectObject(bmpDc, oldFont);
305       DeleteObject(font);
306     }
307     else {
308       i_push_errorf(0, "Could not create logical font: %ld",
309                     GetLastError());
310       DeleteDC(bmpDc);
311       ReleaseDC(NULL, dc);
312       return NULL;
313     }
314     DeleteDC(bmpDc);
315   }
316   else {
317     i_push_errorf(0, "Could not create rendering DC: %ld", GetLastError());
318     ReleaseDC(NULL, dc);
319     return NULL;
320   }
321
322   ReleaseDC(NULL, dc);
323
324   *pbm = bm;
325   *psz = sz;
326
327   return bits;
328 }
329
330 /*
331 =head1 BUGS
332
333 Should really use a structure so we can set more attributes.
334
335 Should support UTF8
336
337 =head1 AUTHOR
338
339 Tony Cook <tony@develop-help.com>
340
341 =head1 SEE ALSO
342
343 Imager(3)
344
345 =cut
346 */