MeasureString und DrawString - GDI auf die harte Tour
-
WishfulThinking schrieb:
MM_TEXT
hatte ich gewählt, weil dieses Maßsystem in GDI und GDI+ gleich zu sein scheint; für die anderen Koordinatensysteme sah ich zwischen GDI und GDI+ eben keine Übereinstimmungen. Welches alternative Koordinatensystem würdest du vorschlagen?Dass GetABCCharWidthsFloat im Maßsystem MM_TEXT nur ganzzahlige Werte zurückgibt, habe ich in meiner Quelle (http://msdn.microsoft.com/en-us/library/dd144858%28v=vs.85%29.aspx) nicht nachvollziehen können. Hast du dazu eine andere Quelle?
Das steht in dem Buch, das ich erwähnt habe, Windows Graphics Programming. Das geht sehr detailliert auf viele Themen bezüglich GDI ein.
Du kriegst die Infos mit GetABCCharWidthsFloat nicht genauer raus (steht auch in dem Buch). Das sind nur Floats, damit du in anderen Koordinatensystemen auch sinnvolle Werte bekommst. Also, wenn du sagst, du willst die Breite in Metern, dann bringt dir ein Integer nichts. Du kriegst einen Float, der auf Pixel gerundet ist. Aber eben nicht genauer als Pixel.
Edit: hier kannst du keine Bilder anhängen, aber du kannst irgendeinen Imagehoster verwenden und hier einen Link reinposten.
-
Der in meinem vorangegangenen Posting erwähnte Artikel hat mir ein sehr gutes Stück weiter geholfen. Hier meine bisherigen Erkenntnisse:
- GDI+ kann nicht als Ersatz für GDI angesehen werden -- GDI+ ist eine alternative Methode zur Ausgabe von Grafik.
- GDI funktioniert pixelorientiert und damit unbedingt abhängig von dem Ausgabe-Device bzw. der gewählten Auflösung. Zeichen werden immer in ein ganzzahliges Geviert von Pixeln aufgelöst, deshalb sind Höhe und Breite immer nur Integer-Werte. So kommen bei höherer Auflösung natürlich sehr viel genauere Konturen zu Stande, weil dann eine größere Annäherung an das Design-Raster erzielt wird. Damit ändert sich die Breite eines ausgegebenen Textes nicht einfach linear mit der Auflösung oder der Schriftgröße.
- GDI+ löst seine Ausgaben auch in Bruchteilen von Pixeln auf; die Ausgaberesultate werden damit (weitgehend) unabhängig von der Auflösung oder Schriftgröße und damit auch unabhängig von der Ausgabe-Device.
Na schön, damit wäre erklärt, dass ich GDI-Ausgaben nicht mit GDI+-Ausgaben vergleichen kann.
In GDI kann ich tatsächlich nur pixelgenau messen und positionieren, etwas anderes macht in diesem System auch keinen Sinn. Die gemessene Breite eines Zeichens (z.B. "M"), mal zehn genommen, entspricht auch genau der gemessenen Breite eines String mit zehn "M".
Als nächstes werde ich mal versuchen, ähnliche Resultate in GDI+ zu erzielen, nur eben auf Bruchteile von Pixeln genau.Editiert: fett gedruckte Stellen
-
Klasse Zusammenfassung, das habe ich endlich mal verstanden.
In Zukunft benutze ich nur noch GDI
-
Vorsicht -- das kann nach hinten losgehen!
Die Beschränkung auf auflösungsabhängige Ausgaben (oder auch: Device-abhängig) bedeutet, dass man kein Wysiwyg hat. Auf dem Bildschirm mit z.B. 96 dpi bekommt man dann vollkommen andere Textumbrüche als auf dem Drucker, der mit 300, 600 oder sogar mit 1200 dpi arbeitet.
Aus diesem Grund würde ich - wenn nur irgendwie nachvollziehbar möglich - lieber GDI+ verwenden. Dann würden meine Texte auf dem Bildschirm UND auf dem Drucker praktisch vollkommen übereinstimmen.
-
Ich bin nicht sicher, wie du das meinst, aber ich denke, das ist im Endeffekt egal. Wenn du die Daten irgendwie "speicherst", dann solltest du eigene Einheiten verwenden, die möglichst genau und geräteunabhängig auflösen, z.B. in Bruchteilen von mm.
Aber wenns dann ums "Zeichnen" geht, versteh ich nicht, wo du hier WYSIWYG siehst oder nicht siehst. Wenn du die Ausgabe für den Drucker berechnest, hast du ein anderes Device Context, als wenn du die für den Bildschirm berechnest. Oder wolltest du die für den Bildschirm voberechnen und dann für den den Drucker einfach hochskalieren? Das wäre in der Tat nicht sinnvoll.
-
@Mechanics
Zwischen Speichern (bzw. Laden) und Zeichnen muss man erstmal noch das Layout erstellen.Wenn dieser Teil über GDI implementiert ist, dann ist er deviceabhängig, und dadurch kann es passieren dass für den Schirm z.B. Zeilenumbrüche anders gesetzt werden als für den Drucker. Das ist dann doof.
Deswegen muss die Layout-/Flow-Engine deviceunabhängig arbeiten.
Und wenn die Layout-/Flow-Engine deviceunabhängig arbeitet, dann muss auch der Renderer/Rasterizer deviceunabhängig arbeiten. Weil er sonst u.U. den Text gar nicht in den von der Layout-/Flow-Engine berechneten Platz reinbringt. Oder viel zu wenig braucht. Was dann beides doof aussieht.
@WishfulThinking
Ich denke du hast dich da verschrieben:GDI funktioniert pixelorientiert und damit unbedingt von der Ausgabe-Device.
Ich würde hier "abhängig" schreiben. "unbedingt" macht hier für mich keinen Sinn. Wenn dann würde ich "unbedingt" als "unabhängig" interpretieren, und das ist es ja gerade nicht.
Und "Device" wird im Deutschen üblicherweise als Neutrum verwendet. Also "das Device", nicht "die Device"
-
hustbaer schrieb:
@Mechanics
Zwischen Speichern (bzw. Laden) und Zeichnen muss man erstmal noch das Layout erstellen.Ich seh das Layouten erstmal als Teil des Zeichnens. Je nachdem, um welche Datenmengen es geht, würde ich es dann direkt beim Zeichnen machen, device abhängig. Wenn man aber tatsächlich sowas wie eine Textverarbeitung schreibt, die das Layout mitspeichert und unabhängig von der Ausgabe wissen muss, wieviele Seiten es gibt usw., dann muss man das Layout natürlich geräteunabhängig bestimmen.
-
Mechanics schrieb:
Je nachdem, um welche Datenmengen es geht, würde ich es dann direkt beim Zeichnen machen, device abhängig.
Dann kann es sein dass das Dokument im Editor anders aussieht als auf dem Ausdruck.
Und das bedeutet: du hast kein WYSIWYG mehr. Genau das was WishfulThinking geschrieben hat.Ich verstehe nicht wo hier kein Bezug zu WYSIWYG zu sehen sein soll
-
@WishfulThinking
Guck dir mal GetCharacterPlacement an.Ansonsten gäbe es noch die Uniscribe API und natürlich DirectWrite
-
hustbaer schrieb:
@WishfulThinking
Ich denke du hast dich da verschrieben:GDI funktioniert pixelorientiert und damit unbedingt von der Ausgabe-Device.
Ich würde hier "abhängig" schreiben. "unbedingt" macht hier für mich keinen Sinn. Wenn dann würde ich "unbedingt" als "unabhängig" interpretieren, und das ist es ja gerade nicht.
Und "Device" wird im Deutschen üblicherweise als Neutrum verwendet. Also "das Device", nicht "die Device"Danke, ich habe mich tatsächlich verschrieben. Es hätte heißen sollen "unbedingt deviceabhängig". Das werde ich der Klarkeit halber im alten Posting editieren.
Und noch danke für den zweiten Rechtschreibtipp. Ein Device ist eine Sache, sogar in der deutschen Sprache. Das war fehlerhafte Anwendung von Neu-Hochdeutsch.Die Funktion
GetCharacterPlacement
habe ich mir bereits angesehen. Das bringt die Angelegenheit leider nicht weiter wegen der Begrenzung der GDI-Auflösung auf ganze Pixel.Die beiden anderen Funktionen werde ich mir noch genauer ansehen.
Trotzdem bleibt die Frage erst einmal offen: Wie hat z.B. WORD das Problem gelöst? Da haben wir ganz offensichtlich ein deviceunabhängiges Layout. UniScribe und DirectWrite sind deutlich neuer als WORD.
(Ich habe nicht den Ehrgeiz, einen neuen Texteditor zu schreiben, ich möchte einfach nur dasselbe Layout für verschiedene Devices (z.B. Bildschirm und Drucker) verwenden können.)
-
WishfulThinking schrieb:
(Ich habe nicht den Ehrgeiz, einen neuen Texteditor zu schreiben, ich möchte einfach nur dasselbe Layout für verschiedene Devices (z.B. Bildschirm und Drucker) verwenden können.)
Dann guck dir mal DirectWrite an. Ich kenn mich damit nicht gut genug aus um sagen zu können ob man damit an die "detailierten" Layout-Infos drankommt die man draucht um z.B. einzelne Buchstaben innerhalb eines Wortes anders einfärben zu können, aber ich würde schätzen dass es geht.
WishfulThinking schrieb:
Wie hat z.B. WORD das Problem gelöst? Da haben wir ganz offensichtlich ein deviceunabhängiges Layout. UniScribe und DirectWrite sind deutlich neuer als WORD.
Word hat mit an Sicherheit grenzender Wahrscheinlichkeit ne eigene Layout-Engine die die nötigen Infos direkt aus den Fonts rauszieht und halt alles "zu fuss" macht.
Internet Explorer hat damals zu Windows 98 Zeiten soweit ich weiss Uniscribe verwendet.
Auf jeden Fall ist Uniscribe mit dem IE mit installiert worden, wodurch der IE dann auch auf z.B. Windows 95 oder 98 Unicode Text korrekt darstellen konnte (inklusive Arabisch und andere "Complex-Scripts" -- wohingegen die GDI Funktionen von Windows 95/98 mit arabsich genau nix anfangen können, denen fehlt der Layout Code für die komplexen Ligaturen/Substitutions die Arabisch benötigt).
Wobei ich heute nix mehr mit Uniscribe anfangen würde. Die Library ist schon sehr alt, und falls es mit DirectWrite geht gehe ich davon aus dass es damit angenehmer und vermutlich auf performanter geht.
(Wobei IE natürlich nicht Device-independent layouten muss. Ist nur das einzige Programm wo ich eine solide Grundlage habe Vermutungen darüber anzustellen welche API zum Layouten verwendet wurde.)Oder du schreibst eben deinen eigenen Layout-Code. So lange du kein RTL und keine Complex-Scripts unterstützen musst ist das auch nicht SO schwer. Im Prinzip brauchst du dazu nur die ABC Widths und die Kerning-Pairs. Grid-Fitting und Hinting brauchst du ja nicht zu berücksichtigen wenn du Device-independent layouten willst.
Und Rendern kannst du dann mit GDI+, FreeType, DirectWrite - ziemlich egal so lange es nur eine Lib ist wo man Grid-Fitting abdrehen kann (und idealerweise Subpixel Koordinaten übergeben).
-
WishfulThinking schrieb:
Ein Device ist eine Sache, sogar in der deutschen Sprache. Das war fehlerhafte Anwendung von Neu-Hochdeutsch.
Ja, in dem Fall stimmt das üblicherweise verwendete Geschlecht für "Device" mit dem der deutschen Übersetzung "das Gerät" überein. Wobei es bei der Übersetzung "der Apparat" schon nicht mehr passt.
Das Geschlecht der Übersetzung ist nicht immer anwendbar, "stimmen" tut letztlich das was von der Mehrzahl als "nicht falsch" angesehen wird
-
Naja, ich glaube, dies ist wohl eher ein WinAPI-Forum
Inzwischen habe ich mir schon einmal oberflächlich die
DirectWrite
-Umgebung angesehen. Ich gestehe, dieses Kapitel habe ich bisher aus Bequemlichkeit (die Schnittstelle ist ja um einiges umfangreicher als die von GDI) für mich ausgeblendet. Jetzt sehe ich aber schon, dass ich da wohl alle gewünschten Fontfamilien, Fontgrößen, Farben u.s.w. einstellen kann.Eine Layout-Engine, die auch Formatierungskommandos versteht, zu schreiben ist tatsächlich kein Hexenwerk, das habe ich in mehreren Varianten bereits mit GDI gemacht. Diese Engine hat allerdings den Nachteil der Auflösungsabhängigkeit/Geräteabhängigkeit, das möchte ich jetzt mit einer neuen Engine, wahrscheinlich sogar auf der Basis von DirectWrite, lösen. Das wird erst einmal einige kleine Versuche und anschließend einige größere Code-Umstellungen erfordern, aber es scheint die Mühe wert.
GDI+ schein übrigens keine geeignete Basis für meine Layout-Engine zu sein. Nach der ersten Durchsicht der DirectWrite-Umgebung sehe ich jetzt auch ein, dass eine Zeile immer in einem Zuge geschrieben werden muss, weil es sonst in den Pixel-Spalten an der Grenze zwischen zwei Schreibvorgängen zu Konflikten kommt.
-
WishfulThinking schrieb:
Nach der ersten Durchsicht der DirectWrite-Umgebung sehe ich jetzt auch ein, dass eine Zeile immer in einem Zuge geschrieben werden muss, weil es sonst in den Pixel-Spalten an der Grenze zwischen zwei Schreibvorgängen zu Konflikten kommt.
Verstehe ich jetzt nicht ganz. Wo siehst du da ein Problem?
Die Berechnung der Positionen für die einzelnen Glyphen muss in einem Rutsch gemacht werden, ja. Aber rausschreiben kann man die dann hübsch einzeln.
Natürlich ist das Ergebnis theoretisch genauer wenn man erstmal die Vektordarstellung der ganzen Zeile erstellt, und dann alles auf einmal rasterized. Das macht aber keiner*, weil es viel zu langsam ist. Macht auch DirectWrite nicht.
Und der Unterschied wird, wenn überhaupt mit freiem Auge sichtbar, vermutlich wirklich minimalst sein.*: Vermutlich gibt es Programme die doch genau das machen. Bzw. dann vermutlich gleich die ganze Seite auf einmal. Vielleicht Programme wie Corel-Draw und vergleichbare. Ich meine: keine normale Text-Rendering Engine wie sie für Text-Editoren, Browser etc. verwendet wird. Eben weil viel zu langsam.
-
vieleicht hilfts:
/* =============================================================================== MeassureText_NitasTrickMethod =============================================================================== */ void MeassureText_NitasTrickMethod(long *width, long *height, char *text, HDC hDCMem){ SIZE sizeText, sizeLastCharacter; RECT rect; HBITMAP hBitmap, hOldBitmap; int i, iXmax, iYmed; boolean bFound; GetTextExtentPoint32(hDCMem, text, lstrlen(text), &sizeText); *height = sizeText.cy; *width = sizeText.cx; if((text == NULL) || (text[0] == '\0')){ TEXTMETRIC tm; GetTextMetrics(hDCMem,&tm); *height = tm.tmHeight; return; } GetTextExtentPoint32(hDCMem, &text[-1+lstrlen(text)], 1, &sizeLastCharacter); rect.left = 0; rect.top = 0; rect.right = *width + sizeLastCharacter.cx; rect.bottom = *height; hBitmap= CreateCompatibleBitmap(hDCMem, rect.right-rect.left, rect.bottom-rect.top); hOldBitmap = (HBITMAP)SelectObject(hDCMem, hBitmap); FillRect(hDCMem, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); SetBkColor(hDCMem,RGB(0,0,0)); DrawText(hDCMem, text, -1, &rect, (DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX)); iXmax = 0; bFound = FALSE; iYmed = (rect.bottom + rect.top)/2; for(i = rect.right-1; i >= 0 && !bFound; i--){ COLORREF rgbColor = GetPixel(hDCMem, i, iYmed); if(rgbColor != RGB(255,255,255)){ iXmax = i; bFound = TRUE; } } *width = iXmax + 1; SelectObject(hDCMem, hOldBitmap); DeleteObject(hBitmap); } /* =============================================================================== MeassureText_SmartMethod =============================================================================== */ void MeassureText_SmartMethod(long *width, long *height, char *text, HDC hDCMem){ SIZE sizeText, sizeLastCharacter; RECT rect; boolean bFound; int x, y, iXmax; HBITMAP hBitmap, hOldBitmap; GetTextExtentPoint32(hDCMem, text, lstrlen(text), &sizeText); *height = sizeText.cy; //height==0 if the text is empty, so try GetTextMetrics below! *width = sizeText.cx; if((text == NULL) || (text[0] == '\0')){ TEXTMETRIC tm; GetTextMetrics(hDCMem,&tm); *height = tm.tmHeight; return; } GetTextExtentPoint32(hDCMem, &text[lstrlen(text) - 1], 1, &sizeLastCharacter); rect.left = 0; rect.top = 0; rect.right = *width + sizeLastCharacter.cx; rect.bottom = *height; hBitmap = CreateCompatibleBitmap(hDCMem, rect.right-rect.left, rect.bottom-rect.top); hOldBitmap = (HBITMAP)SelectObject(hDCMem, hBitmap); FillRect(hDCMem, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); DrawText(hDCMem, text, -1, &rect, (DT_LEFT | DT_TOP | DT_SINGLELINE | DT_NOPREFIX)); iXmax = 0; bFound = FALSE; for(x = rect.right-1; x >= 0 && !bFound; x--){ for(y = 0; y <= rect.bottom-1 && !bFound; y++){ COLORREF rgbColor = GetPixel(hDCMem,x,y); if(rgbColor != RGB(255,255,255)){ iXmax = x; bFound = TRUE; } } } *width = iXmax + 1; SelectObject(hDCMem, hOldBitmap); DeleteObject(hBitmap); } /* =============================================================================== MeassureText_ClassicMethod =============================================================================== */ void MeassureText_ClassicMethod(long *width, long *height, char *text, HDC hDC, double *dOverhangTrailing, boolean bAdjustOverhang){ SIZE sizeText; TEXTMETRIC tm; GetTextExtentPoint32(hDC, text, lstrlen(text), &sizeText); *height = sizeText.cy; *width = sizeText.cx; GetTextMetrics(hDC,&tm); *height = tm.tmHeight; if(lstrlen(text) == 0){ return; } if(bAdjustOverhang){ ABCFLOAT WidthsABC[256]; boolean bResult; *dOverhangTrailing = 0; bResult = GetCharABCWidthsFloat(hDC,0,255, WidthsABC); if(bResult){ *dOverhangTrailing = WidthsABC[text[lstrlen(text)-1]].abcfC; if(*dOverhangTrailing < 0){ *width -= (LONG)*dOverhangTrailing; } } } } /* =============================================================================== MeassureText_FontHandle misst die Textlänge und Breite, es wird ein Handle auf einen Font benötigt =============================================================================== */ boolean MeassureText_FontHandle(long *width, long *height, char *text, HFONT hFont, u_int method){ HDC hDC, hDCMem; HFONT hFontOld; hDC = GetDC(NULL); switch(method){ case MESSURETEXT_NITASTRICK: hDCMem = CreateCompatibleDC(hDC); hFontOld = (HFONT)SelectObject(hDCMem, hFont); MeassureText_NitasTrickMethod(width, height, text, hDCMem); SelectObject(hDCMem, hFontOld); DeleteDC(hDCMem); break; case MESSURETEXT_SMART: hDCMem = CreateCompatibleDC(hDC); hFontOld = (HFONT)SelectObject(hDCMem, hFont); MeassureText_SmartMethod(width, height, text, hDCMem); SelectObject(hDCMem, hFontOld); DeleteDC(hDCMem); break; case MESSURETEXT_CLASSIC:{ hFontOld = (HFONT)SelectObject(hDC, hFont); MeassureText_ClassicMethod(width, height, text, hDC, 0, 0); SelectObject(hDC, hFontOld); break; } } ReleaseDC(NULL, hDC); return TRUE; } /* =============================================================================== MeassureText_LogFont =============================================================================== */ boolean MeassureText_LogFont(long *width, long *height, char *text, LOGFONT *lf, u_int method){ HFONT hFont; boolean back; hFont = CreateFontIndirect(lf); if(!hFont){ return FALSE; } back = MeassureText_FontHandle(width, height, text, hFont, method); DeleteObject(hFont); return back; }
-
hustbaer schrieb:
Ich verstehe nicht wo hier kein Bezug zu WYSIWYG zu sehen sein soll
Naja, das Dokument schaut auf dem Bildschirm so gut aus, wie es auf dem Bildschirm ausschauen kann, weil pixelgenau gerechnet wird. Auf dem Drucker schaut es so gut aus, wie es auf dem Drucker ausschauen kann, weil die Pixel kleiner sind. Aber das Problem hat man ja grundsätzlich. Das ist immer noch WYSIWYG. Aber ich besteh nicht drauf Entweder steh ich komplett auf dem Schlauch oder das ist Interpretationssache.
Bei DirectWrite sind einige interessante Funktionen erst mit Windows 8 reingekommen, das würde den Nutzerkreis evtl. einschränken.
-
WYSIWYG heisst in dem Zusammenhang dass du das bekommst (=der Ausdruck) was du im Editor (=am Bildschirm) siehst.
Wenn das Layouting deviceabhängig ist, und dadurch auf dem Ausdruck die Zeilenumbrücke/Seitenumbrüche etc. anders sind ... dann ist das ja wohl ganz klar und krass überhaupt nicht mehr das selbe.
Und kann daher auch kein WYSIWYG sein.Und nur damit wir uns nicht falsch verstehen...
Es kann durch deviceabhängigs Layouting sein dass es am Bildschirm so aussieht:The quick brown| fox jumped over| the lazy dog. |
Und beim Drucker kommt das dann so raus:
The quick | brown fox | jumped over | the lazy dog. |
Dadurch kann sich *alles* ändern was die Layout-/Flow-Engine so "flowt".
Und da check grad echt nicht wie man das noch als WYSIWYG bezeichnen wollen würde
-
Ok, dass dabei mehr oder weniger Zeilen rauskommen können, habe ich nicht berücksichtigt, da hast du Recht. Ich dachte bisher immer an etwas unsauberes Kerning, aber nicht daran, dass der Unterschied natürlich so groß sein kann, dass die Zeile umbrochen wird.
-
@.-)
.-) schrieb:
vieleicht hilfts:
Ja, schon möglich, dass ich gute Messergebnisse für die Textbreite bekomme. Aber das alles hilft nichts, wenn ich nicht - nachvollziehbar - dasselbe Ausgabeergebnis bekomme.
hustbaer schrieb:
Verstehe ich jetzt nicht ganz. Wo siehst du da ein Problem?
Die Berechnung der Positionen für die einzelnen Glyphen muss in einem Rutsch gemacht werden, ja. Aber rausschreiben kann man die dann hübsch einzeln.Nein, das würde leider nicht zufriedenstellend funktionieren.
Wenn ein Glyph innerhalb einer Pixelspalte endet und der nächste Glyph unmittelbar daran anschließend geschrieben werden soll, weiß der Renderer nicht, was beim vorangegangenen Glyph geschrieben wurde und beschreibt die Pixelspalte so, als ob der vorangegangene Rendering-Vorgang nicht stattgefunden hätte.
-
WishfulThinking schrieb:
Wenn ein Glyph innerhalb einer Pixelspalte endet und der nächste Glyph unmittelbar daran anschließend geschrieben werden soll, weiß der Renderer nicht, was beim vorangegangenen Glyph geschrieben wurde und beschreibt die Pixelspalte so, als ob der vorangegangene Rendering-Vorgang nicht stattgefunden hätte.
Meinst du den Fall wo...
* Der nachfolgende Glyph "nahtlos" an den vorhergehenden anschliessen soll
* Die Trennlinie mitten in einem Pixel liegt (statt genau zwischen zwei Pixeln)
* Man ohne Grid-Fitting rendert
...und dadurch dann kein Nahtloser Zusammenschluss erfolgt? Sondern man statt dessen eine nicht vollständig schwarze Trennlinie bekommt.Der Fall ist in der Tat doof.
Beheben liesse sich das indem man ala 3D Karten mit FSAA rendert. Also z.B. die 4-fache Auflösung horizontal und vertikal verwendet, dafür ohne klassischem Antialiasing, und dann zum schluss das gesamte Bild runterskaliert. Dabei könnte man die gerasterten Glyphen auch cachen. Die subpixel Genauigkeit bei der Positionierung wäre dann natürlich durch den Oversampling-Faktor begrenzt, aber ich denke das sollte kein Problem sein. Und vermutlich ist es performanter mit 16-facher (4x4) Auflösung Glyphen aus nem Cache zu kopieren als alles jedes mal neu zu rastern.
Bzw. eine "nicht perfekt aber vermutlich gut genug" Lösung wäre wenn man die Pixel einfach additiv blendet (bzw. subtraktiv - additiv wenn man den Hintergrund mit 0.0 abbildet und die Glyphen mit 1.0, subtraktiv wenn man es ala "schwarz auf weiss" umgekehrt macht). Wobei der Renderer dazu natürlich halbwegs gute und vor allem lineare (=nicht Gamma-korrigierte) Coverage-Werte ausspucken muss.
Aber ist schon irgendwie interessant wie nicht-trivial sowas triviales wie Anzeige von Text sein kann, wenn man es "ordentlich" machen will