Direct2Dテクノロジーを使用してWinRTコンポーネントを作成する

この記事では、Windows 8 UIのスタイルで視覚的なWinRTコントロールを開発した経験を共有する一連のストーリーを続けます。



前回 、WinRTコントロールとそのためのSDKを作成するために必要な基本手順を説明しましたが、Direct2Dテクノロジを使用してWinRTコンポーネントに視覚効果を作成することについて説明します。



この記事では、移動時に矢印がぼやける、別名ゲージインジケーター(ゲージコントロール)を作成するプロセスを検討します。



注:このプロジェクトの完全なコードは、次のリンクからダウンロードできます: go.devexpress.com/Habr_WinRTSample.aspx







Direct2Dとは何ですか?



Direct2Dテクノロジーは、ハードウェアアクセラレーションされた2次元グラフィックスAPIであり、2次元ジオメトリ、ビットマップ、およびテキストの高性能で高品質な表示を提供します。



Direct2D APIは、Windowsオペレーティングシステムを実行するアプリケーションを作成し、GDI、GDI +、またはDirect3Dを使用する既存のコードと対話するために、Microsoftによって開発されました。



アプリケーションでDirect2Dを使用する必要があるのはいつですか?



典型的な例は、多数のグラフィック要素が描画されるアプリケーションのパフォーマンスの最適化です(たとえば、これは、大量のデータ、マップ、およびすべての種類のゲージインジケーターでグラフを作成するときに発生します)。



Direct2Dとそのアプリケーション機能の詳細については、 対応する MSDN セクションをご覧ください。



システム要件



Windows 8 UI(METRO)のスタイルでアプリケーションを開発するには、Windows 8とVisual Studio 2012が必要になります。これについては、 以前の記事で確認できます。



Direct2Dを使用してコンポーネントを含むC ++ライブラリを作成する



アプリケーションでDirect2Dを使用するには、WinRTコンポーネントをC ++で記述する必要があります。



これを行うには、次の手順を実行します。

  1. Visual Studio 2012を起動します(閉じた場合:-))

  2. Windowsランタイムコンポーネントのような新しいVisual C ++プロジェクトを作成する







    このライブラリには、発表の実装と、Direct2Dテクノロジを使用したレンダリング用に設計されたいくつかのインターフェイスの実装が含まれます。

  3. 次に、C ++プロジェクトのプロパティにDirect2Dライブラリd2d1.libおよびd3d11.libへのリンクを追加する必要があります。







  4. 次に、Direct2Dを使用してコンポーネントのレンダリング実装を記述する必要があります。



    まず、DirectXライブラリを初期化し、 Rendererクラスのインスタンスを作成して返す単一のCreateRendererメソッドを使用して、静的なDrawingFactoryクラスを作成します。



    public ref class DrawingFactory sealed { public: static IRenderer^ CreateRenderer(); };
          
          





    次に、次のメソッドとプロパティを含むメインのIRendererインターフェイスについて説明します。

    -描画用のサーフェス、この場合はImageBrushを転送された寸法で再作成するように設計されたResetメソッド。 将来、結果のImageBrushをWinRTコンポーネントのビジュアルツリー内の任意の要素に割り当てます。

    -Resetメソッドで作成されたブラシを返すTargetBrushプロパティ。レンダリング結果を含む必要があります。

    -プロパティEnableMotionBlurMotionBlurAngleMotionBlurDeviation 。プリミティブをレンダリングするときにぼかし効果を有効にして構成するために使用されます。

    - ジオメトリを作成および描画するためのCreateGeometryDrawGeometryFillGeometryメソッド。

    -さらに、問題のインターフェイスには、 BeginDrawEndDrawClearTargetWidthTargetHeightなど、よりシンプルでわかりやすいいくつかのメソッドとプロパティが含まれています。



     public interface class IRenderer { property int TargetWidth { int get(); } property int TargetHeight { int get(); } property ImageBrush^ TargetBrush { ImageBrush^ get(); } property bool EnableMotionBlur { bool get(); void set(bool value); } property float MotionBlurAngle { float get(); void set(float value); } property float MotionBlurDeviation { float get(); void set(float value); } void Reset(int width, int height); void BeginDraw(); void EndDraw(); void Clear(Color color); IShapeGeometry^ CreateGeometry(); void DrawGeometry(IShapeGeometry^ geometry, Color color, float width); void FillGeometry(IShapeGeometry^ geometry, Color color); };
          
          







    次に、ぼかし効果を作成する方法について説明します。 Direct2Dでは、このタスクは非常に簡単です。 Resetメソッドでは、標準のDirectionalBlurエフェクトと一時的なビットマップ( m_inputSource )を作成する必要があります。このビットマップでは、以前に作成されたエフェクトのソースとして描画して使用します。



     void Renderer::Reset(int width, int height) { D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; ThrowIfFailed(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &m_d3dDevice, NULL, &m_d3dContext)); ThrowIfFailed(m_d3dDevice.As(&m_dxgiDevice)); ThrowIfFailed(m_d2dFactory->CreateDevice(m_dxgiDevice.Get(), &m_d2dDevice)); ThrowIfFailed(m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dContext)); m_imageSource = ref new SurfaceImageSource(width, height, false); IInspectable* inspectable = (IInspectable*) reinterpret_cast<IInspectable*>(m_imageSource); ThrowIfFailed(inspectable->QueryInterface(__uuidof(ISurfaceImageSourceNative), (void**)&m_imageSourceNative)); ThrowIfFailed(m_imageSourceNative->SetDevice(m_dxgiDevice.Get())); m_imageBrush = ref new ImageBrush(); m_imageBrush->ImageSource = m_imageSource; m_targetWidth = width; m_targetHeight = height; ThrowIfFailed(m_d2dContext->CreateEffect(CLSID_D2D1DirectionalBlur, &m_blurEffect)); m_d2dContext->CreateBitmap(D2D1::SizeU(m_targetWidth, m_targetHeight), nullptr, 0, D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET,D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)), &m_inputSource); m_blurEffect->SetInput(0, m_inputSource.Get()); }
          
          







    次に、 BeginDrawメソッドで、m_inputSource中間ビットマップを描画のサーフェスとして使用し、 EndDrawメソッドで描画が完了したらTargetBrushサーフェスを設定し、 EnableMotionBlurプロパティの値に応じて、エフェクトまたは中間ビットマップを描画します。



     void Renderer::BeginDraw() { if (!m_drawingInProcess && (m_d2dContext.Get() != NULL)) { m_d2dContext->BeginDraw(); m_d2dContext->SetTarget(m_inputSource.Get()); m_drawingInProcess = true; } } void Renderer::EndDraw() { if (m_drawingInProcess) { m_d2dContext->EndDraw(); ComPtr<ID2D1Bitmap1> bitmap; PrepareSurface(&bitmap); m_d2dContext->SetTarget(bitmap.Get()); m_d2dContext->BeginDraw(); m_d2dContext->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); if (m_motionBlurEnabled) { m_blurEffect->SetValue(D2D1_DIRECTIONALBLUR_PROP_STANDARD_DEVIATION, m_motionBlurDeviation); m_blurEffect->SetValue(D2D1_DIRECTIONALBLUR_PROP_ANGLE, m_motionBlurAngle); m_d2dContext->DrawImage(m_blurEffect.Get()); } else m_d2dContext->DrawImage(m_inputSource.Get()); m_d2dContext->EndDraw(); m_d2dContext->SetTarget(NULL); m_imageSourceNative->EndDraw(); m_drawingInProcess = false; } }
          
          









WinRTコンポーネントを含むC#ライブラリの作成





次の段階では、別のプロジェクトをソリューションに追加する必要があります-今回はC#です。 このプロジェクトは、以前に作成されたC ++ライブラリで動作するWinRTコンポーネントを含むライブラリです。







このプロジェクトでは、以前に作成したC ++アセンブリにリンクを追加する必要があります。







このライブラリには、WinRT Gauge自体が含まれます。 アセンブリに含めるには、新しいテンプレートコントロールを追加します。







矢印を除くすべての視覚要素は、標準のWinRTツールを使用して描画します。つまり、テンプレートに設定します。



 <Style TargetType="local:Gauge"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:Gauge"> <Grid> <Grid Margin="0,0,0,0"> <Rectangle Fill="#FF252525" RadiusX="30" RadiusY="30"/> <Path Stretch="Uniform" VerticalAlignment="Top" HorizontalAlignment="Center" Stroke="#FFDAA5F8" Fill="#FFD365F8" StrokeThickness="3" StrokeStartLineCap="Square" StrokeEndLineCap="Square" Margin="150,0,150,0"> <Path.Data> <PathGeometry> <PathGeometry.Figures> <PathFigure StartPoint="0,0"> <PathFigure.Segments> <LineSegment Point="0.1,0"/> <ArcSegment Point="10.1,0" Size="5,5" RotationAngle="180" IsLargeArc="False" SweepDirection="Counterclockwise"/> <LineSegment Point="10.2,0"/> <ArcSegment Point="0,0" Size="5.1,5.1" RotationAngle="180" IsLargeArc="False" SweepDirection="Clockwise"/> </PathFigure.Segments> </PathFigure> </PathGeometry.Figures> </PathGeometry> </Path.Data> </Path> <TextBlock Text="{Binding Value, RelativeSource={RelativeSource Mode=TemplatedParent}}" FontSize="100" Foreground="#FFD365F8" VerticalAlignment="Top" HorizontalAlignment="Center"/> <Border x:Name="RendererSurface"/> </Grid> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
      
      







このコンポーネントの視覚的表現には、 RendererSurfaceという名前の空のBorderがあります。 OnApplyTemplateメソッドでこの要素を取得し、クラス変数に保存して、 レンダラーからBrushBackgroundプロパティに割り当てることができるようにします。



 protected override void OnApplyTemplate() { base.OnApplyTemplate(); rendererSurface = (Border)GetTemplateChild("RendererSurface"); }
      
      







クラスのコンストラクターでは、 DrawingFactoryを使用してIRendererを作成し、 SizeChangedイベントをサブスクライブする必要があります。 このイベントでは、 描画されるブラシのサイズがコンポーネントのサイズに対応するように、 IRenderer.Resetメソッドを呼び出します。 また、静的CompositionTarget.Renderingイベントをサブスクライブします。このイベントのハンドラーで矢印を描画します。



 public Gauge() { renderer = DrawingFactory.CreateRenderer(); DataContext = this; this.DefaultStyleKey = typeof(Gauge); arrowStrokeColor = ColorHelper.FromArgb(0xFF, 0xD3, 0xAA, 0xF8); arrowFillColor = ColorHelper.FromArgb(0xFF, 0xD3, 0x65, 0xF8); this.SizeChanged += Gauge_SizeChanged; CompositionTarget.Rendering += CompositionTarget_Rendering; } void Gauge_SizeChanged(object sender, SizeChangedEventArgs e) { renderer.Reset((int)e.NewSize.Width, (int)e.NewSize.Height); rendererSurface.Background = renderer.TargetBrush; } void CompositionTarget_Rendering(object sender, object e) { Render(); }
      
      







次に、 Value依存関係の1つのプロパティを追加して、矢印のジオメトリを描画するだけです。



 static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(Gauge), new PropertyMetadata(0.0, ValuePropertyChanged)); public double Value { get { return (double)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static void ValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Gauge gauge = d as Gauge; if (gauge != null) gauge.renderer.EnableMotionBlur = true; } void Render() { double angle = Math.PI * Value / 1000 - Math.PI / 2.0; IShapeGeometry geometry = CreateArrowGeometry(angle); renderer.BeginDraw(); renderer.MotionBlurAngle = (float)(angle / Math.PI * 180); renderer.MotionBlurDeviation = 5.0f; renderer.Clear(Colors.Transparent); renderer.FillGeometry(arrow, ColorHelper.FromArgb(0xFF, 0xD3, 0x65, 0xF8)); renderer.DrawGeometry(arrow, ColorHelper.FromArgb(0xFF, 0xD3, 0xAA, 0xF8), 3.0f); renderer.EndDraw(); if (renderer.EnableMotionBlur) renderer.EnableMotionBlur = false; }
      
      







以上です。 コンポーネントの準備が完了し、実際のアプリケーションで使用できるようになりました。



WinRT Gaugeコンポーネントの使用



コンポーネントの動作を示すために、別のプロジェクトをソリューションに追加します。 空白のアプリ(XAML)テンプレートとします。 テストプロジェクトにGaugeMatrixという名前を付けましょう







次に、前に作成したGaugeおよびDrawingLayerプロジェクトへのリンクを追加します。







次に、 GridMainPage.xamlに追加して、2つの行と2つの列を作成します。



 <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions>
      
      







その後、 Gaugeコントロールの矢印を使用して表示される値動的に変更するSliderと同様に、 Gaugeコンポーネントを各セルに配置します。



 <Grid Grid.Column="0" Grid.Row="0" Margin="30"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Slider Grid.Row="0" Name="ValueSlider1" Minimum="0" Maximum="1000" Value="0" Width="1000" Margin="50,0,50,0"/> <g:Gauge Grid.Row="1" Value="{Binding ElementName=ValueSlider1, Path=Value}"/> </Grid>
      
      







GaugeMatrixプロジェクトを開始し、アプリケーションを起動します...そして、WinRTアプリケーションでDirect2Dを使用して描画を管理しました。 おめでとうございます!







注:このプロジェクトの完全なコードは、次のリンクからダウンロードできます: go.devexpress.com/Habr_WinRTSample.aspx



PS Windows 8 Campのモスクワで9月7日にWinRTのコンポーネントの開発について質問したいすべての人に会えてうれしいです! このイベントに参加する機会がない場合は、オンラインブロードキャストをフォローしてください。



All Articles