How to Rotate Text using GDI

 

Most of us used DrawText or TextOut functions of GDI to draw text with different fonts and colors. Have you ever tried to rotate text using GDI?

OK. Let’s see how we can do that. Windows GDI provides option to specify the angle for a row of text and again we can set angle even for the characters in the same string. Where we can set this information. The thing is that, we’ve to create a new font using CreateFont or modify the existing font setting by Getting the current font.

We can employ CreateFontIndirect function to create the font by specifying log font structure which contains the detailed font specification. Still I’ve not disclosed how to do that.

There are two different fields in the LOGFONT structure. They’re lfEscapement and lfOrientation

Here’s the description of these fields from MSDN

 

lfEscapement
Specifies the angle, in tenths of degrees, between the escapement vector and the x-axis of the device. The escapement vector is parallel to the base line of a row of text.Windows NT/2000/XP: When the graphics mode is set to GM_ADVANCED, you can specify the escapement angle of the string independently of the orientation angle of the string’s characters.

When the graphics mode is set to GM_COMPATIBLE, lfEscapement specifies both the escapement and orientation. You should set lfEscapement and lfOrientation to the same value.

Windows 95/98/Me: The lfEscapement member specifies both the escapement and orientation. You should set lfEscapement and lfOrientation to the same value.

lfOrientation
Specifies the angle, in tenths of degrees, between each character’s base line and the x-axis of the device.

The reason why we’re using LOGFONT is it’s so detailed. If you use CFont::CreatePointFont API, you will not be able to specify the Escapement and Orientation.I hope now you’re comfortable to go directly to the code. This is a modified version of the example presented in MSDN

[sourcecode language='cpp']
void DrawRotatedText(CString strDraw,
CRect rc, HDC hDC,
int nAngleLine = 0,
bool bEnableAngleChar = false,
int nAngleChar = 0,
LPCTSTR lpStrFontName = _T(“Arial”),
int nFontSize = 12 )

{
LOGFONT lf = { 0 };
_tcscpy_s(lf.lfFaceName, LF_FACESIZE, lpStrFontName);
lf.lfWeight = FW_NORMAL;

// Set the background mode to transparent for the
// text-output operation.
int nOldBkMode = SetBkMode(hDC, TRANSPARENT);
// Specify the angle to draw line
lf.lfEscapement = nAngleLine*10;

int nOldGMode;
if( bEnableAngleChar ) // Enable character angle
{
// Set graphics mode to advance to enable orientation
nOldGMode = SetGraphicsMode( hDC, GM_ADVANCED );
// Specify the angle of characters
lf.lfOrientation = nAngleChar*10;
}
else // Draw in normal mode
{
nOldGMode = SetGraphicsMode( hDC, GM_COMPATIBLE );
}

lf.lfHeight = -MulDiv(nFontSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);

// Select the new font created
HFONT hFont = reinterpret_cast( CreateFontIndirect(&lf));
HFONT hPrevFont = reinterpret_cast( SelectObject(hDC, hFont));

// Draw text to screen
TextOut(hDC, rc.right / 2, rc.bottom / 2, strDraw, strDraw.GetLength());
SelectObject(hDC, hPrevFont);
DeleteObject(hFont);

// Restore old values
SetBkMode( hDC, nOldBkMode );
SetGraphicsMode( hDC, nOldGMode );
}
[/sourcecode]

I hope the comments in the code is enough to explain the logic to apply the angle for entire row of text and for characters.

The function takes 8 parameters and 5 of them are default.

Parameter Description
CString strDraw Specify the string to draw
CRect rc Rectangle to draw the text
HDC hDC Device context for drawing
int nAngleLine Escapement Angle (angle for entire line), in degrees
bool bEnableAngleChar Enables angle for character. If this flag is enabled, nAngleChar value will be accepted. If this value if false, both the escapement and angle will be same.
int nAngleChar Orientation angle (angle for characters), in degrees
LPCTSTR lpStrFontName Name of the font. Default font will be Arial
int nFontSize Font size
Technorati Tags: ,,

Sharing my thoughts...

 

Windows Vista Synchronization: Slim Read Writer Locks

 

In the previous post we had discussed about condition variable and we demonstrated it using the commonly used Critical Section Synchronization objects. Now let’s check what Slim Reader Writer Lock is.

It’s again a user mode synchronization object which allows concurrent access to a shared resource for multiple threads and exclusive write access to the writer thread. Earlier if you had to implement suck kind of mechanism, we’ll have to write your own code or to get expert written code from some text books or to steal somewhere. This mechanism is broadly used where data is reads frequently with multiple thread and infrequent update of the shared resource.

It designed to be fast, small and for resource efficiency hence it wont support recursive acquisition of resources as it may create additional overhead.

Here are the functions used to implement Slim Reader and writer locks.

SRW API

Description

AcquireSRWLockExclusive 

Acquires an SRW lock in exclusive mode.

AcquireSRWLockShared 

Acquires an SRW lock in shared mode.

InitializeSRWLock 

Initialize an SRW lock.

ReleaseSRWLockExclusive 

Releases an SRW lock that was opened in exclusive mode.

ReleaseSRWLockShared 

Releases an SRW lock that was opened in shared mode.

SleepConditionVariableSRW 

Sleeps on the specified condition variable and releases the specified lock as an atomic operation.

 There are two modes to access the resource

  • Shared mode
    grants shared read-only access to multiple reader threads, which enables them to access data from the shared resource concurrently. If read operations exceed write operations, this concurrency increases performance and throughput compared to critical sections.
  • Exclusive mode grants read/write access to one writer thread at a time. When the lock has been acquired in exclusive mode, no other thread can access the shared resource until the writer releases the lock.

The locks should be initilized by using InitializeSRWLock function with SRWLOCK structure.

VOID WINAPI InitializeSRWLock(PSRWLOCK SRWLock);

AcquireSRWLockExclusive and ReleaseSRWLockExclusive apis can be used to acquire and release exclusive access to resource respectively. On the other hand, AcquireSRWLockShared and ReleaseSRWLockShared functions can be used to acquire and release shared access to the resource.

We’ve discussed about condition variables before. SRW locks can be used with condition variables apis to implement user mode wait operations for the threads running in a single process. SleepConditionVariableSRW api can be used to wait for a condition variables with SRW lock. SRW lock will be released when it’s being used with SleepConditionVariableSRW lock function.

[sourcecode language='cpp']
BOOL WINAPI SleepConditionVariableSRW(
PCONDITION_VARIABLE ConditionVariable,
PSRWLOCK SRWLock,
DWORD dwMilliseconds,
ULONG Flags
);
[/sourcecode]

The parameter is Flag to this API is important because there we can specify the mode of locking the resource as shared or exclusive access upon the successful execution of the API. If the flag parameter is CONDITION_VARIABLE_LOCKMODE_SHARED on returning the function the SRW lock will be granted with shared access and otherwise an exclusive access.

Let’s take a simple example which is available with Jeffry Richter’s “Windows Via C++” published in his website. I’ve made it short for the sake of expressing it here.

[sourcecode language='cpp']
BOOL ConsumeElement(int nThreadNum, int nRequestNum, HWND hWndLB)
{
// Get access to the queue to consume a new element
AcquireSRWLockShared(&g_srwLock);
// Fall asleep until there is something to read.
while (…) {
// There was not a readable element
SleepConditionVariableSRW(&g_cvReadyToConsume, &g_srwLock,
INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);
}

// Get the first new element
CQueue::ELEMENT e;
// Note: No need to test the return value since IsEmpty
// returned FALSE
g_q.GetNewElement(nThreadNum, e);
// No need to keep the lock any longer
ReleaseSRWLockShared(&g_srwLock);
// A free slot is now available for writer threads to produce
// –> wake up a writer thread
WakeConditionVariable(&g_cvReadyToProduce);
return(TRUE);
}

DWORD WINAPI ReaderThread(PVOID pvParam)
{
for (…)
{
if (!ConsumeElement(nThreadNum, nRequestNum, hWndLB))
return(0);
Sleep(2500); // Wait before reading another element
}
return(0);
}

DWORD WINAPI WriterThread(PVOID pvParam) {

int nThreadNum = PtrToUlong(pvParam);
for (…)
{
CQueue::ELEMENT e = { nThreadNum, nRequestNum };

// Require access for writing
AcquireSRWLockExclusive(&g_srwLock);

// If the queue is full, fall asleep as long as the condition variable
// is not signaled
if (…) {
// No more room in the queue Need to wait for a reader to empty a slot before acquiring the lock again
SleepConditionVariableSRW(&g_cvReadyToProduce, &g_srwLock,
INFINITE, 0);
}

// Add the new ELEMENT into the queue
g_q.AddElement(e);
// No need to keep the lock any longer
ReleaseSRWLockExclusive(&g_srwLock);
// Signal reader threads that there is an element to consume
WakeAllConditionVariable(&g_cvReadyToConsume);
// Wait before adding a new element
Sleep(1500);
}

return(0);
}
[/sourcecode]

I removed lot of lines from the original code written by Jeff. You can download the entire source code from his website. One comment about his code is, it’s remarkably professional.

And finally a SRW lock doesn’t have APIs to destroy the SRW lock handle.

You might be interested to see the some performance figure presented in MSDN magazine on SRW locks.

Sharing my thoughts...

 

Windows Vista Synchronization: Condition Variables

 

There are few new synchronization primitives has been introduced with Windows Vista like Condition Variables, Slim Reader/Writer Lock, One Time Initialization, Double Checked locking etc…
Let’s take a look to Condition variables

Condition variable is a user mode synchronization primitive to wait the thread(s) until a particular condition occurs. The conditional variable can be used with either Critical Section Objects and SRW Objects. We can either wake one or all threads waiting for condition variables according to our requirements. This type of synchronization mechanism can’t be shared among the multiple process as it’s user mode synchronization object. The following are the APIs used with Condition Variables.

InitializeConditionVariable Initializes a condition variable.
SleepConditionVariableCS Sleeps on the specified condition variable and releases the specified critical section as an atomic operation.
SleepConditionVariableSRW Sleeps on the specified condition variable and releases the specified SRW lock as an atomic operation.
WakeAllConditionVariable Wakes all threads waiting on the specified condition variable.
WakeConditionVariable Wakes a single thread waiting on the specified condition variable.

OK Let’s take a simple sample provided from MSDN. We will be having a producer who should always write some data to a shared queue and we’re having a client reading data from the same queue.
[sourcecode language='cpp']
// Producer
for (unsigned int i = 0;i < g_uiIterations;i++)
{
EnterCriticalSection(&g_csLock); // Produce work
g_listWork.push_back(i);
LeaveCriticalSection(&g_csLock);
WakeConditionVariable(&g_condNotEmpty);
Sleep(g_uiProducerDelay);
}

//Consumer
while (true)
{
EnterCriticalSection(&g_csLock);
while (g_listWork.empty())
{
SleepConditionVariableCS(&g_condNotEmpty,&g_csLock,INFINITE);
}
i = g_listWork.front();
g_listWork.pop_front();
LeaveCriticalSection(&g_csLock);
Sleep(g_uiConsumerDelay); // Simulating work
}
[/sourcecode]
The above example is quite straight forward. It shows how we can sleep and wait using condition variables. The producer acquire the critical section for writing data in to queue. At this time the consumer thread will be waiting for the condition variables. When the producer calls "WakeConditionVariable", the consumer thread will be release and sooner it will acquire the lock for critical section in the SleepConditionVariableCS call itself. After completing the usage of resource as usual we've to call LeaveCritical Section.
The synchronization object used here is Critical Section. Of course as part of initialization, The critical section object should be initialized using InitializeCriticalSection API and the Condition variable (CONDITION_VARIABLE) with InitializeConditionVariable API. If you notice, there are no API to clean up or destroy the CONDITION_VARIABLE structure as it’s not needed by the underlying implementation.
The above examples can be implemented in conventional style using event and critical section. The drawback of the method is, the event which we’re waiting will be a kernel object and it makes a costly kernel mode roundtrip for execution. While the condition variables are user mode synchronization objects.
You can see a sophisticated example in MSDN.

Technorati Tags: ,

Sharing my thoughts...