MeasureString und DrawString - GDI auf die harte Tour



  • 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 🙂



  • Ja, WYSIWYG kann ich nur bekommen, wenn ich auf GridFitting verzichte. Ziel ist es, DASSELBE Layout auf dem Bildschirm (z.B. 96dpi) und auf dem Drucker (z.B. 1200dpi) zu bekommen. Der Zusammenhang ist nicht ganz einfach, ich muss ein wenig ausholen und hoffe, dass ich das so vollständig und korrekt darstelle:

    Eine Schrift wird in einem Designraster (i.d.R. 1024x1024 oder 2048x2048 Punkte) definiert. Wenn nun die Glyphs für eine konkrete Font-Instanz berechnet werden, muss bei GridFitting natürlich gerundet werden, und jetzt macht die Auflösung tatsächlich einen bedeutenden Unterschied. Nehmen wir mal an, die Breite eines bestimmten Glyph, der in 2048x2048 entworfen wurde, beträgt 800 Design-Einheiten; mit diesem Zeichen soll in der Schriftgröße 24 Punkt (auf dem Bildschirm bei 96dpi) und auf dem Drucker (1200dpi) eine Zeile der Breite von 10,0 Inch gefüllt werden.

    Unabhängig davon, welches Maßsystem ich bei GDI, GDI+,... verwende -- es läuft immer darauf hinaus, dass ich letztendlich Ausgabe-Pixel habe. Der Einfachheit halber will ich mich jetzt auch nur um die Zeichen-Breiten kümmern. Für die Umrechnung von Points in Pixel soll die allgemeine akzeptierte Formel {points = pixels * 72 / dpi} bzw. {pixels = points * dpi / 72} gelten.

    Ausgabe auf dem Bildschirm mit 96dpi:
    Die Schriftgröße 24Pt entspricht {24*96/72=32,0}Pixeln; 32 Pixel entsprechen also den 2048 Designraster-Punkten. Die 800 Designeinheiten des Beispiel-Zeichens rechnen sich um in {32/2048*800} 12,5 Pixel. Ich nehme mal an, dass dieser Wert fürs Gridfitting auf 13 Pixel aufgerundet wird -- der Glyph wird entsprechend skaliert.
    Die Ausgabezeile hat eine Breite von 10,0 Inch; das sind bei 96dpi 960 Pixel. Das Zeichen mit der Breite von 13 Pixel geht also 960/13=73,85 mal in die Zeile.

    Jetzt möchte ich die Ausgabe auf dem Drucker (bei 1200 dpi) wiederholen:
    Die Schriftgröße 24Pt entspricht {24*1200/72=400}Pixel; 400 Pixel entsprechen also den 2048 Designraster-Punkten. Die 800 Designeinheiten des Beispiel-Zeichens rechnen sich um in {400/2048*800} 156,25 Pixel. Ich nehme mal an, dass dieser Wert fürs Gridfitting auf 156 Pixel abgerundet wird -- der Glyph wird entsprechend skaliert.
    Die Ausgabezeile hat eine Breite von 10,0 Inch; das sind bei 1200dpi 12000 Pixel. Das Zeichen mit der Breite von 156 Pixel geht also 12000/156=76,92 mal in die Zeile.

    GridFit?
    Das ist ein willkürlich herausgegriffenes Beispiel mit einem einzigen Zeichen. Je nachdem, wie der Mix der Zeichen in einer Zeile ist, kann das Layout durch Auf- und Abrunden sich mehr oder weniger stark verändern.
    Ich glaube, damit konnte ich zeigen, dass GridFit nicht die taugliche Mess- und Ausgabemethode ist, wenn man dasselbe Layout auf dem Bildschirm und auf dem Drucker (also: WYSIWYG) erzielen möchte. Das kann ich nur dann zufriedenstellend hinbekommen, wenn ich auf das Auf- und Abrunden der GridFit-Methode verzichte. Damit läge die Trennlinie zwischen zwei Zeichen wohl immer innerhalb einer Pixel-Spalte. Oder sieht da noch jemand einen Fehler in meiner Darstellung?
    (Vielleicht sollte ich in der Darstellung nicht den Begriff "Glyph" sondern lieber den Begriff "Zeichen" verwenden; das "Zeichen" enthält zusätzlich noch den designbedigten Lerrraum vor und nach dem Glyph)



  • WishfulThinking schrieb:

    Damit läge die Trennlinie zwischen zwei Zeichen wohl immer innerhalb einer Pixel-Spalte. Oder sieht da noch jemand einen Fehler in meiner Darstellung?

    Nö das wird schon hinkommen.
    Ist aber Wurst, wenn ohne Antialiasing gerendert wird.
    Mit klassischem Antialiasing wie schon erwähnt ein Problem.



  • Naja, jetzt haben wir nachgewiesen, warum der Schuh drückt -- es tut aber immer noch weh. Den "Schmerz" versuche ich jetzt auf der Suche nach einer Mess- und Renderlogik, die dieselben Ergebnisse liefern sollen, zu lindern. Ich mache also weiter mit meiner Exploration von [c]DirectWrite[/c].



  • Die DirectWrite-Methode IDWriteFactory::CreateTextLayout erlaubt, soweit ich sehe, nur die Anwendung eines einzigen Font innerhalb des Layout (=Zeile?). In einem solchen Layout kann ich alle möglichen Parameter der Schrift verändern (Fett, kursiv, unterstrichen, Größe), aber ich habe noch keine Methode gefunden, mit der ich den Font selbst für einen Teil des Layout ändern kann. Habe ich da etwas übersehen? 😃



  • Hat sich schon erledigt ..... IDWriteTextLayout::SetFontFamilyName 😃

    Es steht ja alles in der Dokumentation ..... man muss es eben nur finden (Nachdem man es genügend intensiv gesucht hat).



  • So, jetzt bin ich ein gutes Stück weiter. Ich habe in den vergangenen Tagen intensiv mit DirectWrite experimentiert (Dank an hustbaer für den Tipp) und weiß jetzt schon, dass alle meine Anforderungen mit dieser Engine erfüllt werden.

    Für alle, die auch an der Anwendung von DirectWrite interessiert sind, hier habe ich einige Links, die mir bei meinen Experimenten erheblich weitergeholfen haben:

    Aber alle diese Quellen drücken sich um das Thema TABulation. Lediglich in der MSDN-Dokumentation wird die Existenz der Funktionen SetIncrementalTab und GetIncrementalTab sehr sparsam zugegeben. Man erfährt leider nicht, wie diese TABs in der Praxis angewendet werden können: So würde ich z.B. sehr gerne (und dringend) wissen, wie man das Text-Alignment für die verschiedenen TAB-Spalten einstellt.
    Weiß jemand, wie das funktioniert; oder kennt jemand eine Quelle, in der dieses offenbar sehr spezielle DirectWrite-Thema näher beleuchtet wird?



  • Hast Du auch einen Link zu dem Petzold-Artikel?



  • PaulB48 schrieb:

    Hast Du auch einen Link zu dem Petzold-Artikel?

    Sorry -- vergessen.

    Hier ist der Link (ich werde ihn auch im Original-Post nacheditieren):
    http://www.charlespetzold.com/blog/2014/01/Character-Formatting-Extensions-with-DirectWrite.html



  • Ich hab' da mal reingesehen, das sind klasse Quellen, die ich auch nutzen werde.



  • Mechanics schrieb:

    Du könntest vielleicht mal versuchen, dir das Buch "Windows Graphics Programming" zu besorgen. Da steht auch einiges zu dem Thema drin. Hab jetzt z.B. noch die Funktion GetCharacterPlacement gefunden, die hatte ich gar nicht mehr in Erinnerung. Bin mir nicht sicher, ob sie dir jezt weiterhilft, könnte aber sein.

    Inzwischen hatte ich mir mal die Zeit genommen, nach käuflicher Fachliteratur zu suchen, denn auch ich bin nicht zu 100% überzeugt, dass die kostenlosen Informationen aus dem WEB immer zielführend sind. Allerdings: Das Feld der Literatur zu Grafik-Programmierung, insbesondere Berechnen und Rendern von Text, ist sehr schwach vertreten. Auch Deinen Buchtipp glaube ich geortet zu haben: http://www.amazon.de/Windows-Graphics-Programming-Hewlett-Packard-Professional/dp/0130869856/ref=sr_1_4?ie=UTF8&qid=1416059685&sr=8-4&keywords=Windows+Graphics+Programming -- ist es das? Dieses Buch ist aus dem Jahr 2000 und damit - programmiertechnisch - sehr alt; der Preis von gut 200€ für ein neues Exemplar (und gebraucht für knapp unter 100€) animiert nicht gerade zum Spontankauf.

    @Mechanics: Würdest Du mir dieses Buch bei dem aktuellen Stand der Diskussion hier immer noch empfehlen?

    Hat sonst noch jemand dieses Buch und kann mir eventuell eine Kaufempfehlung geben?



  • Nein, wie gesagt, das war seinerzeit ein gutes Buch zum Thema GDI Programmierung. Da geht es eben auch nur um GDI und natürlich nicht um DirectWrite. Du musst auch bedenken, dass es zu der Zeit auch im Internet bei weitem nicht so viele Informationen gab, wie jetzt. Ich habs seinerzeit recht günstig gebraucht bekommen.



  • Ist schon wahr: Inzwischen sind die im Internet verfügbaren Informationen sehr viel umfangreicher als noch zu Beginn des Jahrtausends. Der Buchtipp war aber für den Stand der Diskussion schon sehr sinnvoll.

    Aber leider gibt es im Internet über DirectWrite, speziell bei der Verwendung von TABs, noch erheblichen Informationsbedarf. Ich habe sogar den Verdacht, dass Microsoft das Thema noch nicht zu Ende gedacht hat.



  • Ich finde, mittlerweile findet man grundsätzlich zu jedem Thema immer weniger sinnvolles, dafür immer mehr Müll... Wenn ich z.B. eine bestimmte Info zu einem Spiel suche, werd ich kaum noch fündig. Dafür kommen 50 Ergebnisseiten mit immer gleichen Reviews, die mich überhaupt nicht interessieren, wo aber evtl. die Wörter vorkommen, die ich eingegeben habe.

    Dein Problem ist eher, dass sowas kaum jemand nutzt. Gibt vielleicht paar Leute, die mal damit rumgespielt haben. Nicht genug, um Forenbeiträge und Blogs zu dem Thema zu schreiben. Deswegen hab ich in dem Buch damals auch viele Infos und Beispiele gefunden, die ich seinerzeit im Internet nicht gefunden hatte.

    Bist du sicher, dass du die Funktionen richtig geschrieben hast? Wenn ich nach GetIncrementalTab suche, finde ich gar nichts. Nicht mal diesen Thread hier 😉



  • Ist wahr, das war mein nachlässiger Schreibfehler - entschuldige.

    Die Funktion heißt GetIncrementalTabStop bzw. SetIncrementalTabStop. Wenn man das als Suchwort eingibt, bekommt man doch etliche Hits, aber die verweisen alle aud dieselbe (dürftige) Beschreibung.



  • Ich hatte eine andere Idee, ich wollte mal im Chromium Code danach suchen. Hab die Funktion aber nicht gefunden. Die ist aber wohl auch eher für Textverarbeitung und nicht für Browser interessant.
    Aber grundsätzlich findet man die mit Codesuchmaschinen schon, z.B. openhub.net. Ich frag mich aber, inwiefern die Funktion überhaupt nützlich ist. So wie ich die Doku interpretiere macht die nicht das, was du willst. Aber was willst du eigentlich genau erreichen?



  • Ich möchte Tab-Stopps in meinen Text einbauen können, ganz so, wie ich es auch bei einem normalen Texteditor kann. Nur muss ich die Formatierung nicht interaktiv eingeben, sie wird in den auszugebenden Text eingefügt.

    Ich weiß, dass es solche Formatierungsschnittstellen bereits gibt. Die mir am meisten geläufige ist RTF (Rich Text), aber die Formatierung ist für meine Zwecke zu komplex (in anderen Applikationen hatte ich sie schon erfolgreich eingesetzt).

    Ja, so sehe ich es auch: Die in DirectWrite vorgesehenen TabStopps scheinen wirklich nicht den vorgesehenen Zweck zu erfüllen, da ich dort nur einen (oder mehrere aufeinanderfolgende TabStopps???) setzen kann, und die Orientierung der Tab-Spalten (links, mittig, rechts) kann ich offensichtlich nicht setzen. Aber ich kann kaum glauben, dass Microsoft dieses Feature in der ansonsten so beeindruckenden und mächtigen Schnittstelle "vergessen" hat. Ich finde eben nur (noch) nicht die richtige Funktion.

    Das, was ich da angegangen bin, entspricht wohl eher einer Textverarbeitung. Vom Chromium Code hatte ich bisher noch nichts gehört, werde mich da aber mal aufschlauen.



  • Also, du willst Tabs wie in einer Textverarbeitung, nicht einem Texteditor? Also, sowas wie Ausrichtung, Position, Füllzeichen?
    Kann schon sein, dass es nicht geht... Ich find da grad ebenfalls auch nichts.



  • Ja, genau das ist mein Ziel. Die Füllzeichen sind mir allerdings weniger wichtig.

    Mit GDI hatte ich das alles schon einmal vollständig implementiert, nur eben die Druckerausgabe ergab - aus inzwischen bekanntem Grunde - einen anderen Umbruch. Das hatte mich ziemlich umgetrieben, und ich hatte meine Versuche mit GDI+ gestartet, mit der ich auch nicht die erwünschte Übereinstimmung zwischen Bildschirm und Drucker erzielen konnte.

    Tja, und jetzt DirectWrite.....
    Nachdem ich mir die Schnittstelle angesehen hatte, war ich von der Vollständigkeit und Klarheit der Implementation begeistert. Leider aber scheinen die TABs zu fehlen. Das ist sehr schade, denn ansonsten könnte ich meinen Code für den Textumbruch erheblich (um weit mehr als die Hälfte) reduzieren.

    Im Moment sehe ich für eine "ordentliche" Implementierung von TABs nur die Möglichkeit, die Textabstände zwischen den Tab-Blöcken in Abhängigkeit von den mit DirectWrite gemessenen Textlängen durch entsprechend breite Zwischenräume zu implementieren. Da bietet DirectWrite zum Glück eine Methode an; der Code wird dadurch allerdings wieder stark vergrößert.



  • Ich glaube ich hab' schon geschrieben dass ich davon ausgehe dass Textverarbeitungen das ganze Layouting selbst machen, oder? 😉


Anmelden zum Antworten