GreExtTextOutW函数实现一串字符串输出到指定区域,当然是从字符串变成图片输出。这些都调用FreeType库来实现的,具体实现代码如下:
#001 BOOL
#002 APIENTRY
#003 GreExtTextOutW(
#004 IN HDC hDC,
#005 IN INT XStart,
#006 IN INT YStart,
#007 IN UINT fuOptions,
#008 IN OPTIONAL LPRECT lprc,
#009 IN LPWSTR String,
#010 IN INT Count,
#011 IN OPTIONAL LPINT Dx,
#012 IN DWORD dwCodePage)
#013 {
#014 /*
#015 * FIXME:
#016 * Call EngTextOut, which does the real work (calling DrvTextOut where
#017 * appropriate)
#018 */
#019
#020 DC *dc;
#021 PDC_ATTR Dc_Attr;
#022 SURFOBJ *SurfObj;
#023 SURFACE *psurf = NULL;
#024 int error, glyph_index, n, i;
#025 FT_Face face;
#026 FT_GlyphSlot glyph;
#027 FT_Glyph realglyph;
#028 FT_BitmapGlyph realglyph2;
#029 LONGLONG TextLeft, RealXStart;
#030 ULONG TextTop, previous, BackgroundLeft;
#031 FT_Bool use_kerning;
#032 RECTL DestRect, MaskRect;
#033 POINTL SourcePoint, BrushOrigin;
#034 HBRUSH hBrushFg = NULL;
#035 PGDIBRUSHOBJ BrushFg = NULL;
#036 GDIBRUSHINST BrushFgInst;
#037 HBRUSH hBrushBg = NULL;
#038 PGDIBRUSHOBJ BrushBg = NULL;
#039 GDIBRUSHINST BrushBgInst;
#040 HBITMAP HSourceGlyph;
#041 SURFOBJ *SourceGlyphSurf;
#042 SIZEL bitSize;
#043 FT_CharMap found = 0, charmap;
#044 INT yoff;
#045 FONTOBJ *FontObj;
#046 PFONTGDI FontGDI;
#047 PTEXTOBJ TextObj = NULL;
#048 PPALGDI PalDestGDI;
#049 XLATEOBJ *XlateObj=NULL, *XlateObj2=NULL;
#050 ULONG Mode;
#051 FT_Render_Mode RenderMode;
#052 BOOLEAN Render;
#053 POINT Start;
#054 BOOL DoBreak = FALSE;
#055 HPALETTE hDestPalette;
#056 USHORT DxShift;
#057
#058 // TODO: Write test-cases to exactly match real Windows in different
#059 // bad parameters (e.g. does Windows check the DC or the RECT first?).
锁住输出设备。
#060 dc = DC_LockDc(hDC);
#061 if (!dc)
#062 {
#063 SetLastWin32Error(ERROR_INVALID_HANDLE);
#064 return FALSE;
#065 }
#066 if (dc->DC_Type == DC_TYPE_INFO)
#067 {
#068 DC_UnlockDc(dc);
#069 /* Yes, Windows really returns TRUE in this case */
#070 return TRUE;
#071 }
#072
获取设备的属性。
#073 Dc_Attr = dc->pDc_Attr;
#074 if (!Dc_Attr) Dc_Attr = &dc->Dc_Attr;
#075
检查输出字符串是否有效,如果无效就直接返回。
#076 /* Check if String is valid */
#077 if ((Count > 0xFFFF) || (Count > 0 && String == NULL))
#078 {
#079 SetLastWin32Error(ERROR_INVALID_PARAMETER);
#080 goto fail;
#081 }
#082
#083 DxShift = fuOptions & ETO_PDY ? 1 : 0;
#084
获取输出路径。
#085 if (PATH_IsPathOpen(dc->DcLevel))
#086 {
#087 if (!PATH_ExtTextOut( dc,
#088 XStart,
#089 YStart,
#090 fuOptions,
#091 (const RECT *)lprc,
#092 String,
#093 Count,
#094 (const INT *)Dx)) goto fail;
#095 goto good;
#096 }
#097
转换逻辑坐标为设备坐标。
#098 if (lprc && (fuOptions & (ETO_OPAQUE | ETO_CLIPPED)))
#099 {
#100 IntLPtoDP(dc, (POINT *)lprc, 2);
#101 }
#102
获取设备输出的表面层缓冲区。
#103 psurf = SURFACE_LockSurface(dc->w.hBitmap);
#104 if (!psurf)
#105 {
#106 goto fail;
#107 }
#108 SurfObj = &psurf->SurfObj;
#109 ASSERT(SurfObj);
#110
#111 Start.x = XStart;
#112 Start.y = YStart;
#113 IntLPtoDP(dc, &Start, 1);
#114
#115 RealXStart = (Start.x + dc->ptlDCOrig.x) << 6;
#116 YStart = Start.y + dc->ptlDCOrig.y;
#117
创建输出字符串的调色板。
#118 /* Create the brushes */
#119 hDestPalette = psurf->hDIBPalette;
#120 if (!hDestPalette) hDestPalette = pPrimarySurface->DevInfo.hpalDefault;
#121 PalDestGDI = PALETTE_LockPalette(hDestPalette);
#122 if ( !PalDestGDI )
#123 Mode = PAL_RGB;
#124 else
#125 {
#126 Mode = PalDestGDI->Mode;
#127 PALETTE_UnlockPalette(PalDestGDI);
#128 }
#129 XlateObj = (XLATEOBJ*)IntEngCreateXlate(Mode, PAL_RGB, hDestPalette, NULL);
#130 if ( !XlateObj )
#131 {
#132 goto fail;
#133 }
创建输出字体的画刷。
#134 hBrushFg = NtGdiCreateSolidBrush(XLATEOBJ_iXlate(XlateObj, Dc_Attr->crForegroundClr), 0);
#135 if ( !hBrushFg )
#136 {
#137 goto fail;
#138 }
#139 BrushFg = BRUSHOBJ_LockBrush(hBrushFg);
#140 if ( !BrushFg )
#141 {
#142 goto fail;
#143 }
#144 IntGdiInitBrushInstance(&BrushFgInst, BrushFg, NULL);
#145 if ((fuOptions & ETO_OPAQUE) || Dc_Attr->jBkMode == OPAQUE)
#146 {
#147 hBrushBg = NtGdiCreateSolidBrush(XLATEOBJ_iXlate(XlateObj, Dc_Attr->crBackgroundClr), 0);
#148 if ( !hBrushBg )
#149 {
#150 goto fail;
#151 }
#152 BrushBg = BRUSHOBJ_LockBrush(hBrushBg);
#153 if ( !BrushBg )
#154 {
#155 goto fail;
#156 }
#157 IntGdiInitBrushInstance(&BrushBgInst, BrushBg, NULL);
#158 }
#159 XlateObj2 = (XLATEOBJ*)IntEngCreateXlate(PAL_RGB, Mode, NULL, hDestPalette);
#160 if ( !XlateObj2 )
#161 {
#162 goto fail;
#163 }
#164
#165 SourcePoint.x = 0;
#166 SourcePoint.y = 0;
#167 MaskRect.left = 0;
#168 MaskRect.top = 0;
#169 BrushOrigin.x = 0;
#170 BrushOrigin.y = 0;
#171
在输出文本字符串前之前,用当前背景色更新输出区域。
#172 if ((fuOptions & ETO_OPAQUE) && lprc)
#173 {
#174 DestRect.left = lprc->left + dc->ptlDCOrig.x;
#175 DestRect.top = lprc->top + dc->ptlDCOrig.y;
#176 DestRect.right = lprc->right + dc->ptlDCOrig.x;
#177 DestRect.bottom = lprc->bottom + dc->ptlDCOrig.y;
#178 IntLPtoDP(dc, (LPPOINT)&DestRect, 2);
#179 IntEngBitBlt(
#180 &psurf->SurfObj,
#181 NULL,
#182 NULL,
#183 dc->CombinedClip,
#184 NULL,
#185 &DestRect,
#186 &SourcePoint,
#187 &SourcePoint,
#188 &BrushBgInst.BrushObject,
#189 &BrushOrigin,
#190 ROP3_TO_ROP4(PATCOPY));
#191 fuOptions &= ~ETO_OPAQUE;
#192 }
#193 else
#194 {
#195 if (Dc_Attr->jBkMode == OPAQUE)
#196 {
#197 fuOptions |= ETO_OPAQUE;
#198 }
#199 }
#200
初始化字符串输出的字体。
#201 TextObj = RealizeFontInit(Dc_Attr->hlfntNew);
#202 if (TextObj == NULL)
#203 {
#204 goto fail;
#205 }
#206
#207 FontObj = TextObj->Font;
#208 ASSERT(FontObj);
#209 FontGDI = ObjToGDI(FontObj, FONT);
#210 ASSERT(FontGDI);
#211
使用FREETYPE库来查找相应字符编码表。
#212 IntLockFreeType;
#213 face = FontGDI->face;
#214 if (face->charmap == NULL)
#215 {
#216 DPRINT("WARNING: No charmap selected!/n");
#217 DPRINT("This font face has %d charmaps/n", face->num_charmaps);
#218
#219 for (n = 0; n < face->num_charmaps; n++)
#220 {
#221 charmap = face->charmaps[n];
#222 DPRINT("found charmap encoding: %u/n", charmap->encoding);
#223 if (charmap->encoding != 0)
#224 {
#225 found = charmap;
#226 break;
#227 }
#228 }
#229 if (!found)
#230 {
#231 DPRINT1("WARNING: Could not find desired charmap!/n");
#232 }
#233 error = FT_Set_Charmap(face, found);
#234 if (error)
#235 {
#236 DPRINT1("WARNING: Could not set the charmap!/n");
#237 }
#238 }
#239
判断是单色字符显示,还是彩色图片显示。
#240 Render = IntIsFontRenderingEnabled();
#241 if (Render)
#242 RenderMode = IntGetFontRenderMode(&TextObj->logfont.elfEnumLogfontEx.elfLogFont);
#243 else
#244 RenderMode = FT_RENDER_MODE_MONO;
#245
设置字体需像素点大小。
#246 error = FT_Set_Pixel_Sizes(
#247 face,
#248 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
#249 /* FIXME should set character height if neg */
#250 (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight < 0 ?
#251 - TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight :
#252 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? 11 : TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight));
#253 if (error)
#254 {
#255 DPRINT1("Error in setting pixel sizes: %u/n", error);
#256 IntUnLockFreeType;
#257 goto fail;
#258 }
#259
#260 /*
#261 * Process the vertical alignment and determine the yoff.
#262 */
#263
处理每行字体的高度。
#264 if (Dc_Attr->lTextAlign & TA_BASELINE)
#265 yoff = 0;
#266 else if (Dc_Attr->lTextAlign & TA_BOTTOM)
#267 yoff = -face->size->metrics.descender >> 6;
#268 else /* TA_TOP */
#269 yoff = face->size->metrics.ascender >> 6;
#270
#271 use_kerning = FT_HAS_KERNING(face);
#272 previous = 0;
#273
#274 /*
#275 * Process the horizontal alignment and modify XStart accordingly.
#276 */
#277
处理字体宽度。
#278 if (Dc_Attr->lTextAlign & (TA_RIGHT | TA_CENTER))
#279 {
#280 ULONGLONG TextWidth = 0;
#281 LPCWSTR TempText = String;
#282 int Start;
#283
#284 /*
#285 * Calculate width of the text.
#286 */
#287
#288 if (NULL != Dx)
#289 {
#290 Start = Count < 2 ? 0 : Count - 2;
#291 TextWidth = Count < 2 ? 0 : (Dx[(Count-2)<<DxShift] << 6);
#292 }
#293 else
#294 {
#295 Start = 0;
#296 }
#297 TempText = String + Start;
#298
#299 for (i = Start; i < Count; i++)
#300 {
#301 if (fuOptions & ETO_GLYPH_INDEX)
#302 glyph_index = *TempText;
#303 else
#304 glyph_index = FT_Get_Char_Index(face, *TempText);
#305
#306 if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
#307 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)))
#308 {
#309 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
#310 if (error)
#311 {
#312 DPRINT1("WARNING: Failed to load and render glyph! [index: %u]/n", glyph_index);
#313 }
#314
#315 glyph = face->glyph;
#316 realglyph = ftGdiGlyphCacheSet(face, glyph_index,
#317 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, glyph, RenderMode);
#318 if (!realglyph)
#319 {
#320 DPRINT1("Failed to render glyph! [index: %u]/n", glyph_index);
#321 IntUnLockFreeType;
#322 goto fail;
#323 }
#324
#325 }
#326 /* retrieve kerning distance */
#327 if (use_kerning && previous && glyph_index)
#328 {
#329 FT_Vector delta;
#330 FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
#331 TextWidth += delta.x;
#332 }
#333
#334 TextWidth += realglyph->advance.x >> 10;
#335
#336 previous = glyph_index;
#337 TempText++;
#338 }
#339
#340 previous = 0;
#341
#342 if (Dc_Attr->lTextAlign & TA_RIGHT)
#343 {
#344 RealXStart -= TextWidth;
#345 }
#346 else
#347 {
#348 RealXStart -= TextWidth / 2;
#349 }
#350 }
#351
#352 TextLeft = RealXStart;
#353 TextTop = YStart;
#354 BackgroundLeft = (RealXStart + 32) >> 6;
#355
#356 /*
#357 * The main rendering loop.
#358 */
#359
这里循环处理显示每一个字符,主要的过程就是读取一个字符的编码,然后根据编码到码表里找到字符的笔画,然后生成一个字符的BMP位图,再把每个字符位图输出,就可以显示相应的字符串了。
#360 for (i = 0; i < Count; i++)
#361 {
#362 if (fuOptions & ETO_GLYPH_INDEX)
#363 glyph_index = *String;
#364 else
#365 glyph_index = FT_Get_Char_Index(face, *String);
#366
#367 if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
#368 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)))
#369 {
#370 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
#371 if (error)
#372 {
#373 DPRINT1("Failed to load and render glyph! [index: %u]/n", glyph_index);
#374 IntUnLockFreeType;
#375 goto fail;
#376 }
#377 glyph = face->glyph;
#378 realglyph = ftGdiGlyphCacheSet(face,
#379 glyph_index,
#380 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight,
#381 glyph,
#382 RenderMode);
#383 if (!realglyph)
#384 {
#385 DPRINT1("Failed to render glyph! [index: %u]/n", glyph_index);
#386 IntUnLockFreeType;
#387 goto fail;
#388 }
#389 }
#390 // DbgPrint("realglyph: %x/n", realglyph);
#391 // DbgPrint("TextLeft: %d/n", TextLeft);
#392
#393 /* retrieve kerning distance and move pen position */
#394 if (use_kerning && previous && glyph_index && NULL == Dx)
#395 {
#396 FT_Vector delta;
#397 FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
#398 TextLeft += delta.x;
#399 }
#400 // DPRINT1("TextLeft: %d/n", TextLeft);
#401 // DPRINT1("TextTop: %d/n", TextTop);
#402
#403 if (realglyph->format == ft_glyph_format_outline)
#404 {
#405 DbgPrint("Should already be done/n");
#406 // error = FT_Render_Glyph(glyph, RenderMode);
#407 error = FT_Glyph_To_Bitmap(&realglyph, RenderMode, 0, 0);
#408 if (error)
#409 {
#410 DPRINT1("WARNING: Failed to render glyph!/n");
#411 goto fail;
#412 }
#413 }
#414 realglyph2 = (FT_BitmapGlyph)realglyph;
#415
#416 // DPRINT1("Pitch: %d/n", pitch);
#417 // DPRINT1("Advance: %d/n", realglyph->advance.x);
#418
#419 if (fuOptions & ETO_OPAQUE)
#420 {
#421 DestRect.left = BackgroundLeft;
#422 DestRect.right = (TextLeft + (realglyph->advance.x >> 10) + 32) >> 6;
#423 DestRect.top = TextTop + yoff - ((face->size->metrics.ascender + 32) >> 6);
#424 DestRect.bottom = TextTop + yoff + ((32 - face->size->metrics.descender) >> 6);
#425 IntEngBitBlt(
#426 &psurf->SurfObj,
#427 NULL,
#428 NULL,
#429 dc->CombinedClip,
#430 NULL,
#431 &DestRect,
#432 &SourcePoint,
#433 &SourcePoint,
#434 &BrushBgInst.BrushObject,
#435 &BrushOrigin,
#436 ROP3_TO_ROP4(PATCOPY));
#437 BackgroundLeft = DestRect.right;
#438 }
#439
#440 DestRect.left = ((TextLeft + 32) >> 6) + realglyph2->left;
#441 DestRect.right = DestRect.left + realglyph2->bitmap.width;
#442 DestRect.top = TextTop + yoff - realglyph2->top;
#443 DestRect.bottom = DestRect.top + realglyph2->bitmap.rows;
#444
#445 bitSize.cx = realglyph2->bitmap.width;
#446 bitSize.cy = realglyph2->bitmap.rows;
#447 MaskRect.right = realglyph2->bitmap.width;
#448 MaskRect.bottom = realglyph2->bitmap.rows;
#449
#450 /*
#451 * We should create the bitmap out of the loop at the biggest possible
#452 * glyph size. Then use memset with 0 to clear it and sourcerect to
#453 * limit the work of the transbitblt.
#454 *
#455 * FIXME: DIB bitmaps should have an lDelta which is a multiple of 4.
#456 * Here we pass in the pitch from the FreeType bitmap, which is not
#457 * guaranteed to be a multiple of 4. If it's not, we should expand
#458 * the FreeType bitmap to a temporary bitmap.
#459 */
#460
#461 HSourceGlyph = EngCreateBitmap(bitSize, realglyph2->bitmap.pitch,
#462 (realglyph2->bitmap.pixel_mode == ft_pixel_mode_grays) ?
#463 BMF_8BPP : BMF_1BPP, BMF_TOPDOWN,
#464 realglyph2->bitmap.buffer);
#465 if ( !HSourceGlyph )
#466 {
#467 DPRINT1("WARNING: EngLockSurface() failed!/n");
#468 // FT_Done_Glyph(realglyph);
#469 IntUnLockFreeType;
#470 goto fail;
#471 }
#472 SourceGlyphSurf = EngLockSurface((HSURF)HSourceGlyph);
#473 if ( !SourceGlyphSurf )
#474 {
#475 EngDeleteSurface((HSURF)HSourceGlyph);
#476 DPRINT1("WARNING: EngLockSurface() failed!/n");
#477 IntUnLockFreeType;
#478 goto fail;
#479 }
#480
#481 /*
#482 * Use the font data as a mask to paint onto the DCs surface using a
#483 * brush.
#484 */
#485
#486 if (lprc &&
#487 (fuOptions & ETO_CLIPPED) &&
#488 DestRect.right >= lprc->right + dc->ptlDCOrig.x)
#489 {
#490 // We do the check '>=' instead of '>' to possibly save an iteration
#491 // through this loop, since it's breaking after the drawing is done,
#492 // and x is always incremented.
#493 DestRect.right = lprc->right + dc->ptlDCOrig.x;
#494 DoBreak = TRUE;
#495 }
#496
#497 IntEngMaskBlt(
#498 SurfObj,
#499 SourceGlyphSurf,
#500 dc->CombinedClip,
#501 XlateObj,
#502 XlateObj2,
#503 &DestRect,
#504 &SourcePoint,
#505 (PPOINTL)&MaskRect,
#506 &BrushFgInst.BrushObject,
#507 &BrushOrigin);
#508
#509 EngUnlockSurface(SourceGlyphSurf);
#510 EngDeleteSurface((HSURF)HSourceGlyph);
#511
#512 if (DoBreak)
#513 {
#514 break;
#515 }
#516
#517 if (NULL == Dx)
#518 {
#519 TextLeft += realglyph->advance.x >> 10;
#520 // DbgPrint("new TextLeft: %d/n", TextLeft);
#521 }
#522 else
#523 {
#524 TextLeft += Dx[i<<DxShift] << 6;
#525 // DbgPrint("new TextLeft2: %d/n", TextLeft);
#526 }
#527
#528 if (DxShift)
#529 {
#530 TextTop -= Dx[2 * i + 1] << 6;
#531 }
#532
#533 previous = glyph_index;
#534
#535 String++;
#536 }
#537
后面就是删除分配的资源。
#538 IntUnLockFreeType;
#539
#540 EngDeleteXlate(XlateObj);
#541 EngDeleteXlate(XlateObj2);
#542 SURFACE_UnlockSurface(psurf);
#543 if (TextObj != NULL)
#544 TEXTOBJ_UnlockText(TextObj);
#545 if (hBrushBg != NULL)
#546 {
#547 BRUSHOBJ_UnlockBrush(BrushBg);
#548 NtGdiDeleteObject(hBrushBg);
#549 }
#550 BRUSHOBJ_UnlockBrush(BrushFg);
#551 NtGdiDeleteObject(hBrushFg);
#552 good:
#553 DC_UnlockDc( dc );
#554
#555 return TRUE;
#556
#557 fail:
#558 if ( XlateObj2 != NULL )
#559 EngDeleteXlate(XlateObj2);
#560 if ( XlateObj != NULL )
#561 EngDeleteXlate(XlateObj);
#562 if (TextObj != NULL)
#563 TEXTOBJ_UnlockText(TextObj);
#564 if (psurf != NULL)
#565 SURFACE_UnlockSurface(psurf);
#566 if (hBrushBg != NULL)
#567 {
#568 BRUSHOBJ_UnlockBrush(BrushBg);
#569 NtGdiDeleteObject(hBrushBg);
#570 }
#571 if (hBrushFg != NULL)
#572 {
#573 BRUSHOBJ_UnlockBrush(BrushFg);
#574 NtGdiDeleteObject(hBrushFg);
#575 }
#576 DC_UnlockDc(dc);
#577
#578 return FALSE;
#579 }