Dieser Artikel wurde maschinell übersetzt.

DirectX-Faktor

Extravagante Buchstabeneffekte

Charles Petzold

Charles PetzoldDer bedeutendste Fortschritt in der digitalen Typografie auf Personal Computern kam vor über 20 Jahren mit dem Schalter von Bitmap-Schriftarten Konturschriftarten. In Versionen von Windows vor Windows 3.1 war auf dem Bildschirm Text von Font-Dateien, bestehend aus kleinen Bitmaps in bestimmten Größen erzeugt. Diese Bitmaps könnten für zwischendurch-Größen, aber nicht ohne einen Verlust von Treue skaliert werden.

Adobe Systems Inc. Pionier eine alternative Möglichkeit, Computer-Schriftarten mit PostScript, die Schriftzeichen mit grafischen Konturen bestehend aus geraden Linien und Bézier-Kurven definiert. Sie könnten diese Konturen und jede Dimension und algorithmische "Hints" half Treue bei kleinen Schriftgraden bewahren skalieren. Als Alternative zu PostScript -Schriftarten auf dem PC-Bildschirm, Apple Inc. entwickelt TrueType-Schriftart-Spezifikation, welche Microsoft später übernommen. Schließlich entwickelte, die heutigen gemeinsamen OpenType Standard.

In diesen Tagen wir als gegeben hinnehmen die kontinuierliche Skalierbarkeit auf dem Bildschirm Schriften sowie die Fähigkeit zu drehen oder neigen Text mit grafischen Transformationen. Doch ist es auch möglich, die tatsächlichen Geometrien zu erhalten, die die Umrisse dieser Schrift-Zeichen definieren und verwenden Sie sie für ungewöhnliche Zwecke, z. B. gliedern Textzeichen, beschneiden oder durchführen von nicht-linearen Transformationen.

Von Schriftart zu Clipping Geometrie

Wenn Sie Zeichen Gliederung Geometrien in einer Windows-Speicher-Anwendung abrufen möchten, hilft die Windows Runtime API nicht. Du musst DirectX verwenden. Die GetGlyphRunOutline-Methode des IDWriteFontFace schreibt die Zeichenkonturen in ein IDWriteGeometrySink (das ist dasselbe wie ein ID2D1SimplifiedGeometrySink), die definiert (oder trägt) ein ID2D1PathGeometry-Objekt.

Abbildung 1 zeigt den Konstruktor einer Klasse Darstellung in einer Windows-8.1-Anwendung mit dem Namen ClipToText, die ich aus der DirectX-App (XAML)-Vorlage erstellt. Das Projekt umfasst die verteilbare Miramonte kühn-Font-Datei und der Code zeigt, wie ein Symbol ausgeführt, um eine Pfadgeometrie konvertieren. Wie üblich habe ich die Prüfungen von fahrenden HRESULT-Werten der Klarheit halber entfernt.

Abbildung 1 Konvertieren eines Symbols laufen auf einem Pfadgeometrie

ClipToTextRenderer::ClipToTextRenderer(
  const std::shared_ptr<DeviceResources>& deviceResources) :
  m_deviceResources(deviceResources)
{
  // Get font file
  ComPtr<IDWriteFactory> factory = m_deviceResources->GetDWriteFactory();
  String^ filePath = Package::Current->InstalledLocation->Path +
     "\\Fonts\\Miramob.ttf";
  ComPtr<IDWriteFontFile> fontFile;
  factory->CreateFontFileReference(filePath->Data(), 
    nullptr, &fontFile);
  // Get font face
  ComPtr<IDWriteFontFace> fontFace;
  factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE,
                          1,
                          fontFile.GetAddressOf(),
                          0,
                          DWRITE_FONT_SIMULATIONS_NONE,
                          &fontFace);
  // Create path geometry and open it
  m_deviceResources->GetD2DFactory()->CreatePathGeometry(&m_clipGeometry);
  ComPtr<ID2D1GeometrySink> geometrySink;
  m_clipGeometry->Open(&geometrySink);
  // Get glyph run outline ("CLIP")
  uint16 glyphIndices [] = { 0x0026, 0x002F, 0x002C, 0x0033 };
  float emSize = 96.0f;
      // 72 points, arbitrary in this program
  fontFace->GetGlyphRunOutline(emSize,
                               glyphIndices,
                               nullptr,
                               nullptr,
                               ARRAYSIZE(glyphIndices),
                               false,
                               false,
                               geometrySink.Get());
  // Don't forget to close the geometry sink!
geometrySink->Close();
  CreateDeviceDependentResources();
}

Obwohl der Code in Abbildung 1 erhält ein IDWriteFontFace-Objekt von einer privat geladen-Schriftart können die Anwendungen auch abrufen, Font-Face-Objekte von Schriften in Font-Kollektionen, inklusive der System Font Collection. Der Code in Abbildung 1 gibt Symbolindizes explizit entspricht der Text "CLIP", aber Sie auch können eine Unicode-Zeichenfolge mithilfe der GetGlyphIndices-Methode Symbolindizes abgeleitet.

Sobald Sie ein ID2D1PathGeometry-Objekt erstellt haben, können Sie es zum Ausfüllen (in diesem Fall das Ergebnis sieht aus wie Text gerendert), Zeichnung (das nur die Umrisse wiedergegeben wird), oder Clipping. Abbildung 2 zeigt eine Render-Methode, die skaliert und übersetzt die Pfadgeometrie um einen Freistellungsbereich zu definieren, die den gesamten Anzeigebereich umfasst. Beachten Sie die Pfadgeometrie hat positive und negative Koordinaten. Die (0, 0) Herkunft der Pfadgeometrie entspricht die Grundlinie zu Beginn des Symbols führen.

Abbildung 2 Clipping mit einer Pfadgeometrie

bool ClipToTextRenderer::Render()
{
  if (!m_needsRedraw)
    return false;
  ID2D1DeviceContext* context = m_deviceResources->GetD2DDeviceContext();
  Windows::Foundation::Size outputBounds = m_deviceResources->GetOutputBounds();
  context->SaveDrawingState(m_stateBlock.Get());
  context->BeginDraw();
  context->Clear(ColorF(ColorF::DarkBlue));
  // Get the clip geometry bounds
  D2D_RECT_F geometryBounds;
  m_clipGeometry->GetBounds(D2D1::IdentityMatrix(), &geometryBounds);
  // Define transforms to center and scale clipping geometry
  Matrix3x2F orientationTransform =
     m_deviceResources->GetOrientationTransform2D();
  Matrix3x2F translateTransform =
     Matrix3x2F::Translation(SizeF(-geometryBounds.left, -geometryBounds.top));
  float scaleHorz = outputBounds.Width / 
                    (geometryBounds.right - geometryBounds.left);
  float scaleVert = outputBounds.Height / 
                    (geometryBounds.bottom - geometryBounds.top);
  Matrix3x2F scaleTransform = Matrix3x2F::Scale(SizeF(scaleHorz, scaleVert));
  // Set the geometry for clipping
  ComPtr<ID2D1Layer> layer;
  context->CreateLayer(&layer);
  context->PushLayer(
    LayerParameters(InfiniteRect(),
                    m_clipGeometry.Get(),
                    D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
                    translateTransform * scaleTransform
                         * orientationTransform), layer.Get());
  // Draw lines radiating from center
  translateTransform = Matrix3x2F::Translation(outputBounds.Width / 2,
                         outputBounds.Height / 2);
  for (float angle = 0; angle < 360; angle += 1)
  {
    Matrix3x2F rotationTransform = Matrix3x2F::Rotation(angle);
    context->SetTransform(rotationTransform * translateTransform * 
                          orientationTransform);
    context->DrawLine(Point2F(0, 0),
                      Point2F(outputBounds.Width / 2, outputBounds.Height / 2),
                      m_whiteBrush.Get(), 2);
  }
  context->PopLayer();
  HRESULT hr = context->EndDraw();
  if (hr != D2DERR_RECREATE_TARGET)
  {
    DX::ThrowIfFailed(hr);
  }
  context->RestoreDrawingState(m_stateBlock.Get());
  m_needsRedraw = false;
  return true;
}

Die Render-Methode zeichnet dann eine Reihe von Zeilen, die Strahlen von der Mitte des Bildschirms, Erstellen der Abbildung Abbildung 3.

The ClipToText Display
Abbildung 3 die ClipToText-Anzeige

Tiefer in Geometriedefinitionen

Generell ist eine Pfadgeometrie eine Sammlung von Figuren, von die jedes eine Auflistung der verbundenen Segmente ist. Diese Segmente haben die Form von geraden Linien, quadratische und kubische Bézier-Kurven und Bögen (die Kurven auf den Umfang einer Ellipse). Eine Figur kann entweder geschlossen, in diesem Fall ist der Endpunkt der Verbindung zum Startpunkt oder offen, sein.

Die GetGlyphRunOutline-Methode schreibt die Glyph Umrisse in ein IDWriteGeometrySink, das ist dasselbe wie ein ID2D1SimplifiedGeometrySink. Dies ist wiederum die übergeordnete Klasse zu einem regelmäßigen ID2D1GeometrySink. Mit ID2D1SimplifiedGeometry­Waschbecken statt ID2D1GeometrySink impliziert, dass die resultierende Pfadgeometrie Figuren, die ausschließlich aus geraden Linien und kubische Bézierkurven bestehen, enthält — keine quadratischen Bézier-Kurven und keine Bögen.

Schriftkonturen Charakter, diese Segmente werden immer geschlossen, — d. h. verbindet der Endpunkt der Figur zum Startpunkt. Die Pfadgeometrie in das ClipToText-Programm für die Zeichen "CLIP" aus fünf Figuren besteht erstellt — eine Figur für die ersten drei Buchstaben und zwei für den letzten Buchstaben für das Innere des oberen Teils der p-Konto

Vielleicht möchten Sie Zugriff auf die eigentlichen Linien und Bézier-Kurven, die die Pfadgeometrie zu bilden, so dass Sie diese seltsame und ungewöhnliche Weise bearbeiten können. Zunächst scheint dies nicht möglich. Sobald ein ID2D1PathGeometry-Objekt mit Daten initialisiert wurde, das Objekt ist unveränderlich und die Schnittstelle bietet keine Möglichkeit, den Inhalt zu erhalten.

Es gibt eine Lösung, aber: Sie können Ihre eigene Klasse schreiben, die die ID2D1SimplifiedGeometrySink-Schnittstelle implementiert, und übergeben Sie eine Instanz dieser Klasse an die GetGlyphRunOutline-Methode. Ihre benutzerdefinierte Implementierung des ID2D1SimplifiedGeometrySink muss die Methoden mit dem Namen BeginFigure, AddLines, AddBeziers und EndFigure (u.a. ein paar) enthalten. In diesen Methoden können Sie die Geometrie der gesamten Pfad in einer Struktur von Strukturen speichern, die Sie definieren können.

Dies ist, was ich getan habe. Die Strukturen, die ich zum Speichern des Inhalts einer Pfad-Geometrie definiert werden angezeigt, Abbildung 4. Diese Strukturen zeigen, wie eine Pfadgeometrie eine Sammlung von Abbildung Pfadobjekte und jeder Pfad-Figur ist eine Sammlung von verbundenen Segmenten bestehend aus geraden Linien und kubische Bézier-Kurven.

Abbildung 4 Strukturen zum Speichern des Inhalts einer Pfad-Geometrie

struct PathSegmentData
{
  bool IsBezier;
  std::vector<D2D1_POINT_2F> Points;
          // for IsBezier == false
  std::vector<D2D1_BEZIER_SEGMENT> Beziers;
   // for IsBezier == true
};
struct PathFigureData
{
  D2D1_POINT_2F StartPoint;
  D2D1_FIGURE_BEGIN FigureBegin;
  D2D1_FIGURE_END FigureEnd;
  std::vector<PathSegmentData> Segments;
};
struct PathGeometryData
{
  D2D1_FILL_MODE FillMode;
  std::vector<PathFigureData> Figures;
  Microsoft::WRL::ComPtr<ID2D1PathGeometry>
  GeneratePathGeometry(ID2D1Factory * factory);
};

Meine Implementierung von der ID2D1SimplifiedGeometrySink nennt man InterrogableGeometrySink, so genannt, weil es eine Methode enthält, die die resultierende Geometrie als ein PathGeometryData-Objekt zurückgibt. Die interessantesten Teile des InterrogableGeometrySink werden angezeigt, Abbildung 5.

Abbildung 5 die meisten der InterrogableGeometrySink-Klasse

void InterrogableGeometrySink::BeginFigure(D2D1_POINT_2F startPoint,
                    D2D1_FIGURE_BEGIN figureBegin)
{
  m_pathFigureData.StartPoint = startPoint;
  m_pathFigureData.FigureBegin = figureBegin;
  m_pathFigureData.Segments.clear();
}
void InterrogableGeometrySink::AddLines(const D2D1_POINT_2F *points,
                UINT pointsCount)
{
  PathSegmentData polyLineSegment;
  polyLineSegment.IsBezier = false;
  polyLineSegment.Points.assign(points, points + pointsCount);
  m_pathFigureData.Segments.push_back(polyLineSegment);
}
void InterrogableGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers,
                   UINT beziersCount)
{
  PathSegmentData polyBezierSegment;
  polyBezierSegment.IsBezier = true;
  polyBezierSegment.Beziers.assign(beziers, beziers + beziersCount);
  m_pathFigureData.Segments.push_back(polyBezierSegment);
}
void InterrogableGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd)
{
  m_pathFigureData.FigureEnd = figureEnd;
  m_pathGeometryData.Figures.push_back(m_pathFigureData);
}
HRESULT InterrogableGeometrySink::Close()
{
  // Assume that the class accessing the geometry sink knows what it's doing
  return S_OK;
}
// Method for this implementation
PathGeometryData InterrogableGeometrySink::GetPathGeometryData()
{
  return m_pathGeometryData;
}

Übergeben Sie einfach eine Instanz von InterrogableGeometrySink zu GetGlyphRunOutline das PathGeometryData-Objekt ab, das die Zeichenkonturen beschreibt. PathGeometryData enthält auch eine Methode namens GeneratePathGeometry, die den Baum mit Abbildungen und Segmenten ein ID2D1PathGeometry-Objekt zu erstellen, die Sie dann, zum Zeichnen verwenden können, füllen oder clipping verwendet. Der Unterschied ist, dass vor dem Aufruf von GeneratePathGeometry, Ihr Programm die Punkte ändern kann, die der Linie und Bézier-Segmente bilden. Sie können auch hinzufügen oder Entfernen von Segmenten oder Figuren.

Die InterrogableGeometrySink-Klasse und die unterstützenden Strukturen sind Teil eines Projekts mit dem Namen RealTextEditor; "Real" meine ich, dass Sie Textpfade statt den Text selbst bearbeiten können. Wenn das Programm kommt, zeigt es die großen Buchstaben "DX." Tippen Sie oder klicken Sie auf den Bildschirm, um den Bearbeitungsmodus zu wechseln. Im Bearbeiten-Modus die Zeichen werden skizziert und Punkte angezeigt.

Grüne Punkte markieren den Anfang und ein Ende von Liniensegmenten und Bézier-Segmenten. Rote Punkte sind Bézier-Kontrollpunkte. Kontrollpunkte sind mit entsprechenden Endpunkte mit roten Linien verbunden. Sie können diese Punkte mit der Maus zu greifen – sie sind ein wenig zu klein für Finger — und ziehen Sie sie, verzerren die Textzeichen auf seltsame Art und Weise, wie Abbildung 6 veranschaulicht.

Modified Character Outlines in RealTextEditor
Abbildung 6 geändert Zeichenkonturen in RealTextEditor

RealTextEditor hat keine Möglichkeit, Ihre benutzerdefinierten Zeichen-Geometrien zu speichern, aber Sie könnte sicherlich einen hinzufügen. Die Absicht dieses Programms ist nicht wirklich Schriftzeichen zu bearbeiten, aber verdeutlichen, wie Schriftzeichen definiert sind, durch eine Reihe von geraden Linien und Bézier-Kurven in geschlossenen Körpern verbunden – in diesem Fall drei Zahlen, zwei für das innerhalb und außerhalb der D und eine weitere für das X.

Algorithmische Manipulationen

Sobald Sie eine Pfadangabe Geometrie in Form von Strukturen wie PathGeometryData, PathFigureData und PathSegmentData haben, können Sie auch bearbeiten die einzelnen Punkte algorithmisch, verdrehen und drehen Zeichen in welcher Weise Sie bitte vielleicht Erstellen eines Abbilds wie z. B. in Abbildung 7.

The OscillatingText Program
Abbildung 7 das OscillatingText-Programm

Naja, nicht ganz. Der Abbildung Abbildung 7 ist nicht möglich, mithilfe eines PathGeometryData-Objekts generiert aus der Interrogable­GeometrySink Klasse ich habe Sie gerade gezeigt. In vielen einfachen serifenlose Schriftarten bestehend aus die Hauptstadt H 12 Punkte durch gerade Linien verbunden. Wenn Sie ausschließlich mit diesen Punkten zu tun haben, gibt es keine Möglichkeit Sie sie ändern können, so dass die geraden h Kurven.

Jedoch können Sie dieses Problem durch eine verbesserte Version des InterrogableGeometrySink genannt InterpolatableGeometrySink lösen. Wenn diese neue Klasse eine gerade Linie in der AddLines-Methode stößt, bricht es diese Zeile in mehrere kleinere aufgeteilt. (Sie können dieses Feature mit einem Konstruktorargument steuern.) Das Ergebnis ist eine vollständig Klemmverbinder Pfaddefinition Geometrie.

Das OscillatingText-Programm verantwortlich für das Bild in Abbildung 7 tatsächlich schwingt das Innere des Protagonisten hin und her, viel wie ein Hula-Tanz. Dieser Algorithmus ist in der Update-Methode in der Renderingklasse implementiert. Zwei Kopien von einem PathGeometryData werden beibehalten: Die Quelle (gekennzeichnet als "Src") beschreibt den ursprünglichen Text-Umriss, und das Ziel ("dst") enthält modifizierte Punkte anhand des Algorithmus. Die Update-Methode schließt mit dem Aufruf von GeneratePathGeometry für die Ziel-Struktur, und das ist, was das Programm in seiner Render-Methode zeigt.

Manchmal, wenn eine Pfadgeometrie algorithmisch zu ändern, könnten Sie arbeiten ausschließlich mit Linien und nicht als Bézier-Kurven vorziehen. Sie können das tun. Sie können definieren ein ID2D1PathGeometry-Objekt aus einem Aufruf von GetGlyphRunOutline und rufen Sie dann vereinfachen auf diese ID2D1PathGeometry mit der D2D1_GEOMETRY_SIMPLI­FICATION_OPTION_LINES Konstante und ein Interpolatable­GeometrySink Instanz.

Von DirectX für die Windows-Runtime

Wenn Sie mit Windows Runtime API, PathGeometryData, PathFigureData und PathSegmentData Betriebe im kennen Abbildung 4 wahrscheinlich scheinen sehr vertraut. Der Windows::Xaml::UI::Media-Namespace enthält ähnliche Klassen PathGeometry, PathFigure und PathSegment, von der PolyLineSegment und PolyBezierSegment abgeleitet werden. Dies sind die Klassen, die Sie verwenden, um eine Pfadgeometrie in der Windows-Runtime zu definieren, die Sie normalerweise Rendern mit dem Pfad-Element.

Natürlich sollte nicht die Ähnlichkeit überrascht sein. Schließlich baut Windows Runtime auf DirectX. Was diese Ähnlichkeit bedeutet, ist, dass Sie eine Klasse schreiben können, die ID2D1Simpli implementiert­FiedGeometrySink um eine Struktur von PolyBezierSegment, PolyLineSegment, PathGeometry und PathFigure-Objekten erstellen. Das resultierende PathGeometry-Objekt ist direkt von einer Windows-Runtime-Anwendung verwendet und kann in einer XAML-Datei verwiesen werden. (Sie könnte auch eine ID2D1SimplifiedGeometrySink-Implementierung, die eine XAML-Darstellung einer PathGeometry generiert schreiben und einfügen, die in einer XAML-Datei in allen XAML-basierte Umgebungen, wie Silverlight.)

Die TripleAnimatedOutline-Lösung veranschaulicht diese Technik. Die Lösung enthält eine Runtime-Komponente in Windows-Projekt mit dem Namen SimpleDWriteLib, die enthält ein publicrefclass namens TextGeometryGenerator, die ermöglicht den Zugriff auf die Systemschriftarten und Gliederung-Geometrien, die auf der Grundlage von diese Fonts generiert. Da diese Ref-Klasse einer Windows-Runtime-Komponente gehört, besteht die öffentliche Schnittstelle nur aus Windows Runtime-Typen. Ich habe diese öffentliche Schnittstelle meist von Abhängigkeitseigenschaften bestehen, damit sie mit Bindungen in einer XAML-Datei verwendet werden könnte. Das SimpleDWriteLib-Projekt enthält auch eine private Klasse mit dem Namen InteroperableGeometrySink, die die ID2D1SimplifiedGeometrySink-Schnittstelle implementiert und erstellt ein Windows Runtime PathGeometry-Objekt.

Sie können dann diese PathGeometry mit einem Path-Element. Aber aufpassen: Wenn die Windows Runtime-Layout-Engine die Größe eines Pfadelements für Layoutzwecke berechnet verwendet er nur positive Koordinaten. Um die PathGeometry einfacher, in einer XAML-Datei verwendet, definiert TextGeometryGenerator eine DWRITE_GLYPH_OFFSET, die Koordinaten, die basierend auf dem offensiven Feld der Font-Metriken-Struktur ändert. Dies dient dazu, passen Sie die Geometrie-Koordinaten am oberen Rand der Schriftzeichen und nicht am Ursprung zu beginnen, und am häufigsten negative Koordinaten zu beseitigen.

Um die Interoperabilität der SimpleDWriteLib Komponente zu demonstrieren, ist das TripleAnimatedOutline-Anwendung-Projekt in Visual Basicgeschrieben. Aber keine Sorge: Ich musste Visual Basic Code schreiben. Alles, was ich zu diesem Projekt habe ist in der MainPage.xaml-Datei angezeigt, die Abbildung 8. Das Listenfeld zeigt alle Schriftarten auf dem System des Benutzers, und eine Gliederung-Geometrie auf der Grundlage der ausgewählten Schriftart wird animiert auf drei Arten:

  • Punkte um die Zeichen zu reisen;
  • Ein Pinsel mit Farbverlauf fegt hinter den Text;
  • Eine Transformation der Projektion dreht es sich um die vertikale Achse.

Abbildung 8 die TripleAnimatedOutline XAML-Datei

<Page
  x:Class="TripleAnimatedOutline.MainPage"
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:TripleAnimatedOutline"
  xmlns:dwritelib="using:SimpleDWriteLib">
  <Page.Resources>
    <dwritelib:TextGeometryGenerator x:Key="geometryGenerator"
                                     Text="Outline"
                                     FontFamily="Times New Roman"
                                     FontSize="192"
                                     FontStyle="Italic" />
  </Page.Resources>
  <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <ListBox Grid.Column="0"
             ItemsSource="{Binding Source={StaticResource geometryGenerator},
                                   Path=FontFamilies}"
             SelectedItem="{Binding Source={StaticResource geometryGenerator},
                                    Path=FontFamily,
                                    Mode=TwoWay}" />
    <Path Name="path"
          Grid.Column="1"
          Data="{Binding Source={StaticResource geometryGenerator}, Path=Geometry}"
          Fill="LightGray"
          StrokeThickness="6"
          StrokeDashArray="0 2"
          StrokeDashCap="Round"
          HorizontalAlignment="Center"
          VerticalAlignment="Center">
      <Path.Stroke>
        <LinearGradientBrush StartPoint="0 0" EndPoint="1 0"
                                     SpreadMethod="Reflect">
          <GradientStop Offset="0" Color="Red" />
          <GradientStop Offset="1" Color="Blue" />
          <LinearGradientBrush.RelativeTransform>
            <TranslateTransform x:Name="brushTransform" />
          </LinearGradientBrush.RelativeTransform>
        </LinearGradientBrush>
      </Path.Stroke>
      <Path.Projection>
        <PlaneProjection x:Name="projectionTransform" />
      </Path.Projection>
    </Path>
  </Grid>
  <Page.Triggers>
    <EventTrigger>
      <BeginStoryboard>
        <Storyboard>
          <DoubleAnimation Storyboard.TargetName="path"
                           Storyboard.TargetProperty="StrokeDashOffset"
                           EnableDependentAnimation="True"
                           From="0" To="2" Duration="0:0:1"
                           RepeatBehavior="Forever" />
          <DoubleAnimation Storyboard.TargetName="brushTransform"
                           Storyboard.TargetProperty="X"
                           EnableDependentAnimation="True"
                           From="0" To="2" Duration="0:0:3.1"
                           RepeatBehavior="Forever" />
          <DoubleAnimation Storyboard.TargetName="projectionTransform"
                           Storyboard.TargetProperty="RotationY"
                           EnableDependentAnimation="True"
                           From="0" To="360" Duration="0:0:6.7"
                           RepeatBehavior="Forever" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Page.Triggers>
</Page>

Ein zweites Programm wird auch SimpleDWriteLib verwendet. Dies ist RippleText, ein C#-Programm, das ein CompositionTarget.Rendering-Ereignis verwendet, um eine Animation im Code auszuführen. Ähnlich wie bei OscillatingText, RippleText erhält zwei identische PathGeometry-Objekte. Es verwendet einen als eine unveränderliche Quelle und die andere als Fahrziel, deren Punkte algorithmisch transformiert werden. Der Algorithmus beinhaltet eine animierte Sinus-Kurve, die für die vertikalen Koordinaten, was zu Verzerrungen wie gilt Abbildung 9.

The RippleText Display
Abbildung 9 die RippleText-Anzeige

Obwohl die Beispiele ich hier sind extrem in vielerlei Hinsicht gezeigt habe, haben Sie sicherlich die Möglichkeit, subtileren Effekte zu erzeugen. Ich vermute, dass ein Großteil der WordArt-Funktion in Microsoft Word um Verfahren, bei denen die Manipulation der Charakter erstellt wird skizziert, so, dass vielleicht einige Anregungen bieten.

Sie können diese Techniken auch in normaler Text-Anzeige-Code basierend auf IDWriteTextLayout integrieren. Diese Schnittstelle hat eine Methode namens Draw, die akzeptiert eine Instanz einer Klasse, die IDWriteTextRenderer-Schnittstelle implementiert. Das ist eine Klasse, die Sie schreiben, werden Sie sich um Zugriff auf das DWRITE_GLYPH_RUN-Objekt abzurufen, das den wiederzugebende Text beschreibt. Sie können Änderungen an dem Symbol ausführen und wiedergegeben wird, während die geänderte Version, oder können Sie die Zeichen-Gliederung-Geometrien zu diesem Zeitpunkt zu generieren und ändern die Konturen vor dem Rendering.

Großteil des Stroms von DirectX liegt in seiner Flexibilität und Anpassungsfähigkeit an unterschiedliche Szenarien.

Charles Petzold ist ein langjähriger Beitrag zum MSDN Magazine und Autor von "Programming Windows, 6th Edition" (Microsoft Press, 2012), ein Buch über das Schreiben von Anwendungen für Windows 8. Seiner Website lautet charlespetzold.com.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Jim Galasyn (Microsoft)