]>
Commit | Line | Data |
---|---|---|
faa9b3e7 TC |
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 | } |