Tuesday, November 26, 2013

A complete example of drawing text using DirectWrite APIs

a complete example of using DirectWrite to draw text. the procedure is to create a customized brush, set text format, and then call the DrawTextLayout()


CComPtr<ID2D1SolidColorBrush>  m_pSolidColorBrush;
CComPtr<IDWriteTextLayout> m_pTextLayout_;
CComPtr<IDWriteFactory> m_pDWriteFactory_;
CComPtr<IDWriteTextFormat> m_pTextFormat_;
const wchar_t* m_wszText_;
UINT32 m_cTextLength_;
D2D1_POINT_2F m_origin;



// the following init are used for text drawing -

       // create a customized brush
       m_hWndTarget->CreateSolidColorBrush(
              D2D1::ColorF(D2D1::ColorF::Purple, 1.0f),
              &m_pSolidColorBrush
              );

       D2D1_SIZE_F rtSize = m_hWndTarget->GetSize();
       int width = static_cast<int>(rtSize.width);
       int height = static_cast<int>(rtSize.height);         
       // draw text now
       m_origin = D2D1::Point2F(
              static_cast<FLOAT>(rect.left / 4),
              static_cast<FLOAT>(rect.top / 4)
              );
                    
       m_wszText_ = L"Hello World using  DirectWrite!";
       m_cTextLength_ = (UINT32)wcslen(m_wszText_);

       DWriteCreateFactory(
              DWRITE_FACTORY_TYPE_SHARED,
              __uuidof(IDWriteFactory),
              reinterpret_cast<IUnknown**>(&m_pDWriteFactory_)
              );

       //create a customized text format
       m_pDWriteFactory_->CreateTextFormat(
              L"Gabriola",                // Font family name.
              NULL,                       // Font collection (NULL sets it to use the system font collection).
              DWRITE_FONT_WEIGHT_REGULAR,
              DWRITE_FONT_STYLE_NORMAL,
              DWRITE_FONT_STRETCH_NORMAL,
              32.0f, //72.0f,  // font size
              L"en-us",
              &m_pTextFormat_
              );

       //create text layout
       m_pDWriteFactory_->CreateTextLayout(
              m_wszText_,      // The string to be laid out and formatted.
              m_cTextLength_,  // The length of the string.
              m_pTextFormat_,  // The text format to apply to the string (contains font information, etc).
              width,         // The width of the layout box.
              height,        // The height of the layout box.
              &m_pTextLayout_  // The IDWriteTextLayout interface pointer.
              );

//jamesz: actual text drawing function here, can call this once per frames
              if (1)
              {
                     m_hWndTarget->DrawTextLayout(
                           m_origin,
                           m_pTextLayout_,
                           m_pSolidColorBrush
                           );                  

              }

How to create WCHAR string

const wchar_t* wszText_;
UINT32 cTextLength_;
wszText_ = L"Hello World using  DirectWrite!";

cTextLength_ = (UINT32)wcslen(wszText_);

How to create drawing brush and draw a colored block in windows

http://msdn.microsoft.com/en-us/library/windows/desktop/dd756651(v=vs.85).aspx

m_hWndTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
m_hWndTarget->DrawBitmap(m_bitmap, rectangle);


//this function needs to be called between BegeinDraw() and EndDraw()
if (1)
{
ID2D1SolidColorBrush*  m_pSolidColorBrush;
m_hWndTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Purple, 1.0f),
&m_pSolidColorBrush
);

D2D1_SIZE_F rtSize = m_hWndTarget->GetSize();
// Draw a grid background.
int width = static_cast<int>(rtSize.width);
int height = static_cast<int>(rtSize.height);
// Draw two rectangles.
D2D1_RECT_F rectangle1 = D2D1::RectF(
rtSize.width / 2 - 50.0f,
rtSize.height / 2 - 50.0f,
rtSize.width / 2 + 50.0f,
rtSize.height / 2 + 50.0f
);

m_hWndTarget->FillRectangle(&rectangle1, m_pSolidColorBrush);
}



HRESULT hr = m_hWndTarget->EndDraw();

How to draw text in Windows (simple API)



HDC hdc;
hdc = GetDC(m_hWnd);

RECT r;
GetClientRect(m_hWnd, &r);


ExtTextOut(hdc, 500, 500, ETO_CLIPPED, &r, L"test", 4, NULL);

old fashion open/write file in c code

Just a note to remind myself ...

char buffer[50];
sprintf(buffer, "james: %dms passed ...\r\n", g_tick2 - g_tick1);
FILE *fp;
fp = fopen("c:\\temp\\log.txt", "a");
fputs(buffer,fp);

fclose(fp);

Monday, November 25, 2013

Windows Timer Resolution problem

http://download.microsoft.com/download/3/0/2/3027D574-C433-412A-A8B6-5E0A75D5B237/Timer-Resolution.docx

"The default timer resolution on Windows 7 is 15.6 milliseconds (ms). Some applications reduce this to 1 ms, which reduces the battery run time on mobile systems by as much as 25 percent."

This is a crazy setup. I understand Windows is not a realtime system but still limiting the timer resolution to 15.6ms is a big limitation for application developers. 

How to use Windows Timer API

To create a timer:

   SetTimer(g_AppWindow,             // handle to main window
              IDT_TIMER1,            // timer identifier
              //1000/60,                 // 1/60-second interval
              41,//1000/24,                 // 1/24-second interval
              (TIMERPROC)NULL);     // no timer callback

To use the timer to do sth:
case WM_TIMER:
  switch (LOWORD(wParam))
  {
      case IDT_TIMER1:
             // do sth here

             // time your code
             g_tick2 = g_pfnGetTickCount();
             if (1)
             {

               WCHAR buffer[50];
               swprintf_s(buffer, 50, L"Debug: %d ms passed ...\n\0",
                  (g_tick2 - g_tick1));

               OutputDebugStringW(buffer); 
             }

             g_tick1 = g_tick2;

How to create fullscreen window without title bar

How to create fullscreen window without title bar

The use of WS_POPUP | WS_MAXIMIZEBOX sets the window properties and allowing us to create fullscreen window without title bar.

Note that GetSystemMetrics() with SM_CXSCREEN returns the screen resolution of the primary monitor.


HWND CVideoWindow::InitInstance(LPCWSTR windowName, int width, int height)
{
       int x = ::GetSystemMetrics(SM_CXSCREEN) / 2 - width / 2;
       int y = ::GetSystemMetrics(SM_CYSCREEN) / 2 - height / 2;

      
       m_hWnd = CreateWindow(L"MyWindow", windowName, WS_POPUP | WS_MAXIMIZEBOX, x, y, width, height, NULL, NULL, NULL, NULL);

    return m_hWnd;

}

Friday, November 22, 2013

Output debug data to debug windows in Visual C++

//test
g_tick1 = g_tick2;
g_tick2 = g_pfnGetTickCount();
if (1)
{

WCHAR buffer[50];
swprintf_s(buffer, 50, L"james %d ms passed: --\n\0",
(g_tick2 - g_tick1));

OutputDebugStringW(buffer);

}


time the code, and send to the debug window

DirectShow filter, video renderer and filter graph manager

DirectShow video renderer and filter graph manager

The filter graph manager (FGM) may include several DirecthShow filters. The FGM can be constructed, controlled and its event can be captured and processed at application level.

IGraphBuilder*       g_pGraphBuilder = NULL;  // to access the FGM
IMediaControl*       g_pMediaControl = NULL;  // to control the media, run/stop etc.
IMediaEventEx*       g_pMediaEvent = NULL;    // to get event and notifications from FGM
IMediaPosition*      g_pMediaPosition = NULL;   //   to do seek function

// the following code does the init()
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder,
        (void**)&g_pGraphBuilder);
if (FAILED(hr))        return -1;
g_pGraphBuilder->QueryInterface(IID_IMediaControl, (void**)&g_pMediaControl);
g_pGraphBuilder->QueryInterface(IID_IMediaEvent, (void**)&g_pMediaEvent);
g_pGraphBuilder->QueryInterface(IID_IMediaPosition, (void**)&g_pMediaPosition);

g_pMediaEvent->SetNotifyWindow((OAHWND)g_AppWindow, WM_GRAPHEVENT, 0); // set the current window to handle GFM events
g_pMediaEvent->SetNotifyFlags(0); // turn on notifications

//in the following WindowProc() callfaback function,

#define WM_GRAPHEVENT      WM_USER              // define a custom window message for graph events

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
    {
    case WM_COMMAND// handles windows commands and messages
        switch (LOWORD(wParam))
        {
        case ID_FILE_OPEN:
                     …
}

    case WM_GRAPHEVENT:  // handles user defined msg, here generated by FGM
       OnGraphEvent();            
       break;   

}


//in the OnGraphEvent() we handle all the filter graph events at application level
void OnGraphEvent()
{
       long EventCode, Param1, Param2;

       g_pMediaEvent->CancelDefaultHandling(EC_REPAINT);  // test by james, to remove

       while (g_pMediaEvent->GetEvent(&EventCode, &Param1, &Param2, 0) != E_ABORT)
       {
              switch (EventCode)
              {
        case EC_COMPLETE:  
            if (!g_Looping)
                g_pMediaControl->Stop();
            g_pMediaPosition->put_CurrentPosition(0);   // reset to beginning
            break;
       case EC_PAUSED// just notification only to app. FGM already paused the video
              Break

       case EC_CONTENTPROPERTY_CHANGED// my user defined msg              OutputDebugStringW(L"EC_CONTENTPROPERTY_CHANGED recieved - james.\n");
              break;

default:
             
              break;
}

       g_pMediaEvent->FreeEventParams(EventCode, Param1, Param2);
}



Note here I captured CONTENTPROPERTY_CHANGED event here at application level. This event by default is not supported in directshow, but we could use this as user defined message. The message-data-flow will be something like below.
DirectShow filters --> Filter Graph Manager --> Application

Directshow filter such as a video renderer can generate a notification by calling the function NotifyEvent(EC_CONTENTPROPERTY_CHANGED, S_OK, 0) inside the DirectShow filter; Then, this will send notification to filter graph manager. For most messages and notifications, normally FGM will process or hide some messages such as EC_REPAINT notifications to application layer since usually this is transparent to applications. But for event such as EC_COMPLETE, FGM will forward this notification to upper levels so that application layer can capture this event, as shown in the code above. Here I am using a special event code CONTENTPROPERTY_CHANGED to implement my own task, because I know FGM will not hide or do anything when my renderer filter send this notification to FGM. And then at application layer, I can capture this event. The reason I am doing this is because for every DoRenderSample() function call, I want to notify the application layer that a video frame has changed, and then I might want to do something for example doing backlight control per frame basis. The reason why I do not want to do backlight control inside the directshow video renderer filter is because the communication to my i2c device seems to stall the video playback and affect the playback performance. So I prefer to do this separate control using a different thread at application layer, not creating any impact on the video playback.

The cleanup code is shown below.
void CleanUpDirectShow()
{
    HELPER_RELEASE(g_pMediaPosition);
    HELPER_RELEASE(g_pMediaEvent);
    HELPER_RELEASE(g_pMediaControl);
    HELPER_RELEASE(g_pGraphBuilder);

}