text: // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  // PARTICULAR PURPOSE.
  //
  // Copyright (c) Microsoft Corporation. All rights reserved
  
  #include "stdafx.h"
  
  static const PCWSTR sc_fontName = L"Calibri";
  
  static const float sc_fontSize = 20.0f;
  
  static const D2D1_RECT_F sc_textInfoBox = {10, 10, 350, 200 };
  static const float sc_textInfoBoxInset = 10;
  
  static const UINT sc_defaultNumSquares = 16;
  static const UINT sc_minNumSquares = 1;
  static const UINT sc_maxNumSquares = 1024;
  
  static const float sc_boardWidth = 900.0f;
  
  static const float sc_rotationSpeed = 3.0f;
  
  static const float sc_loupeInset = 20.0f;
  
  static const float sc_maxZoom = 15.0f;
  static const float sc_minZoom = 1.0f;
  static const float sc_zoomStep = 1.5f;
  static const float sc_zoomSubStep = 1.1f;
  
  static const float sc_strokeWidth = 1.0f;
  
  // This determines that maximum texture size we will
  // generate for our realizations.
  static const UINT sc_maxRealizationDimension = 2000;
  
  /******************************************************************
  *                                                                 *
  *  WinMain                                                        *
  *                                                                 *
  *  Application entrypoint                                         *
  *                                                                 *
  ******************************************************************/
  
  //int WINAPI WinMain(
  //    HINSTANCE /*hInstance*/,
  //    HINSTANCE /*hPrevInstance*/,
  //    LPSTR /*lpCmdLine*/,
  //    int /*nCmdShow*/
  //    )
  //{
  //    // Ignore the return value because we want to continue running even in the
  //    // unlikely event that HeapSetInformation fails.
  //    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
  //
  //    if (SUCCEEDED(CoInitialize(NULL)))
  //    {
  //        {
  //            DemoApp app;
  //            if (SUCCEEDED(app.Initialize()))
  //            {
  //                app.RunMessageLoop();
  //            }
  //        }
  //        CoUninitialize();
  //    }
  //
  //    return 0;
  //}
  
  extern "C" HWND WINAPI CreateDemoHelloWorldApp(HWND parent)
  {
  	DemoApp *app = new DemoApp();
  
      if (SUCCEEDED(app->Initialize(parent)))
      {
          //app.RunMessageLoop();
  		HWND x = app->GetHwnd();
  		return x;
      }
  	return 0;
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::DemoApp constructor                                   *
  *                                                                 *
  *  Initialize member data                                         *
  *                                                                 *
  ******************************************************************/
  
  DemoApp::DemoApp() :
      m_hwnd(NULL),
      m_antialiasMode(m_antialiasMode),
      m_useRealizations(false),
      m_updateRealization(true),
      m_drawStroke(true),
      m_autoGeometryRegen(true),
      m_paused(false),
      m_pausedTime(0),
      m_numSquares(sc_defaultNumSquares),
      m_targetZoomFactor(1.0f),
      m_currentZoomFactor(1.0f),
      m_pD2DFactory(NULL),
      m_pWICFactory(NULL),
      m_pDWriteFactory(NULL),
      m_pRT(NULL),
      m_pTextFormat(NULL),
      m_pSolidColorBrush(NULL),
      m_pRealization(NULL),
      m_pGeometry(NULL)
  {
      LARGE_INTEGER time;
      QueryPerformanceCounter(&time);
  
      m_timeDelta = -time.QuadPart;
  
      m_mousePos = D2D1::Point2F(0.0f, 0.0f);
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::~DemoApp destructor                                   *
  *                                                                 *
  *  Tear down resources                                            *
  *                                                                 *
  ******************************************************************/
  
  DemoApp::~DemoApp()
  {
      SafeRelease(&m_pD2DFactory);
      SafeRelease(&m_pWICFactory);
      SafeRelease(&m_pDWriteFactory);
      SafeRelease(&m_pRT);
      SafeRelease(&m_pTextFormat);
      SafeRelease(&m_pSolidColorBrush);
      SafeRelease(&m_pRealization);
      SafeRelease(&m_pGeometry);
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::Initialize                                            *
  *                                                                 *
  *  Create application window and device-independent resources     *
  *                                                                 *
  ******************************************************************/
  
  HRESULT DemoApp::Initialize(HWND parent)
  {
      HRESULT hr;
  
      hr = CreateDeviceIndependentResources();
      if (SUCCEEDED(hr))
      {
          //register window class
          WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
          wcex.style         = CS_HREDRAW | CS_VREDRAW;
          wcex.lpfnWndProc   = DemoApp::WndProc;
          wcex.cbClsExtra    = 0;
          wcex.cbWndExtra    = sizeof(LONG_PTR);
          wcex.hInstance     = HINST_THISCOMPONENT;
          wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
          wcex.hbrBackground = NULL;
          wcex.lpszMenuName  = NULL;
          wcex.lpszClassName = L"D2DDemoApp";
  
          RegisterClassEx(&wcex);
  
          // Create the application window.
          //
          // Because the CreateWindow function takes its size in pixels, we
          // obtain the system DPI and use it to scale the window size.
          FLOAT dpiX, dpiY;
          m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);
  
          m_hwnd = CreateWindow(
              L"D2DDemoApp",
              L"D2D Demo App",
              WS_CHILD,
              CW_USEDEFAULT,
              CW_USEDEFAULT,
              static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
              static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
              parent,
              NULL,
              HINST_THISCOMPONENT,
              this
              );
  
          hr = m_hwnd ? S_OK : E_FAIL;
          //if (SUCCEEDED(hr))
          //{
          //    ShowWindow(m_hwnd, SW_SHOWNORMAL);
          //    UpdateWindow(m_hwnd);
          //}
      }
  
      return hr;
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::CreateDeviceIndependentResources                      *
  *                                                                 *
  *  This method is used to create resources which are not bound    *
  *  to any device. Their lifetime effectively extends for the      *
  *  duration of the app. These resources include the D2D,          *
  *  DWrite, and WIC factories; and a DWrite Text Format object     *
  *  (used for identifying particular font characteristics) and     *
  *  a D2D geometry.                                                *
  *                                                                 *
  ******************************************************************/
  
  HRESULT DemoApp::CreateDeviceIndependentResources()
  {
      HRESULT hr = S_OK;
  
      // Create the Direct2D factory.
      hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
  
      if (SUCCEEDED(hr))
      {
          // Create a WIC factory.
          hr = CoCreateInstance(
              CLSID_WICImagingFactory,
              NULL,
              CLSCTX_INPROC_SERVER,
              IID_IWICImagingFactory,
              reinterpret_cast<void **>(&m_pWICFactory)
              );
      }
  
      if (SUCCEEDED(hr))
      {
          // Create a DirectWrite factory.
          hr = DWriteCreateFactory(
              DWRITE_FACTORY_TYPE_SHARED,
              __uuidof(m_pDWriteFactory),
              reinterpret_cast<IUnknown **>(&m_pDWriteFactory)
              );
      }
  
      if (SUCCEEDED(hr))
      {
          // Create a DirectWrite text format object.
          hr = m_pDWriteFactory->CreateTextFormat(
              sc_fontName,
              NULL,
              DWRITE_FONT_WEIGHT_NORMAL,
              DWRITE_FONT_STYLE_NORMAL,
              DWRITE_FONT_STRETCH_NORMAL,
              static_cast<FLOAT>(sc_fontSize),
              L"", //locale
              &m_pTextFormat
              );
      }
  
      return hr;
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::CreateDeviceResources                                 *
  *                                                                 *
  *  This method creates resources which are bound to a particular  *
  *  D3D device. It's all centralized here, in case the resources   *
  *  need to be recreated in case of D3D device loss (eg. display   *
  *  change, remoting, removal of video card, etc).                 *
  *                                                                 *
  ******************************************************************/
  
  HRESULT DemoApp::CreateDeviceResources()
  {
      HRESULT hr = S_OK;
  
      if (!m_pRT)
      {
          IGeometryRealizationFactory *pRealizationFactory = NULL;
  
          RECT rc;
          GetClientRect(m_hwnd, &rc);
  
          D2D1_SIZE_U size = D2D1::SizeU(
              rc.right - rc.left,
              rc.bottom - rc.top
              );
  
  		D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties = D2D1::RenderTargetProperties();
  		//renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
          // Create a Direct2D render target.
          hr = m_pD2DFactory->CreateHwndRenderTarget(
              renderTargetProperties,
              D2D1::HwndRenderTargetProperties(m_hwnd, size),
              &m_pRT
              );
          if (SUCCEEDED(hr))
          {
              // Create brushes.
              hr = m_pRT->CreateSolidColorBrush(
                  D2D1::ColorF(D2D1::ColorF::White),
                  &m_pSolidColorBrush
                  );
          }
          if (SUCCEEDED(hr))
          {
              hr = CreateGeometryRealizationFactory(
                  m_pRT,
                  sc_maxRealizationDimension,
                  &pRealizationFactory
                  );
          }
          if (SUCCEEDED(hr))
          {
              hr = pRealizationFactory->CreateGeometryRealization(&m_pRealization);
          }
          if (SUCCEEDED(hr))
          {
              m_updateRealization = true;
          }
  
          SafeRelease(&pRealizationFactory);
      }
  
      return hr;
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::DiscardDeviceResources                                *
  *                                                                 *
  *  Discard device-specific resources which need to be recreated   *
  *  when a Direct3D device is lost.                                *
  *                                                                 *
  ******************************************************************/
  
  void DemoApp::DiscardDeviceResources()
  {
      SafeRelease(&m_pRT);
      SafeRelease(&m_pSolidColorBrush);
      SafeRelease(&m_pRealization);
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::DiscardGeometryData                                   *
  *                                                                 *
  ******************************************************************/
  
  void DemoApp::DiscardGeometryData()
  {
      SafeRelease(&m_pGeometry);
  
      m_updateRealization = true;
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::RunMessageLoop                                        *
  *                                                                 *
  *  Main window message loop                                       *
  *                                                                 *
  ******************************************************************/
  
  void DemoApp::RunMessageLoop()
  {
      MSG msg;
  
      while (GetMessage(&msg, NULL, 0, 0))
      {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
      }
  }
  
  
  HRESULT DemoApp::CreateGeometries()
  {
      HRESULT hr = S_OK;
  
      if (!m_pGeometry)
      {
          IGeometryRealizationFactory *pRealizationFactory = NULL;
          IGeometryRealization *pRealization = NULL;
  
          ID2D1TransformedGeometry *pGeometry = NULL;
          ID2D1PathGeometry *pPathGeometry = NULL;
          ID2D1GeometrySink *pSink = NULL;
  
          float squareWidth = 0.9f * sc_boardWidth / m_numSquares;
  
          // Create the path geometry.
          hr = m_pD2DFactory->CreatePathGeometry(&pPathGeometry);
          if (SUCCEEDED(hr))
          {
              // Write to the path geometry using the geometry sink to 
              // create an hour glass shape.
              hr = pPathGeometry->Open(&pSink);
          }
          if (SUCCEEDED(hr))
          {
              pSink->SetFillMode(D2D1_FILL_MODE_ALTERNATE);
  
              pSink->BeginFigure(
                  D2D1::Point2F(0, 0),
                  D2D1_FIGURE_BEGIN_FILLED
                  );
  
              pSink->AddLine(D2D1::Point2F(1.0f, 0));
  
              pSink->AddBezier(
                  D2D1::BezierSegment(
                      D2D1::Point2F(0.75f, 0.25f),
                      D2D1::Point2F(0.75f, 0.75f),
                      D2D1::Point2F(1.0f, 1.0f))
                  );
  
              pSink->AddLine(D2D1::Point2F(0, 1.0f));
  
              pSink->AddBezier(
                  D2D1::BezierSegment(
                      D2D1::Point2F(0.25f, 0.75f),
                      D2D1::Point2F(0.25f, 0.25f),
                      D2D1::Point2F(0, 0))
                  );
  
              pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
  
              hr = pSink->Close();
          }
  
          if (SUCCEEDED(hr))
          {
              D2D1_MATRIX_3X2_F scale = D2D1::Matrix3x2F::Scale(squareWidth, squareWidth);
              D2D1_MATRIX_3X2_F translation = D2D1::Matrix3x2F::Translation(-squareWidth / 2, -squareWidth / 2);
  
              hr = m_pD2DFactory->CreateTransformedGeometry(
                      pPathGeometry,
                      scale * translation,
                      &pGeometry
                      );
          }
  
          if (SUCCEEDED(hr))
          {
              // Transfer the reference.
              m_pGeometry = pGeometry;
              pGeometry = NULL;
          }
  
          SafeRelease(&pRealizationFactory);
          SafeRelease(&pRealization);
          SafeRelease(&pGeometry);
          SafeRelease(&pPathGeometry);
          SafeRelease(&pSink);
      }
  
      return hr;
  }
  
  
  HRESULT DemoApp::RenderMainContent(float time)
  {
      HRESULT hr = S_OK;
  
      static DWORD dwTimeStart = 0;
      static DWORD dwTimeLast = 0;
  
      D2D1_SIZE_F rtSize = m_pRT->GetSize();
  
      float squareWidth = sc_boardWidth / m_numSquares;
  
      m_pRT->SetAntialiasMode(m_antialiasMode);
  
      m_pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black));
  
      D2D1_MATRIX_3X2_F currentTransform;
  
      m_pRT->GetTransform(&currentTransform);
  
      D2D1_MATRIX_3X2_F worldTransform =
          D2D1::Matrix3x2F::Translation(
              0.5f*(rtSize.width - squareWidth*m_numSquares),
              0.5f*(rtSize.height - squareWidth*m_numSquares)
              ) * currentTransform;
  
      for (UINT i = 0; SUCCEEDED(hr) && (i < m_numSquares); ++i)
      {
          for (UINT j = 0; SUCCEEDED(hr) && (j < m_numSquares); ++j)
          {
              float dx = i+0.5f-0.5f*m_numSquares;
              float dy = j+0.5f-0.5f*m_numSquares;
  
              float length = sqrtf(2)*m_numSquares;
  
              //
              // The intensity variable determines the color and speed of rotation of the
              // realization instance. We choose a function that is rotationaly
              // symmetric about the center of the grid, which produces a nice
              // effect.
              //
              float intensity =
                  0.5f * (1+sinf( (0.2f * time + 10.0f * sqrtf(static_cast<float>(dx*dx+dy*dy))/length)));
  
              D2D1_MATRIX_3X2_F rotateTransform =
                  D2D1::Matrix3x2F::Rotation(
                      (intensity * sc_rotationSpeed * time * 360.0f) * ((float)M_PI / 180.0f)
                      );
  
              D2D1_MATRIX_3X2_F newWorldTransform =
                  rotateTransform *
                  D2D1::Matrix3x2F::Translation(
                      (i+0.5f)*squareWidth,
                      (j+0.5f)*squareWidth
                      ) * worldTransform;
  
              if (m_updateRealization)
              {
                  //
                  // Note: It would actually be a little simpler to generate our
                  // realizations prior to entering RenderMainContent. We instead
                  // generate the realizations based on the top-left primitive in
                  // the grid, so we can illustrate the fact that realizations
                  // appear identical to their unrealized counter-parts when the
                  // exact same world transform is supplied. Only the top left
                  // realization will look identical, though, as shifting or
                  // rotating an AA realization can introduce fuzziness.
                  //
                  // Realizations are regenerated every frame, so to
                  // demonstrate that the realization geometry produces identical
                  // results, you actually need to pause (<space>), which forces
                  // a regeneration.
                  //
                  hr = m_pRealization->Update(
                      m_pGeometry,
                      static_cast<REALIZATION_CREATION_OPTIONS>(
                          REALIZATION_CREATION_OPTIONS_ANTI_ALIASED |
                          REALIZATION_CREATION_OPTIONS_ALIASED |
                          REALIZATION_CREATION_OPTIONS_FILLED |
                          REALIZATION_CREATION_OPTIONS_STROKED |
                          REALIZATION_CREATION_OPTIONS_UNREALIZED
                          ),
                      &newWorldTransform,
                      sc_strokeWidth,
                      NULL //pIStrokeStyle
                      );
                  if (SUCCEEDED(hr))
                  {
                      m_updateRealization = false;
                  }
              }
              if (SUCCEEDED(hr))
              {
                  m_pRT->SetTransform(newWorldTransform);
                  m_pSolidColorBrush->SetColor(
                      D2D1::ColorF(
                          0.0f,
                          intensity,
                          1.0f - intensity
                          ));
  
                  hr = m_pRealization->Fill(
                          m_pRT,
                          m_pSolidColorBrush,
                          m_useRealizations ?
                              REALIZATION_RENDER_MODE_DEFAULT :
                              REALIZATION_RENDER_MODE_FORCE_UNREALIZED
                          );
                  if (SUCCEEDED(hr) && m_drawStroke)
                  {
                      m_pSolidColorBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White));
  
                      hr = m_pRealization->Draw(
                              m_pRT,
                              m_pSolidColorBrush,
                              m_useRealizations ?
                                  REALIZATION_RENDER_MODE_DEFAULT :
                                  REALIZATION_RENDER_MODE_FORCE_UNREALIZED
                              );
                  }
              }
          }
      }
  
      return hr;
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::OnRender                                              *
  *                                                                 *
  *  Called whenever the application needs to display the client    *
  *  window. This method draws the main content (a 2D array of      *
  *  spinning geometries) and some perf statistics.                 *
  *                                                                 *
  *  Note that this function will automatically discard device-     *
  *  specific resources if the D3D device disappears during         *
  *  function invocation, and will recreate the resources the next  *
  *  time it's invoked.                                             *
  *                                                                 *
  ******************************************************************/
  
  HRESULT DemoApp::OnRender()
  {
      HRESULT hr = S_OK;
  
      LARGE_INTEGER time;
      LARGE_INTEGER frequency;
      QueryPerformanceCounter(&time);
      QueryPerformanceFrequency(&frequency);
  
      float floatTime;
  
      hr = CreateDeviceResources();
      if (SUCCEEDED(hr))
      {
          if (!m_paused)
          {
              floatTime = static_cast<float>(time.QuadPart + m_timeDelta)/static_cast<float>(frequency.QuadPart);
          }
          else
          {
              floatTime = static_cast<float>(m_pausedTime + m_timeDelta)/static_cast<float>(frequency.QuadPart);
          }
  
          m_times.Add(time.QuadPart);
  
          if (m_currentZoomFactor < m_targetZoomFactor)
          {
              m_currentZoomFactor *= sc_zoomSubStep;
  
              if (m_currentZoomFactor > m_targetZoomFactor)
              {
                  m_currentZoomFactor = m_targetZoomFactor;
  
                  if (m_autoGeometryRegen)
                  {
                      m_updateRealization = true;
                  }
              }
          }
          else if (m_currentZoomFactor > m_targetZoomFactor)
          {
              m_currentZoomFactor /= sc_zoomSubStep;
  
              if (m_currentZoomFactor < m_targetZoomFactor)
              {
                  m_currentZoomFactor = m_targetZoomFactor;
  
                  if (m_autoGeometryRegen)
                  {
                      m_updateRealization = true;
                  }
              }
          }
  
          m_pRT->SetTransform(
              D2D1::Matrix3x2F::Scale(
                  m_currentZoomFactor,
                  m_currentZoomFactor,
                  m_mousePos)
              );
  
          hr = CreateGeometries();
  
          if (SUCCEEDED(hr))
          {
              m_pRT->BeginDraw();
  
              hr = RenderMainContent(floatTime);
              if (SUCCEEDED(hr))
              {
                  hr = RenderTextInfo();
                  if (SUCCEEDED(hr))
                  {
                      hr = m_pRT->EndDraw();
                      if (SUCCEEDED(hr))
                      {
                          if (hr == D2DERR_RECREATE_TARGET)
                          {
                              DiscardDeviceResources();
                          }
                      }
                  }
              }
          }
      }
  
      return hr;
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::RenderTextInfo                                        *
  *                                                                 *
  *  Draw the stats text (AA type, fps, etc...).                    *
  *                                                                 *
  ******************************************************************/
  
  HRESULT DemoApp::RenderTextInfo()
  {
      HRESULT hr = S_OK;
  
      WCHAR textBuffer[400];
      LARGE_INTEGER frequency;
      float fps = 0.0f;
      float primsPerSecond = 0.0f;
  
      QueryPerformanceFrequency(&frequency);
  
      UINT numPrimitives = m_numSquares * m_numSquares;
  
      if (m_drawStroke)
      {
          numPrimitives *= 2;
      }
  
      if (m_times.GetCount() > 0)
      {
          fps = (m_times.GetCount()-1) * frequency.QuadPart /
                  static_cast<float>((m_times.GetLast() - m_times.GetFirst()));
  
          primsPerSecond = fps * numPrimitives;
      }
  
      hr = StringCchPrintf(
          textBuffer,
          400,
          L"%s\n"
          L"%s\n"
          L"%s\n"
          L"# primitives: %d x %d%s = %d\n"
          L"Fps: %.2f\n"
          L"Primitives / sec : %.0f\n",
          m_antialiasMode == D2D1_ANTIALIAS_MODE_ALIASED ?
               L"Aliased" : L"PerPrimitive",
          m_useRealizations ?
              L"Realized"  : L"Unrealized",
          m_autoGeometryRegen?
              L"Auto Realization Regeneration"  : L"No Auto Realization Regeneration",
          m_numSquares,
          m_numSquares,
          m_drawStroke ? L" x 2" : L"",
          numPrimitives,
          fps,
          primsPerSecond
          );
      if (SUCCEEDED(hr))
      {
          m_pRT->SetTransform(D2D1::Matrix3x2F::Identity());
  
          m_pSolidColorBrush->SetColor(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.5f));
  
          m_pRT->FillRoundedRectangle(
              D2D1::RoundedRect(
                  sc_textInfoBox,
                  sc_textInfoBoxInset,
                  sc_textInfoBoxInset),
              m_pSolidColorBrush
              );
  
          m_pSolidColorBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White));
  
          m_pRT->DrawText(
              textBuffer,
              static_cast<UINT>(wcsnlen(textBuffer, ARRAYSIZE(textBuffer))),
              m_pTextFormat,
              D2D1::RectF(
                  sc_textInfoBox.left + sc_textInfoBoxInset,
                  sc_textInfoBox.top + sc_textInfoBoxInset,
                  sc_textInfoBox.right - sc_textInfoBoxInset,
                  sc_textInfoBox.bottom - sc_textInfoBoxInset),
              m_pSolidColorBrush,
              D2D1_DRAW_TEXT_OPTIONS_NONE
              );
      }
  
      return hr;
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::OnResize                                              *
  *                                                                 *
  *  If the application receives a WM_SIZE message, this method     *
  *  resizes the render target appropriately.                       *
  *                                                                 *
  ******************************************************************/
  
  void DemoApp::OnResize(UINT width, UINT height)
  {
      if (m_pRT)
      {
          D2D1_SIZE_U size;
          size.width = width;
          size.height = height;
  
          // Note: This method can fail, but it's okay to ignore the
          // error here -- it will be repeated on the next call to
          // EndDraw.
          m_pRT->Resize(size);
      }
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::OnKeyDown                                             *
  *                                                                 *
  ******************************************************************/
  
  void DemoApp::OnKeyDown(SHORT vkey)
  {
      switch (vkey)
      {
      case 'A':
          m_antialiasMode =
              (m_antialiasMode == D2D1_ANTIALIAS_MODE_ALIASED) ?
                  D2D1_ANTIALIAS_MODE_PER_PRIMITIVE :
                  D2D1_ANTIALIAS_MODE_ALIASED;
          break;
  
      case 'R':
          m_useRealizations = !m_useRealizations;
          break;
  
      case 'G':
          m_autoGeometryRegen = !m_autoGeometryRegen;
          break;
  
      case 'S':
          m_drawStroke = !m_drawStroke;
          break;
  
      case VK_SPACE:
  
          LARGE_INTEGER time;
          QueryPerformanceCounter(&time);
  
          if (!m_paused)
          {
              m_pausedTime = time.QuadPart;
          }
          else
          {
              m_timeDelta += (m_pausedTime - time.QuadPart);
          }
  
          m_paused = !m_paused;
          m_updateRealization = true;
  
          break;
  
      case VK_UP:
          m_numSquares = min(m_numSquares * 2, sc_maxNumSquares);
  
          // Regenerate the geometries.
          DiscardGeometryData();
          break;
  
      case VK_DOWN:
          m_numSquares = max(m_numSquares / 2, sc_minNumSquares);
  
          // Regenerate the geometries.
          DiscardGeometryData();
          break;
  
      default:
          break;
      }
  }
  
  /******************************************************************
  *                                                                 *
  *  WindowPointToTargetPoint                                       *
  *                                                                 *
  *  Converts a window point to a D2D point, accounting for DPI     *
  *  and RTL differences                                            *
  *                                                                 *
  ******************************************************************/
  D2D1_POINT_2F WindowPointToTargetPoint(
      INT x,
      INT y,
      float dpiX,
      float dpiY,
      HWND hwnd
      )
  {
      // On RTL builds, coordinates may need to be restored to or converted
      // from Cartesian coordinates, where x increases positively to the right.
      if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
      {
          RECT rect;
          GetClientRect(hwnd, &rect);
          x = rect.right - x - 1;
      }
  
      return D2D1::Point2F(
          x * 96.0f / dpiX,
          y * 96.0f / dpiY
          );
  }
  
  /******************************************************************
  *                                                                 *
  *  OnMouseMove                                                    *
  *                                                                 *
  ******************************************************************/
  
  void DemoApp::OnMouseMove(LPARAM lParam)
  {
      float dpiX = 96.0f;
      float dpiY = 96.0f;
  
      if (m_pRT)
      {
          m_pRT->GetDpi(&dpiX, &dpiY);
      }
  
      m_mousePos = WindowPointToTargetPoint(
          LOWORD(lParam),
          HIWORD(lParam),
          dpiX,
          dpiY,
          m_hwnd);
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::OnWheel                                               *
  *                                                                 *
  ******************************************************************/
  
  void DemoApp::OnWheel(WPARAM wParam)
  {
      m_targetZoomFactor *=
          pow(sc_zoomStep, GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
  
      m_targetZoomFactor = min(
          max(m_targetZoomFactor, sc_minZoom),
          sc_maxZoom
          );
  }
  
  /******************************************************************
  *                                                                 *
  *  DemoApp::WndProc                                               *
  *                                                                 *
  *  Window message handler                                         *
  *                                                                 *
  ******************************************************************/
  
  LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
      LRESULT result = 0;
  
      if (message == WM_CREATE)
      {
          LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
          DemoApp *pDemoApp = (DemoApp *)pcs->lpCreateParams;
  
          ::SetWindowLongPtrW(
              hwnd,
              GWLP_USERDATA,
              PtrToUlong(pDemoApp)
              );
  
          result = 1;
      }
      else
      {
          DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(static_cast<LONG_PTR>(
              ::GetWindowLongPtrW(
                  hwnd,
                  GWLP_USERDATA
                  )));
  
          bool wasHandled = false;
  
          if (pDemoApp)
          {
              switch (message)
              {
              case WM_SIZE:
                  {
                      UINT width = LOWORD(lParam);
                      UINT height = HIWORD(lParam);
                      pDemoApp->OnResize(width, height);
                  }
                  result = 0;
                  wasHandled = true;
                  break;
  
              case WM_DISPLAYCHANGE:
                  {
                      InvalidateRect(hwnd, NULL, FALSE);
                  }
                  result = 0;
                  wasHandled = true;
                  break;
  
              case WM_PAINT:
                  {
                      pDemoApp->OnRender();
  
                      // Do not call ValidateRect so that the window will be redrawn.
                  }
                  result = 0;
                  wasHandled = true;
                  break;
  
              case WM_KEYDOWN:
                  {
                      pDemoApp->OnKeyDown(static_cast<SHORT>(wParam));
                  }
                  result = 0;
                  wasHandled = true;
                  break;
  
              case WM_MOUSEMOVE:
                  {
                      pDemoApp->OnMouseMove(lParam);
                  }
                  result = 0;
                  wasHandled = true;
                  break;
  
              case WM_MOUSEWHEEL:
                  {
                      pDemoApp->OnWheel(wParam);
                  }
                  result = 0;
                  wasHandled = true;
                  break;
  
              case WM_DESTROY:
                  {
                      PostQuitMessage(0);
                  }
                  result = 1;
                  wasHandled = true;
                  break;
              }
          }
  
          if (!wasHandled)
          {
              result = DefWindowProc(hwnd, message, wParam, lParam);
          }
      }
  
      return result;
  }
  