]> git.imager.perl.org - imager.git/blob - win32.c
tests missing from MANIFEST
[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
17 */
18
19 static void set_logfont(char *face, int size, LOGFONT *lf) {
20   memset(lf, 0, sizeof(LOGFONT));
21
22   lf->lfHeight = -size; /* character height rather than cell height */
23   lf->lfCharSet = ANSI_CHARSET;
24   lf->lfOutPrecision = OUT_TT_PRECIS;
25   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
26   lf->lfQuality = PROOF_QUALITY;
27   strncpy(lf->lfFaceName, face, sizeof(lf->lfFaceName)-1);
28   /* NUL terminated by the memset at the top */
29 }
30
31 #define fixed(x) ((x).value + ((x).fract) / 65536.0)
32
33 int i_wf_bbox(char *face, int size, char *text, int length, int *bbox) {
34   LOGFONT lf;
35   HFONT font, oldFont;
36   HDC dc;
37   SIZE sz;
38   TEXTMETRIC tm;
39   ABC first, last;
40   GLYPHMETRICS gm;
41   int i;
42   MAT2 mat;
43
44   set_logfont(face, size, &lf);
45   font = CreateFontIndirect(&lf);
46   if (!font) 
47     return 0;
48   dc = GetDC(NULL);
49   oldFont = (HFONT)SelectObject(dc, font);
50
51 #if 1
52   if (!GetTextExtentPoint32(dc, text, length, &sz)
53       || !GetTextMetrics(dc, &tm)) {
54     SelectObject(dc, oldFont);
55     ReleaseDC(NULL, dc);
56     DeleteObject(font);
57     return 0;
58   }
59   /* if there's a way to get a characters ascent/descent reliably, I can't
60      see it.  GetGlyphOutline() seems to return the same size for
61      all characters.
62   */
63   bbox[1] = bbox[4] = tm.tmDescent;
64   bbox[2] = sz.cx;
65   bbox[3] = bbox[5] = tm.tmAscent;
66   
67   if (GetCharABCWidths(dc, text[0], text[0], &first)
68       && GetCharABCWidths(dc, text[length-1], text[length-1], &last)) {
69     bbox[0] = first.abcA;
70     if (last.abcC < 0)
71       bbox[2] -= last.abcC;
72   }
73   else {
74     bbox[0] = 0;
75   }
76 #else
77   for (i = 0; i < length; ++i) {
78     memset(&gm, 0, sizeof(gm));
79     memset(&mat, 0, sizeof(mat));
80     mat.eM11.value = 1;
81     mat.eM22.value = 1;
82     if (GetGlyphOutline(dc, GGO_METRICS, text[i], &gm, 0, NULL, &mat) != GDI_ERROR) {
83       printf("%02X: black (%d, %d) origin (%d, %d) cell(%d, %d)\n",
84              text[i], gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x, 
85              gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY);
86       printf("  : mat [ %-8f  %-8f ]\n", fixed(mat.eM11), fixed(mat.eM12));
87       printf("        [ %-8f  %-8f ]\n", fixed(mat.eM21), fixed(mat.eM22));
88     }
89     else {
90       printf("Could not get metrics for '\\x%02X'\n", text[i]);
91     }
92     if (GetCharABCWidths(dc, text[i], text[i], &first)) {
93       printf("%02X: %d %d %d\n", text[i], first.abcA, first.abcB, first.abcC);
94     }
95   }
96 #endif
97
98   SelectObject(dc, oldFont);
99   ReleaseDC(NULL, dc);
100   DeleteObject(font);
101
102   return 1;
103 }
104
105
106 /* renders the text to an in-memory RGB bitmap 
107    It would be nice to render to greyscale, but Windows doesn't have
108    native greyscale bitmaps.
109  */
110 LPVOID render_text(char *face, int size, char *text, int length, int aa,
111                    HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm) {
112   BITMAPINFO bmi;
113   BITMAPINFOHEADER *bmih = &bmi.bmiHeader;
114   HDC dc, bmpDc;
115   LOGFONT lf;
116   HFONT font, oldFont;
117   SIZE sz;
118   HBITMAP bm, oldBm;
119   LPVOID bits;
120   
121   dc = GetDC(NULL);
122   set_logfont(face, size, &lf);
123
124 #ifdef ANTIALIASED_QUALITY
125   /* See KB article Q197076
126      "INFO: Controlling Anti-aliased Text via the LOGFONT Structure"
127   */
128   lf.lfQuality = aa ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
129 #endif
130
131   bmpDc = CreateCompatibleDC(dc);
132   if (bmpDc) {
133     font = CreateFontIndirect(&lf);
134     if (font) {
135       oldFont = SelectObject(bmpDc, font);
136       GetTextExtentPoint32(bmpDc, text, length, &sz);
137       GetTextMetrics(bmpDc, tm);
138       
139       memset(&bmi, 0, sizeof(bmi));
140       bmih->biSize = sizeof(*bmih);
141       bmih->biWidth = sz.cx;
142       bmih->biHeight = sz.cy;
143       bmih->biPlanes = 1;
144       bmih->biBitCount = 24;
145       bmih->biCompression = BI_RGB;
146       bmih->biSizeImage = 0;
147       bmih->biXPelsPerMeter = 72 / 2.54 * 100;
148       bmih->biYPelsPerMeter = bmih->biXPelsPerMeter;
149       bmih->biClrUsed = 0;
150       bmih->biClrImportant = 0;
151       
152       bm = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
153
154       if (bm) {
155         oldBm = SelectObject(bmpDc, bm);
156         SetTextColor(bmpDc, RGB(255, 255, 255));
157         SetBkColor(bmpDc, RGB(0, 0, 0));
158         TextOut(bmpDc, 0, 0, text, length);
159         SelectObject(bmpDc, oldBm);
160       }
161       else {
162         i_push_errorf(0, "Could not create DIB section for render: %ld",
163                       GetLastError());
164         SelectObject(bmpDc, oldFont);
165         DeleteObject(font);
166         DeleteDC(bmpDc);
167         ReleaseDC(NULL, dc);
168         return NULL;
169       }
170       SelectObject(bmpDc, oldFont);
171       DeleteObject(font);
172     }
173     else {
174       i_push_errorf(0, "Could not create logical font: %ld",
175                     GetLastError());
176       DeleteDC(bmpDc);
177       ReleaseDC(NULL, dc);
178       return NULL;
179     }
180     DeleteDC(bmpDc);
181   }
182   else {
183     i_push_errorf(0, "Could not create rendering DC: %ld", GetLastError());
184     ReleaseDC(NULL, dc);
185     return NULL;
186   }
187
188   ReleaseDC(NULL, dc);
189
190   *pbm = bm;
191   *psz = sz;
192
193   return bits;
194 }
195
196 int
197 i_wf_text(char *face, i_img *im, int tx, int ty, i_color *cl, int size, 
198           char *text, int len, int align, int aa) {
199   unsigned char *bits;
200   HBITMAP bm;
201   SIZE sz;
202   int line_width;
203   int x, y;
204   int ch;
205   TEXTMETRIC tm;
206   int top;
207
208   bits = render_text(face, size, text, len, aa, &bm, &sz, &tm);
209   if (!bits)
210     return 0;
211   
212   line_width = sz.cx * 3;
213   line_width = (line_width + 3) / 4 * 4;
214   top = ty;
215   if (align)
216     top -= tm.tmAscent;
217
218   for (y = 0; y < sz.cy; ++y) {
219     for (x = 0; x < sz.cx; ++x) {
220       i_color pel;
221       int scale = bits[3 * x];
222       i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
223       for (ch = 0; ch < im->channels; ++ch) {
224         pel.channel[ch] = 
225           ((255-scale) * pel.channel[ch] + scale*cl->channel[ch]) / 255.0;
226       }
227       i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
228     }
229     bits += line_width;
230   }
231   DeleteObject(bm);
232
233   return 1;
234 }
235
236 int
237 i_wf_cp(char *face, i_img *im, int tx, int ty, int channel, int size, 
238           char *text, int len, int align, int aa) {
239   unsigned char *bits;
240   HBITMAP bm;
241   SIZE sz;
242   int line_width;
243   int x, y;
244   int ch;
245   TEXTMETRIC tm;
246   int top;
247
248   bits = render_text(face, size, text, len, aa, &bm, &sz, &tm);
249   if (!bits)
250     return 0;
251   
252   line_width = sz.cx * 3;
253   line_width = (line_width + 3) / 4 * 4;
254   top = ty;
255   if (align)
256     top -= tm.tmAscent;
257
258   for (y = 0; y < sz.cy; ++y) {
259     for (x = 0; x < sz.cx; ++x) {
260       i_color pel;
261       int scale = bits[3 * x];
262       i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
263       pel.channel[channel] = scale;
264       i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
265     }
266     bits += line_width;
267   }
268   DeleteObject(bm);
269
270   return 1;
271 }