August 2013

Volume 28 Number 8

DirectX-Faktor - Fingerzeichnen mit Direct2D-Geometrien

By Charles Petzold | August 2013

Charles PetzoldAls Betriebssysteme im Laufe der Jahre entwickelt haben, so haben die archetypischen Basisanwendungen, die jeder Entwickler kennen sollte wie man Code. Für alte Befehlszeilenumgebungen war eine gemeinsame Übung ein hex-Dump – ein Programm, das den Inhalt einer Datei im hexadezimalen Bytes werden aufgelistet. Beliebt waren für grafische Oberflächen für Maus und Tastatur Taschenrechner und Notizblock.

In einer Multi-Touch-Umgebung wie Windows 8 würde ich zwei archetypische Anwendungen nominieren: Foto Scatter und Finger zu malen. Die Foto-XY ist ein guter Weg zu erfahren, wie Sie mithilfe von zwei Fingern zu skalieren und visuelle Objekte zu drehen, während Fingerfarbe umfasst einzelne Finger zum Zeichnen von Linien auf dem Bildschirm verfolgen.

Ich erforschte verschiedene Ansätze zu Windows 8 Fingerfarbe in Kapitel 13 von meinem Buch "Programming Windows, 6th Edition" (Microsoft Press, 2012). Diese Programme nur die Windows-Runtime (WinRT) zum Rendern der Zeilen verwendet, aber jetzt würde ich gerne die Ausübung zu überdenken und stattdessen DirectX. Dies wird eine gute Möglichkeit, mit einigen wichtigen Aspekten von DirectX vertraut sein, aber ich vermute, dass es uns schließlich auch einige zusätzliche Flexibilität nicht verfügbar in der Windows-Runtime ermöglichen.

Visual Studio -Vorlage

Als Doug Erickson diskutiert in seinem März 2013-Artikel, "Verwendung von XAML mit DirectX und C++ in Windows Store Apps" (msdn.microsoft.com/­Magazin/jj991975), es gibt drei Möglichkeiten, XAML und DirectX innerhalb einer Windows-Speicher-Anwendung zu verbinden.Ich werde den Ansatz verwenden, der ein SwapChainBackgroundPanel wie das Stammelement Kind eines XAML-Seite-Derivats einbezieht.Dieses Objekt dient als eine Zeichenoberfläche für Direct2D und Direct3D-Grafiken, aber es kann auch mit WinRT Steuerelemente, z. B. Anwendung Bars überlagert werden.

Visual Studio 2012 enthält eine Projektvorlage für ein solches Programm.Wählen Sie im Dialogfeld Neues Projekt Visual C++ und Windows Store auf der linken Seite und dann die Vorlage namens Direct2D App (XAML).  Die andere DirectX-Vorlage heißt Direct3D-App und ein DirectX-Programm ohne WinRT Steuerelemente oder Grafiken erstellt.Allerdings sind diese beiden Vorlagen etwas Namen, weil Sie 2D oder 3D Grafiken mit keinem von beiden tun können.

Die Direct2D App (XAML)-Vorlage erstellt eine einfache Windows-Speicher-Anwendung mit Programmlogik, die eine XAML-basierte Benutzeroberfläche und DirectX Grafik-Produktion aufgeteilt werden sollten.Die Benutzeroberfläche besteht aus einer Klasse namens DirectXPage, die von Page abgeleitet wird, (so wie in einer normalen Windows Store-Anwendung) und besteht aus eine XAML-Datei, die Header-Datei und die Codedatei.Sie verwenden DirectXPage für die Verarbeitung von Benutzereingaben, Anbindung mit WinRT Steuerung und Anzeige von XAML-basierte Grafiken und Text.Das Root-Element des DirectXPage ist die SwapChainBackgroundPanel, die Sie, als ein regulärer Grid-Element in XAML sowie einer Renderingoberfläche DirectX behandeln können.

Die Projektvorlage erstellt auch eine Klasse namens DirectXBase, die Großteil des DirectX-Overheads übernimmt und eine Klasse namens SimpleTextRenderer, die von DirectXBase abgeleitet und führt anwendungsspezifische DirectX Grafikausgabe.Der Name verweist SimpleTextRenderer, was diese Klasse innerhalb der Anwendung aus der Projektvorlage erstellt macht.Sie wollen diese Klasse umbenennen, oder ersetzen Sie ihn durch etwas, das einen besser geeigneten Namen hat.

Vom Template zur Anwendung

Zum herunterladbaren Code für diesen Artikel ist ein Visual Studio -Projekt BasicFingerPaint benannt, dass ich mit die Direct2D (XAML)-Vorlage erstellt.Ich benannte SimpleTextRenderer zu Fingerfarbe­Renderer und einige andere Klassen hinzugefügt.

Die Direct2D (XAML)-Vorlage impliziert eine Architektur, die das XAML und DirectX Teile der Anwendung trennt: Die Anwendung DirectX Code sollte auf DirectXBase (die Sie sollten nicht ändern müssen), Klasse Renderer beschränkt sein, die abgeleitet wird von DirectXBase (in diesem Fall FingerPaintRenderer), und andere Klassen oder Strukturen, die diese beiden Klassen brauchen könntest.Trotz seines Namens müssen DirectXPage keine DirectX-Code enthalten.Stattdessen instanziiert DirectXPage die Renderer-Klasse, die es als Mitglied private Daten mit dem Namen M_renderer gespeichert wird.DirectXPage führt viele Aufrufe in der Renderer-Klasse (und indirekt DirectXBase) zeigen die grafische Ausgabe und Benachrichtigen DirectX Fenster Größenänderungen und andere wichtige Ereignisse.Die Renderer-Klasse aufrufen nicht in DirectXPage.

In der DirectXPage.xaml-Datei hinzugefügt ich Kombinationsfelder in der Anwendungsleiste, mit denen Sie wählen Sie eine Zeichnung Farbe und Linienstärke und Schaltflächen zum Speichern, laden und Löschen von Zeichnungen.(Die Datei I/O Logik ist sehr rudimentär und nicht gehören zu den Annehmlichkeiten, wie Sie zu warnen, wenn Sie sind dabei, eine Zeichnung zu löschen, die Sie nicht gespeichert haben.)

Wie verschieben Sie einen Finger auf den Bildschirm berühren, und heben Sie es, werden PointerPressed, PointerMoved und PointerReleased Ereignisse generiert, um die Finger Status anzugeben.Jedes Ereignis wird durch eine ID-Nummer begleitet, mit dem Sie einzelne Finger und einen Point-Wert, der angibt, der aktuellen Position des Fingers zu verfolgen.Speichern und schließen Sie diese Punkte, und Sie haben einen einzelnen Strich dargestellt.Mehrere Striche zu rendern, und Sie haben eine komplette Zeichnung.Abbildung 1 zeigt eine BasicFingerPaint Zeichnung, bestehend aus neun Striche.

A BasicFingerPaint Drawing
Abbildung 1 BasicFingerPaint Zeichnung

In der Codebehind-Datei DirectXPage fügte ich überschreibt die Ereignismethoden Zeiger.Diese Methoden rufen entsprechende Methoden in FingerPaintRenderer, dass ich mit dem Namen BeginStroke, ContinueStroke, EndStroke und CancelStroke, wie in Abbildung 2.

Abbildung 2 Zeiger Ereignismethoden Aufrufe an die Renderer Klasse

void DirectXPage::OnPointerPressed(PointerRoutedEventArgs^ args)
{
  NamedColor^ namedColor = 
    dynamic_cast<NamedColor^>(colorComboBox->SelectedItem);
  Color color = 
    namedColor != nullptr ?
namedColor->Color : Colors::Black;
  int width = widthComboBox->SelectedIndex !=
    -1 ?
(int)widthComboBox->SelectedItem : 5;
  m_renderer->BeginStroke(args->Pointer->PointerId,
                          args->GetCurrentPoint(this)->Position,
                          float(width), color);
  CapturePointer(args->Pointer);
}
void DirectXPage::OnPointerMoved(PointerRoutedEventArgs^ args)
{
  IVector<PointerPoint^>^ pointerPoints = 
    args->GetIntermediatePoints(this);
  // Loop backward through intermediate points
  for (int i = pointerPoints->Size - 1; i >= 0; i--)
    m_renderer->ContinueStroke(args->Pointer->PointerId,
                               pointerPoints->GetAt(i)->Position);
}
void DirectXPage::OnPointerReleased(PointerRoutedEventArgs^ args)
{
  m_renderer->EndStroke(args->Pointer->PointerId,
                        args->GetCurrentPoint(this)->Position);
}
void DirectXPage::OnPointerCaptureLost(PointerRoutedEventArgs^ args)
{
  m_renderer->CancelStroke(args->Pointer->PointerId);
}
void DirectXPage::OnKeyDown(KeyRoutedEventArgs^ args)
{
  if (args->Key == VirtualKey::Escape)
      ReleasePointerCaptures();
}

Das PointerId-Objekt ist eine eindeutige Ganzzahl, die Finger, Maus und Stift unterscheidet.Die Point- and -Farbe Werte, die an diese Methoden übergeben sind WinRT Grundtypen, aber sie sind nicht DirectX-Arten.DirectX hat seine eigenen Strukturen des Point- and -Farbe mit dem Namen D2D1_POINT_2F und D2D1::ColorF.DirectXPage weiß nicht alles über DirectX, so dass die FingerPaintRenderer-Klasse verfügt über die Verantwortung für alle Konvertierungen zwischen den WinRT Datentypen und DirectX-Datentypen.

Bau Pfadgeometrien

In BasicFingerPaint ist jeder Strich eine Sammlung verbundener kurze Linien aus verfolgen eine Veranstaltungsreihe der Zeiger gebaut.In der Regel eine finger-paint Anwendung rendert diese Zeilen auf eine Bitmap, die dann in einer Datei gespeichert werden kann.Ich beschloss nicht zu tun.Die Dateien, die Sie speichern und Laden von BasicFingerPaint sind Sammlungen von Strichen, die selbst Sammlungen von Punkten.

Wie verwenden Sie Direct2D, diese Striche auf dem Bildschirm gerendert?Schaut man durch die Zeichnungen von ID2D1DeviceContext (die meist von ID2D1RenderTarget definierten Methoden sind) definierten Methoden, springen drei Kandidaten: DrawLine, DrawGeometry und FillGeometry.

DrawLine zeichnet eine einzige gerade Linie zwischen zwei Punkten mit einer bestimmten Breite, Pinsel und Stil.Es ist sinnvoll, einen Strich mit einer Reihe von DrawLine aufrufen zu rendern, aber es ist wahrscheinlich effizienter, die einzelnen Zeilen in einer einzigen Polylinie zu konsolidieren.Dafür benötigen Sie DrawGeometry.

In Direct2D ist im Grunde eine Sammlung von Punkten, die geraden, Bézier-Kurven und Bögen zu definieren.Es gibt kein Konzept für Linienstärke, Farbe oder Stil in einer Geometry.Obwohl Direct2D verschiedene Arten von einfachen Geometrien unterstützt (Rechteck, abgerundetes Rechteck, Ellipse), die vielseitige Geometrie wird durch das ID2D1PathGeometry-Objekt dargestellt.

Eine Pfadgeometrie besteht aus einem oder mehreren "Zahlen." Jede Figur ist eine Reihe von miteinander verbundenen Linien und Kurven.Die einzelnen Komponenten der Figur sind bekannt als "Segmenten." Eine Figur geschlossen werden könnte — das heißt, den letzten Punkt könnte mit dem ersten Punkt verbindet — aber das muss nicht sein.

Um eine Geometrie zu rendern, rufen Sie DrawGeometry auf den Gerätekontext mit bestimmten Linienbreite, Pinsel und Stil.Die FillGeometry-Methode füllt das Innere von geschlossenen Bereichen der Geometrie mit einem Pinsel.

Um einen Schlaganfall zu kapseln, FingerPaintRenderer eine private Struktur mit dem StrokeInfo, wie in gezeigt definiert Abbildung 3.

Abbildung 3 des Renderers StrokeInfo-Struktur und zwei Sammlungen

struct StrokeInfo
{
  StrokeInfo() : Color(0, 0, 0),
                 Geometry(nullptr)
  {
  };
  std::vector<D2D1_POINT_2F> Points;
  Microsoft::WRL::ComPtr<ID2D1PathGeometry> Geometry;
  float Width;
  D2D1::ColorF Color;
};
std::vector<StrokeInfo> completedStrokes;
std::map<unsigned int, StrokeInfo> strokesInProgress;

Abbildung 3 zeigt auch zwei Sammlungen zum Speichern von StrokeInfo-Objekten: Die CompletedStrokes-Auflistung ist ein Vektor, während StrokesInProgress eine Kartensammlung mit Zeiger-ID als Schlüssel ist.

Das Punkte-Mitglied der StrokeInfo-Struktur sammelt alle Punkte, die einen Schlaganfall bilden. Aus diesen Punkten kann ein ID2D1PathGeometry-Objekt erstellt werden. Abbildung 4 zeigt die Methode, die diesen Job ausführt. (Aus Gründen der Übersichtlichkeit nicht die Auflistung des Codes, die fahrenden HRESULT-Werte überprüft zeigen.)

Abbildung 4 erstellen eine Pfadgeometrie von Punkten

ComPtr<ID2D1PathGeometry>
  FingerPaintRenderer::CreatePolylinePathGeometry
    (std::vector<D2D1_POINT_2F> points)
{
  // Create the PathGeometry
  ComPtr<ID2D1PathGeometry> pathGeometry;
  HRESULT hresult = 
    m_d2dFactory->CreatePathGeometry(&pathGeometry);
  // Get the GeometrySink of the PathGeometry
  ComPtr<ID2D1GeometrySink> geometrySink;
  hresult = pathGeometry->Open(&geometrySink);
  // Begin figure, add lines, end figure, and close
  geometrySink->BeginFigure(points.at(0), D2D1_FIGURE_BEGIN_HOLLOW);
  geometrySink->AddLines(points.data() + 1, points.size() - 1);
  geometrySink->EndFigure(D2D1_FIGURE_END_OPEN);
  hresult = geometrySink->Close();
  return pathGeometry;
}

Ein ID2D1PathGeometry-Objekt ist eine Sammlung von Abbildungen und Segmenten.Um den Inhalt einer Geometrie Pfad definieren, rufen Sie zuerst öffnen auf das Objekt, ein ID2D1GeometrySink zu erhalten.Auf diese Geometrie-Senke, die Sie aufrufen, BeginFigure und EndFigure, um jede Figur zu begrenzen und zwischen diese Anrufe, AddLines, AddArc, AddBezier und anderen Segmenten, die diese Zahl hinzu.(Erstellt von FingerPaintRenderer Pfadgeometrien haben nur eine einzelne Figur, mit mehreren geraden Liniensegmenten.) Nach dem Aufruf von Close auf die Geometrie-Senke, die Pfadgeometrie ist gebrauchsfertig aber unveränderlich geworden.Sie können nicht öffnen Sie es erneut oder nichts daran ändern.

Aus diesem Grund müssen wie Ihre Finger über den Bildschirm bewegen und das Programm sammelt Punkte und Striche im Gange ist zeigt, neue Pfadgeometrien kontinuierlich gebaut und alte diejenigen aufgegeben sein.

Wann soll diese neue Pfadgeometrien werden erstellt?Denken Sie daran, dass eine Anwendung PointerMoved Ereignisse empfangen kann schneller als die video-Refresh-Rate, so dass es nicht Sinn der Pfadgeometrie in den PointerMoved-Ereignishandler zu erstellen.Stattdessen das Programm dieses Ereignis behandelt, indem lediglich die neue Nummer speichern aber nicht, wenn es den vorherigen Punkt Duplikate (was manchmal vorkommt).

Abbildung 5 zeigt die drei wichtigsten Methoden in FingerPaintRenderer die Ansammlung von Punkten, die einem Schlaganfall bilden beteiligt.Eine neue StrokeInfo wird die StrokeInProgress-Auflistung während BeginStroke hinzugefügt; Es hat während des ContinueStroke aktualisiert und auf der CompletedStrokes-Auflistung in EndStroke übertragen.

Abbildung 5 Striche in FingerPaintRenderer ansammeln

void FingerPaintRenderer::BeginStroke(unsigned int id, Point point,
                                      float width, Color color)
{
  // Save stroke information in StrokeInfo structure
  StrokeInfo strokeInfo;
  strokeInfo.Points.push_back(Point2F(point.X, point.Y));
  strokeInfo.Color = ColorF(color.R / 255.0f, color.G / 255.0f,
                            color.B / 255.0f, color.A / 255.0f);
  strokeInfo.Width = width;
  // Store in map with ID number
  strokesInProgress.insert(std::pair<unsigned int, 
    StrokeInfo>(id, strokeInfo));
  this->IsRenderNeeded = true;
}
void FingerPaintRenderer::ContinueStroke(unsigned int id, Point point)
{
  // Never started a stroke, so skip
  if (strokesInProgress.count(id) == 0)
      return;
  // Get the StrokeInfo object for this finger
  StrokeInfo strokeInfo = strokesInProgress.at(id);
  D2D1_POINT_2F previousPoint = strokeInfo.Points.back();
  // Skip duplicate points
  if (point.X != previousPoint.x || point.Y != previousPoint.y)
  {
    strokeInfo.Points.push_back(Point2F(point.X, point.Y));
    strokeInfo.Geometry = nullptr;          // Because now invalid
    strokesInProgress[id] = strokeInfo;
    this->IsRenderNeeded = true;
  }
}
void FingerPaintRenderer::EndStroke(unsigned int id, Point point)
{
  if (strokesInProgress.count(id) == 0)
      return;
  // Get the StrokeInfo object for this finger
  StrokeInfo strokeInfo = strokesInProgress.at(id);
  // Add the final point and create final PathGeometry
  strokeInfo.Points.push_back(Point2F(point.X, point.Y));
  strokeInfo.Geometry = CreatePolylinePathGeometry(strokeInfo.Points);
  // Remove from map, save in vector
  strokesInProgress.erase(id);
  completedStrokes.push_back(strokeInfo);
  this->IsRenderNeeded = true;
}

Beachten Sie, dass jede dieser Methoden IsRenderNeeded auf true festgelegt, die darauf hinweist wird, dass der Bildschirm neu gezeichnet werden muss. Dies stellt eine der die strukturellen Veränderungen, die ich hatte, um das Projekt zu machen. Deklarieren Sie in einem neu erstellten Projekt basierend auf der Vorlage Direct2D (XAML) DirectXPage und SimpleTextRenderer Mitglied private Boolean-Daten mit dem Namen M_renderNeeded. Allerdings nur in DirectXPage ist das Datenelement tatsächlich verwendet. Das ist nicht ganz, wie es sein sollte: Oft muss der Renderingcode bestimmen, wenn der Bildschirm neu gezeichnet werden muss. Ich ersetzte diese zwei M_renderNeeded-Datenmember einer öffentlichen-Eigenschaft in FingerPaintRenderer mit dem Namen IsRender­erforderlich. Von DirectXPage und FingerPaintRenderer kann die IsRenderNeeded-Eigenschaft festgelegt werden, aber es wird nur von DirectXPage verwendet.

Die Rendering-Schleife

Im allgemeinen Fall kann ein DirectX-Programm den gesamten Bildschirm neu bei video Bildwiederholfrequenz, zeichnen die oft 60 Frames pro Sekunde oder so ungefähr. Diese Einrichtung bietet die Programm maximale Flexibilität bei der Grafikdarstellung mit Animation oder Transparenz. Anstatt herauszufinden, welcher Teil des Bildschirms aktualisiert werden muss und wie man vermeidet durcheinander vorhandenen Grafiken, wird der gesamte Bildschirm einfach neu gezeichnet.

In einem Programm wie BasicFingerPaint muss der Bildschirm nur neu gezeichnet werden, wenn sich etwas ändert die durch eine Einstellung der IsRenderNeeded-Eigenschaft true angezeigt wird. Darüber hinaus Neuzeichnen möglicherweise denkbar auf bestimmte Bereiche des Bildschirms begrenzt, aber das ist nicht ganz so einfach, mit einer Anwendung aus der Direct2D (XAML)-Vorlage erstellt.

Um den Bildschirm zu aktualisieren, wird DirectXPage verwendet, die handliche Zusammensetzung­Target::Rendering Ereignis, das ausgelöst wird, bei der Synchronisierung mit der Hardware-video-Refresh. In einem DirectX-Programm der Handler für dieses Ereignis bekannt manchmal als die Rendering-Schleife, und erscheint Abbildung 6.

Abbildung 6 die Rendering-Schleife in DirectXPage

void DirectXPage::OnRendering(Object^ sender, Object^ args)
{
  if (m_renderer->IsRenderNeeded)
  {
    m_timer->Update();
    m_renderer->Update(m_timer->Total, m_timer->Delta);
    m_renderer->Render();
    m_renderer->Present();
    m_renderer->IsRenderNeeded = false;
  }
}

Die Update-Methode wird durch den Renderer definiert. Dies ist in dem visuellen Objekte für Rendering, bereit sind, insbesondere, wenn sie Timing-Informationen bereitgestellt durch eine Timer-Klasse, erstellt von der Projektvorlage erfordern. FingerPaintRenderer verwendet die Update-Methode für Pfadgeometrien aus Punkt Sammlungen zu erstellen, falls erforderlich. Die Render-Methode von DirectXBase deklariert ist, aber durch FingerPaintRenderer definiert und ist verantwortlich für die Wiedergabe alle Grafiken. Die Methode mit dem Namen Gegenwart — es ist ein Verb, kein Substantiv — durch DirectXBase definiert ist, und überträgt die zusammengesetzte Visuals auf der video-Hardware.

Die Render-Methode durch Aufrufen von BeginDraw für das Programm ID3D11DeviceContext Objekt beginnt und schließt mit dem Aufruf EndDraw. Dazwischen kann es Zeichenfunktionen nennen. Die Darstellung jedes Strichs während der Render-Methode ist einfach:

m_solidColorBrush->SetColor(strokeInfo.Color);
m_d2dContext->DrawGeometry(strokeInfo.Geometry.Get(),
                           m_solidColorBrush.Get(),
                           strokeInfo.Width,
                           m_strokeStyle.Get());

Die Objekte M_solidColorBrush und M_strokeStyle sind Datenmember.

Was ist der nächste Schritt?

Wie der Name schon sagt, ist BasicFingerPaint eine sehr einfache Anwendung.Da es Striche in ein Bitmap Rendern nicht, verursachen ein eifrig und persistente Finger-Maler das Programm generieren und Tausende von Geometrien zu rendern.Irgendwann könnte Bildschirm aktualisieren leiden.

Jedoch weil das Programm verwaltet diskrete Geometrien, anstatt Sie zu mischen alles zusammen auf eine Bitmap, das Programm kann einzelne Striche später gelöscht oder bearbeitet werden, vielleicht durch Ändern der Farbe oder Breite, oder auch an eine andere Stelle auf dem Bildschirm verschoben.

Da jeder Strich einen einzelnen Pfadgeometrie ist, ist die Anwendung von verschiedenen Styling ziemlich einfach.Z. B. versuchen, eine Zeile erstellen ändern­DeviceIndependentResources-Methode in FingerPaintRenderer:

strokeStyleProps.dashStyle = D2D1_DASH_STYLE_DOT;

Jetzt das Programm zeichnet gepunktete Linien und nicht als durchgezogene Linien, mit dem Ergebnis angezeigt, die Abbildung 7.Diese Technik funktioniert nur, weil jeder Strich einer einzelnen Geometrie ist; Es würde nicht funktionieren, wenn die einzelnen Segmente, bestehend aus die Strichen alle Linien wurden.

Rendering a Path Geometry with a Dotted Line
Abbildung 7 Rendern der Geometrie einer Pfad mit einer gepunkteten Linie

Eine weitere mögliche Verbesserung ist ein Pinsel mit Farbverlauf.Das GradientFingerPaint-Programm ist BasicFingerPaint sehr ähnlich, außer dass es zwei Kombinationsfelder für Farbe hat und Pinsel mit linearen Farbverlauf verwendet, um die Pfadgeometrie zu rendern.Das Ergebnis ist in Abbildung 8 dargestellt.

The GradientFingerPaint Program
Abbildung 8 das GradientFingerPaint-Programm

Obwohl jeder Strich einen eigenen Pinsel mit linearem Farbverlauf hat, ist der Startpunkt des Farbverlaufs immer auf der oberen linken Ecke des der Schlaganfall-Begrenzungen und dem Endpunkt an der unteren rechten Ecke festgelegt.Wie Sie einen Strich mit einem Finger ziehen, sehen oft die graduelle Veränderung Sie wie der Strich länger geht.Aber je nachdem, wie der Strich gezeichnet ist, manchmal die Steigung entlang der Länge des Strichs geht, und manchmal man kaum einen Farbverlauf überhaupt sehen, wie ist offensichtlich mit den zwei Strichen über das X in Abbildung 8.

Wäre es nicht besser, wenn Sie einen Farbverlauf definieren könnte, die entlang die gesamte Länge des Striches, unabhängig von Form oder der Ausrichtung des Strichs erweitert?Oder wie wär 's mit einem Farbverlauf, die ist immer senkrecht auf den Strich, unabhängig davon, wie der Strich dreht und dreht sich?

Wie sie in den Science-Fiction-Filmen Fragen: Wie ist so etwas überhaupt möglich?

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 den folgenden technischen Experten für die Durchsicht dieses Artikels: James McNellis (Microsoft)
James McNellis ist eine C++-Fan und ein Software-Entwickler, die Visual C++-Team bei Microsoft, wo er wo er baut C++-Bibliotheken und unterhält die C-Laufzeitbibliotheken (CRT).  Er tweets auf @JamesMcNellis, und kann man anderswo online über http://jamesmcnellis.com/.

 


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 den folgenden technischen Experten für die Durchsicht dieses Artikels: James McNellis (Microsoft)
James McNellis ist eine C++-Fan und ein Software-Entwickler, die Visual C++-Team bei Microsoft, wo er wo er baut C++-Bibliotheken und unterhält die C-Laufzeitbibliotheken (CRT).  Er tweets auf @JamesMcNellis, und kann man anderswo online über http://jamesmcnellis.com/.