From: Maël Hörz <ma...@us...> - 2008-02-02 15:13:41
|
Update of /cvsroot/synedit/SynEdit/Source In directory sc8-pr-cvs8.sourceforge.net:/tmp/cvs-serv19210/Source Modified Files: Tag: Unicode_2004_08_31 SynEdit.inc SynTextDrawer.pas SynUnicode.pas Added Files: Tag: Unicode_2004_08_31 SynUsp10.pas Log Message: - Introduction of UniScribe to fix TextOut-bugs and allows better Unicode support. It works on all plattforms, if UniScribe isn't available UniversalTextOut falls back to plain old ExtTextOut. - Speed improvements: Due to caching provided by UniScribe drawing and measuring of text is faster, this significantly speeds up selection and loading, though SynEdit still is not a speed deamon. Index: SynEdit.inc =================================================================== RCS file: /cvsroot/synedit/SynEdit/Source/SynEdit.inc,v retrieving revision 1.16.2.14 retrieving revision 1.16.2.15 diff -u -d -r1.16.2.14 -r1.16.2.15 --- SynEdit.inc 30 Jan 2008 20:41:59 -0000 1.16.2.14 +++ SynEdit.inc 2 Feb 2008 15:13:33 -0000 1.16.2.15 @@ -396,17 +396,4 @@ {$ENDIF} -// If the following statement is enabled SynTextDrawer uses a replacement for -// ExtTextOut that is based on DrawText. This fixes a bug of (Ext)TextOut which -// does or doesn't display some characters depending on the preceding -// characters. However as this is an emulation it is a little slower and causes -// more flicker since drawing has to be done in several steps. The best solution -// would be to use UniScribe which would unfortunately require a major/near -// total rewrite of SynEdit. -// -// See UniversalExtTextOut in SynTextDrawer.pas and the link below for details -// on the Windows-API bug: -// http://groups.google.com/group/microsoft.public.win32.programmer.international/browse_thread/thread/77cd596f2b96dc76/146300208098285c?lnk=st&q=font+substitution+problem#146300208098285c -{$DEFINE SYN_FIXTEXTOUTBUG} - // $Id$ Index: SynUnicode.pas =================================================================== RCS file: /cvsroot/synedit/SynEdit/Source/Attic/SynUnicode.pas,v retrieving revision 1.1.2.34 retrieving revision 1.1.2.35 diff -u -d -r1.1.2.34 -r1.1.2.35 --- SynUnicode.pas 30 Jan 2008 20:41:59 -0000 1.1.2.34 +++ SynUnicode.pas 2 Feb 2008 15:13:33 -0000 1.1.2.35 @@ -403,6 +403,7 @@ QSynEditTextBuffer, {$ELSE} SynEditTextBuffer, + SynUsp10, {$ENDIF} Math, {$IFDEF SYN_LINUX} @@ -2270,29 +2271,44 @@ {$ENDIF} function GetTextSize(DC: HDC; Str: PWideChar; Count: Integer): TSize; +const + SSAnalyseFlags = SSA_GLYPHS or SSA_FALLBACK; var tm: TTextMetricA; - r: TRect; + GlyphBufferSize: Integer; + saa: TScriptStringAnalysis; + lpSize: PSize; begin Result.cx := 0; Result.cy := 0; - if Win32PlatformIsUnicode then + + if Usp10IsInstalled then begin - r := Rect(0, 0, 0, 0); - DrawTextW(DC, Str, Count, r, DT_CALCRECT or DT_SINGLELINE or DT_NOPREFIX or - DT_NOCLIP); - Result.cx := r.Right; - Result.cy := r.Bottom; + // According to the MS Windows SDK (1.5 * Count + 16) is the recommended + // value for GlyphBufferSize (see documentation of cGlyphs parameter of + // ScriptStringAnalyse function) + GlyphBufferSize := (3 * Count) div 2 + 16; + + if Succeeded(ScriptStringAnalyse(DC, Str, Count, GlyphBufferSize, -1, + SSAnalyseFlags, 0, nil, nil, nil, nil, nil, @saa)) then + begin + lpSize := ScriptString_pSize(saa); + if lpSize <> nil then + Result := lpSize^; + ScriptStringFree(@saa); + end; end else - GetTextExtentPoint32W(DC, Str, Count, Result); - if not Win32PlatformIsUnicode then begin - GetTextMetricsA(DC, tm); - if tm.tmPitchAndFamily and TMPF_TRUETYPE <> 0 then - Result.cx := Result.cx - tm.tmOverhang - else - Result.cx := tm.tmAveCharWidth * Count; + GetTextExtentPoint32W(DC, Str, Count, Result); + if not Win32PlatformIsUnicode then + begin + GetTextMetricsA(DC, tm); + if tm.tmPitchAndFamily and TMPF_TRUETYPE <> 0 then + Result.cx := Result.cx - tm.tmOverhang + else + Result.cx := tm.tmAveCharWidth * Count; + end; end; end; Index: SynTextDrawer.pas =================================================================== RCS file: /cvsroot/synedit/SynEdit/Source/SynTextDrawer.pas,v retrieving revision 1.6.2.12 retrieving revision 1.6.2.13 diff -u -d -r1.6.2.12 -r1.6.2.13 --- SynTextDrawer.pas 30 Jan 2008 20:41:59 -0000 1.6.2.12 +++ SynTextDrawer.pas 2 Feb 2008 15:13:33 -0000 1.6.2.13 @@ -207,9 +207,6 @@ // Begin/EndDrawing calling count FDrawingCount: Integer; protected - function UniversalExtTextOut(DC: HDC; X, Y: Integer; - Options: TTextOutOptions; Rect: TRect; Str: PWideChar; Count: Integer; - ETODist: PIntegerArray): Boolean; procedure ReleaseETODist; virtual; procedure AfterStyleSet; virtual; procedure DoSetCharExtra(Value: Integer); virtual; @@ -253,6 +250,9 @@ implementation +uses + SynUsp10; + var gFontsInfoManager: TheFontsInfoManager; @@ -270,145 +270,42 @@ if x < y then Result := x else Result := y; end; -// UniversalExtTextOut tries to offer uniform behaviour accross platforms. -// Additionally on Win NT, 2000 and up DrawText is used to work around a bug -// when displaying a combination of Chinese and Korean text using (Ext)TextOut -// (there might be other combinations that trigger this WinAPI-bug, but -// DrawText hopefully handles them fine). +// UniversalExtTextOut uses UniScribe where available for the best possible +// output quality. This also avoids a bug in (Ext)TextOut that surfaces when +// displaying a combination of Chinese and Korean text. // // See here for details: http://groups.google.com/group/microsoft.public.win32.programmer.international/browse_thread/thread/77cd596f2b96dc76/146300208098285c?lnk=st&q=font+substitution+problem#146300208098285c -function TheTextDrawer.UniversalExtTextOut(DC: HDC; X, Y: Integer; - Options: TTextOutOptions; Rect: TRect; Str: PWideChar; Count: Integer; - ETODist: PIntegerArray): Boolean; +function UniversalExtTextOut(DC: HDC; X, Y: Integer; Options: TTextOutOptions; + Rect: TRect; Str: PWideChar; Count: Integer; ETODist: PIntegerArray): Boolean; const - // Default options to make DrawText behave as (Ext)TextOut - OptionFlags: UINT = DT_SINGLELINE or DT_NOPREFIX or DT_NOCLIP; -{$IFDEF SYN_FIXTEXTOUTBUG} + SSAnalyseFlags = SSA_GLYPHS or SSA_FALLBACK; var - TextEnclosingRect, TextSnippetRect: TRect; - SurroundingRgn, BackFillRgn, OldClipRgn: HRGN; - Brush: HBRUSH; - TextSnippetLength, i: Integer; - SavedBkMode: Integer; -{$ENDIF} + TextOutFlags: DWORD; + GlyphBufferSize: Integer; + saa: TScriptStringAnalysis; begin -{$IFDEF SYN_FIXTEXTOUTBUG} - if Win32PlatformIsUnicode then - begin - Result := True; - if tooClipped in Options then - begin - OldClipRgn := CreateRectRgn(0, 0, 0, 0); - if GetClipRgn(DC, OldClipRgn) <> 1 then - begin - DeleteObject(OldClipRgn); - OldClipRgn := 0; - end; - IntersectClipRect(DC, Rect.Left, Rect.Top, Rect.Right, Rect.Bottom); - end - else - OldClipRgn := INVALID_HANDLE_VALUE; - - // determine the smallest rect enclosing the text - TextEnclosingRect := Classes.Rect(X, Y, X, Y); - if ETODist = nil then - Result := Result and (DrawTextW(DC, Str, Count, TextEnclosingRect, - OptionFlags or DT_CALCRECT) <> 0) - else - begin - for i := 0 to Count - 1 do - inc(TextEnclosingRect.Right, ETODist^[i]); - inc(TextEnclosingRect.Bottom, CharHeight); - end; - - // Fill the region occupied by the text with the current background color, - // and if tooOpaque is in Options fill also the surrounding rect defined by - // the parameter Rect. - BackFillRgn := CreateRectRgnIndirect(TextEnclosingRect); - if tooOpaque in Options then - begin - SurroundingRgn := CreateRectRgnIndirect(Rect); - CombineRgn(BackFillRgn, BackFillRgn, SurroundingRgn, RGN_OR); - DeleteObject(SurroundingRgn); - end; - Brush := CreateSolidBrush(GetBkColor(DC)); - FillRgn(DC, BackFillRgn, Brush); - DeleteObject(Brush); - DeleteObject(BackFillRgn); - - // set background mode to transparent, such that several calls to DrawText - // can be made without clipping already drawn text - SavedBkMode := SetBkMode(DC, TRANSPARENT); - i := 0; - while Count > 0 do - begin - // Text is drawn in snippets if for each character a width is specified - // (i.e. if ETODist <> nil). A snippet contains a sequence of characters - // with the same width which are drawn with one call of DrawText. - // - // This is necessary since there is no way to specify the width of each - // character individually for DrawText as this is possible for ExtTextOut. - if ETODist <> nil then - begin - TextSnippetRect := Classes.Rect(X, TextEnclosingRect.Top, X, - TextEnclosingRect.Bottom); - if ETODist^[i] = CharWidth then - begin - TextSnippetLength := 0; - while (ETODist^[i] = CharWidth) and (TextSnippetLength < Count) do - begin - inc(TextSnippetLength); - inc(i); - end; - inc(TextSnippetRect.Right, TextSnippetLength * CharWidth); - end - else - // Characters of different width than CharWidth must be drawn - // individually, since it is the only way to ensure correct spacing. We - // could use SetTextCharacterExtra but it would require that the real - // character width is measured to determine how much to add for each - // character and which characters can then be put into a text snippet. - // Drawing each character individually is faster than doing such complex - // things. - begin - TextSnippetLength := 1; - inc(TextSnippetRect.Right, TextSnippetLength * ETODist^[i]); - inc(i); - end; - end - else - begin - TextSnippetLength := Count; - TextSnippetRect := TextEnclosingRect; - end; - - Result := Result and (DrawTextW(DC, Str, TextSnippetLength, - TextSnippetRect, OptionFlags) <> 0); - - inc(X, TextSnippetRect.Right - TextSnippetRect.Left); - inc(Str, TextSnippetLength); - dec(Count, TextSnippetLength); - end; + TextOutFlags := 0; + if tooOpaque in Options then + TextOutFlags := TextOutFlags or ETO_OPAQUE; + if tooClipped in Options then + TextOutFlags := TextOutFlags or ETO_CLIPPED; - if tooClipped in Options then - begin - SelectClipRgn(DC, OldClipRgn); - DeleteObject(OldClipRgn); - end; + if Usp10IsInstalled then + begin + // According to the MS Windows SDK (1.5 * Count + 16) is the recommended + // value for GlyphBufferSize (see documentation of cGlyphs parameter of + // ScriptStringAnalyse function) + GlyphBufferSize := (3 * Count) div 2 + 16; - if SavedBkMode <> 0 then - SetBkMode(DC, SavedBkMode); + Result := Succeeded(ScriptStringAnalyse(DC, Str, Count, GlyphBufferSize, -1, + SSAnalyseFlags, 0, nil, nil, Pointer(ETODist), nil, nil, @saa)); + Result := Result and Succeeded(ScriptStringOut(saa, X, Y, TextOutFlags, + @Rect, 0, 0, False)); + Result := Result and Succeeded(ScriptStringFree(@saa)); end else -{$ENDIF} begin - OptionFlags := 0; - if tooOpaque in Options then - OptionFlags := OptionFlags or ETO_OPAQUE; - if tooClipped in Options then - OptionFlags := OptionFlags or ETO_CLIPPED; - - Result := ExtTextOutW(DC, X, Y, OptionFlags, @Rect, Str, Count, + Result := ExtTextOutW(DC, X, Y, TextOutFlags, @Rect, Str, Count, Pointer(ETODist)); end; end; @@ -943,7 +840,7 @@ r: TRect; begin r := Rect(X, Y, X, Y); - UniversalExtTextOut(FDC, X, Y, [], r, Text, Length, nil); // TODO: FETODist wird in Universal verwendet aber hier nicht initialisiert + UniversalExtTextOut(FDC, X, Y, [], r, Text, Length, nil); end; procedure TheTextDrawer.ExtTextOut(X, Y: Integer; Options: TTextOutOptions; --- NEW FILE: SynUsp10.pas --- {******************************************************************************} {* *} {* Copyright (c) Microsoft Corporation. All rights reserved. *} {* *} {* File: usp10.h *} {* Content: USP - Unicode Complex Script processor *} {* *} {* Delphi / FreePascal adaptation by Alexey Barkovoy (cl...@cl...) *} (* *) {* The original version from Alexey Barkovoy can be downloaded from: *} {* http://clootie.ru *} (* *) (* Dynamic linking logic (similar to what the JCL does) by Maël Hörz. *) (* *) (* Latest version can be downloaded from http://mh-nexus.de/unisynedit.htm *) (* or checked out from the Unicode branch of SynEdit CVS. *) (* *) {******************************************************************************} { } [...2579 lines suppressed...] _ScriptApplyDigitSubstitution: Pointer; function ScriptApplyDigitSubstitution; begin GetProcedureAddress(_ScriptApplyDigitSubstitution, Usp10DLL, 'ScriptApplyDigitSubstitution'); asm MOV ESP, EBP POP EBP JMP [_ScriptApplyDigitSubstitution] end; end; initialization Usp10DllModule := GetUsp10DllModule; Usp10IsInstalled := Usp10DllModule <> 0; finalization if Usp10DllModule <> 0 then FreeLibrary(Usp10DllModule); end. |