1 #define _WIN32_WINNT 0x500
9 win32.c - implements some win32 specific code, specifically Win32 font support.
14 if (i_wf_bbox(facename, size, text, text_len, bbox)) {
17 i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa, utf8);
18 i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa, utf8)
22 An Imager interface to font output using the Win32 GDI.
29 #define fixed(x) ((x).value + ((x).fract) / 65536.0)
31 static void set_logfont(const char *face, int size, LOGFONT *lf);
33 static LPVOID render_text(const char *face, int size, const char *text, int length, int aa,
34 HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm, int *bbox, int utf8);
35 static LPWSTR utf8_to_wide_string(char const *text, int text_len, int *wide_chars);
38 =item i_wf_bbox(face, size, text, length, bbox, utf8)
40 Calculate a bounding box for the text.
45 int i_wf_bbox(const char *face, int size, const char *text, int length, int *bbox,
55 int ascent, descent, max_ascent = -size, min_descent = size;
59 unsigned long first_ch, last_ch;
61 mm_log((1, "i_wf_bbox(face %s, size %d, text %p, length %d, bbox %p, utf8 %d)\n", face, size, text, length, bbox, utf8));
63 set_logfont(face, size, &lf);
64 font = CreateFontIndirect(&lf);
68 oldFont = (HFONT)SelectObject(dc, font);
72 if (GetTextFace(dc, sizeof(facename), facename)) {
73 mm_log((1, " face: %s\n", facename));
79 while (work_len > 0) {
84 c = i_utf8_advance(&workp, &work_len);
86 i_push_error(0, "invalid UTF8 character");
91 c = (unsigned char)*workp++;
100 cp = c > '~' ? '.' : c < ' ' ? '.' : c;
102 memset(&mat, 0, sizeof(mat));
105 if (GetGlyphOutline(dc, (UINT)c, GGO_METRICS, &gm, 0, NULL, &mat) != GDI_ERROR) {
106 mm_log((2, " glyph '%c' (%02x): bbx (%u,%u) org (%d,%d) inc(%d,%d)\n",
107 cp, c, gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x,
108 gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY));
110 ascent = gm.gmptGlyphOrigin.y;
111 descent = ascent - gm.gmBlackBoxY;
112 if (ascent > max_ascent) max_ascent = ascent;
113 if (descent < min_descent) min_descent = descent;
116 mm_log((1, " glyph '%c' (%02x): error %d\n", cp, c, GetLastError()));
122 LPWSTR wide_text = utf8_to_wide_string(text, length, &wide_chars);
127 if (!GetTextExtentPoint32W(dc, wide_text, wide_chars, &sz)
128 || !GetTextMetrics(dc, &tm)) {
129 SelectObject(dc, oldFont);
138 if (!GetTextExtentPoint32(dc, text, length, &sz)
139 || !GetTextMetrics(dc, &tm)) {
140 SelectObject(dc, oldFont);
146 bbox[BBOX_GLOBAL_DESCENT] = -tm.tmDescent;
147 bbox[BBOX_DESCENT] = min_descent == size ? -tm.tmDescent : min_descent;
148 bbox[BBOX_POS_WIDTH] = sz.cx;
149 bbox[BBOX_ADVANCE_WIDTH] = sz.cx;
150 bbox[BBOX_GLOBAL_ASCENT] = tm.tmAscent;
151 bbox[BBOX_ASCENT] = max_ascent == -size ? tm.tmAscent : max_ascent;
154 && GetCharABCWidths(dc, first_ch, first_ch, &first)
155 && GetCharABCWidths(dc, last_ch, last_ch, &last)) {
156 mm_log((1, "first: %d A: %d B: %d C: %d\n", first_ch,
157 first.abcA, first.abcB, first.abcC));
158 mm_log((1, "last: %d A: %d B: %d C: %d\n", last_ch,
159 last.abcA, last.abcB, last.abcC));
160 bbox[BBOX_NEG_WIDTH] = first.abcA;
161 bbox[BBOX_RIGHT_BEARING] = last.abcC;
163 bbox[BBOX_POS_WIDTH] -= last.abcC;
166 bbox[BBOX_NEG_WIDTH] = 0;
167 bbox[BBOX_RIGHT_BEARING] = 0;
170 SelectObject(dc, oldFont);
174 mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
176 return BBOX_RIGHT_BEARING + 1;
180 =item i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa)
182 Draws the text in the given color.
188 i_wf_text(const char *face, i_img *im, int tx, int ty, const i_color *cl, int size,
189 const char *text, int len, int align, int aa, int utf8) {
198 int bbox[BOUNDING_BOX_COUNT];
200 mm_log((1, "i_wf_text(face %s, im %p, tx %d, ty %d, cl %p, size %d, text %p, length %d, align %d, aa %d, utf8 %d)\n", face, im, tx, ty, cl, size, text, len, align, aa, aa, utf8));
202 if (!i_wf_bbox(face, size, text, len, bbox, utf8))
205 bits = render_text(face, size, text, len, aa, &bm, &sz, &tm, bbox, utf8);
209 tx += bbox[BBOX_NEG_WIDTH];
210 line_width = sz.cx * 3;
211 line_width = (line_width + 3) / 4 * 4;
217 top -= tm.tmAscent - bbox[BBOX_ASCENT];
220 for (y = 0; y < sz.cy; ++y) {
221 for (x = 0; x < sz.cx; ++x) {
223 int scale = bits[3 * x];
224 i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
225 for (ch = 0; ch < im->channels; ++ch) {
227 ((255-scale) * pel.channel[ch] + scale*cl->channel[ch]) / 255;
229 i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
239 =item i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa)
241 Draws the text in the given channel.
247 i_wf_cp(const char *face, i_img *im, int tx, int ty, int channel, int size,
248 const char *text, int len, int align, int aa, int utf8) {
256 int bbox[BOUNDING_BOX_COUNT];
258 mm_log((1, "i_wf_cp(face %s, im %p, tx %d, ty %d, channel %d, size %d, text %p, length %d, align %d, aa %d, utf8 %d)\n", face, im, tx, ty, channel, size, text, len, align, aa, aa, utf8));
260 if (!i_wf_bbox(face, size, text, len, bbox, utf8))
263 bits = render_text(face, size, text, len, aa, &bm, &sz, &tm, bbox, utf8);
267 line_width = sz.cx * 3;
268 line_width = (line_width + 3) / 4 * 4;
274 top -= tm.tmAscent - bbox[BBOX_ASCENT];
277 for (y = 0; y < sz.cy; ++y) {
278 for (x = 0; x < sz.cx; ++x) {
280 int scale = bits[3 * x];
281 i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
282 pel.channel[channel] = scale;
283 i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
292 static HMODULE gdi_dll;
293 typedef BOOL (CALLBACK *AddFontResourceExA_t)(LPCSTR, DWORD, PVOID);
294 static AddFontResourceExA_t AddFontResourceExAp;
295 typedef BOOL (CALLBACK *RemoveFontResourceExA_t)(LPCSTR, DWORD, PVOID);
296 static RemoveFontResourceExA_t RemoveFontResourceExAp;
299 =item i_wf_addfont(char const *filename, char const *resource_file)
301 Adds a TTF font file as usable by the application.
303 Where possible the font is added as private to the application.
305 Under Windows 95/98/ME the font is added globally, since we don't have
306 any choice. In either case call i_wf_delfont() to remove it.
311 i_wf_addfont(char const *filename) {
315 gdi_dll = GetModuleHandle("GDI32");
317 AddFontResourceExAp = (AddFontResourceExA_t)GetProcAddress(gdi_dll, "AddFontResourceExA");
318 RemoveFontResourceExAp = (RemoveFontResourceExA_t)GetProcAddress(gdi_dll, "RemoveFontResourceExA");
322 if (AddFontResourceExAp && RemoveFontResourceExAp
323 && AddFontResourceExAp(filename, FR_PRIVATE, 0)) {
326 else if (AddFontResource(filename)) {
330 i_push_errorf(0, "Could not add resource: %ld", GetLastError());
336 =item i_wf_delfont(char const *filename, char const *resource_file)
338 Deletes a TTF font file as usable by the application.
343 i_wf_delfont(char const *filename) {
346 if (AddFontResourceExAp && RemoveFontResourceExAp
347 && RemoveFontResourceExAp(filename, FR_PRIVATE, 0)) {
350 else if (RemoveFontResource(filename)) {
354 i_push_errorf(0, "Could not remove resource: %ld", GetLastError());
362 =head1 INTERNAL FUNCTIONS
366 =item set_logfont(face, size, lf)
368 Fills in a LOGFONT structure with reasonable defaults.
373 static void set_logfont(const char *face, int size, LOGFONT *lf) {
374 memset(lf, 0, sizeof(LOGFONT));
376 lf->lfHeight = -size; /* character height rather than cell height */
377 lf->lfCharSet = DEFAULT_CHARSET;
378 lf->lfOutPrecision = OUT_TT_PRECIS;
379 lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
380 lf->lfQuality = PROOF_QUALITY;
381 strncpy(lf->lfFaceName, face, sizeof(lf->lfFaceName)-1);
382 /* NUL terminated by the memset at the top */
386 =item render_text(face, size, text, length, aa, pbm, psz, tm)
388 renders the text to an in-memory RGB bitmap
390 It would be nice to render to greyscale, but Windows doesn't have
391 native greyscale bitmaps.
395 static LPVOID render_text(const char *face, int size, const char *text, int length, int aa,
396 HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm, int *bbox, int utf8) {
398 BITMAPINFOHEADER *bmih = &bmi.bmiHeader;
409 set_logfont(face, size, &lf);
411 #ifdef ANTIALIASED_QUALITY
412 /* See KB article Q197076
413 "INFO: Controlling Anti-aliased Text via the LOGFONT Structure"
415 lf.lfQuality = aa ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
419 wide_text = utf8_to_wide_string(text, length, &wide_count);
425 bmpDc = CreateCompatibleDC(dc);
427 font = CreateFontIndirect(&lf);
429 oldFont = SelectObject(bmpDc, font);
431 GetTextMetrics(bmpDc, tm);
432 sz.cx = bbox[BBOX_ADVANCE_WIDTH] - bbox[BBOX_NEG_WIDTH] + bbox[BBOX_POS_WIDTH];
433 sz.cy = bbox[BBOX_GLOBAL_ASCENT] - bbox[BBOX_GLOBAL_DESCENT];
435 memset(&bmi, 0, sizeof(bmi));
436 bmih->biSize = sizeof(*bmih);
437 bmih->biWidth = sz.cx;
438 bmih->biHeight = sz.cy;
440 bmih->biBitCount = 24;
441 bmih->biCompression = BI_RGB;
442 bmih->biSizeImage = 0;
443 bmih->biXPelsPerMeter = (LONG)(72 / 2.54 * 100);
444 bmih->biYPelsPerMeter = bmih->biXPelsPerMeter;
446 bmih->biClrImportant = 0;
448 bm = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
451 oldBm = SelectObject(bmpDc, bm);
452 SetTextColor(bmpDc, RGB(255, 255, 255));
453 SetBkColor(bmpDc, RGB(0, 0, 0));
455 TextOutW(bmpDc, -bbox[BBOX_NEG_WIDTH], 0, wide_text, wide_count);
458 TextOut(bmpDc, -bbox[BBOX_NEG_WIDTH], 0, text, length);
460 SelectObject(bmpDc, oldBm);
463 i_push_errorf(0, "Could not create DIB section for render: %ld",
465 SelectObject(bmpDc, oldFont);
473 SelectObject(bmpDc, oldFont);
479 i_push_errorf(0, "Could not create logical font: %ld",
490 i_push_errorf(0, "Could not create rendering DC: %ld", GetLastError());
507 =item utf8_to_wide_string(text, text_len, wide_chars)
514 utf8_to_wide_string(char const *text, int text_len, int *wide_chars) {
515 int wide_count = MultiByteToWideChar(CP_UTF8, 0, text, text_len, NULL, 0);
518 if (wide_count < 0) {
519 i_push_errorf(0, "Could not convert utf8: %ld", GetLastError());
523 result = mymalloc(sizeof(WCHAR) * wide_count);
524 if (MultiByteToWideChar(CP_UTF8, 0, text, text_len, result, wide_count) < 0) {
525 i_push_errorf(0, "Could not convert utf8: %ld", GetLastError());
529 result[wide_count-1] = (WCHAR)'\0';
530 *wide_chars = wide_count - 1;
541 Should really use a structure so we can set more attributes.
547 Tony Cook <tony@develop-help.com>