Navigation

A memory device context (CMemDC)

<!-- The 'p' starts a paragraph of normal text -->I recently downloaded the CMemDC class published by Keith Rule (see his article here at
codeguru's).
It faces the following problem: If you perform rather huge operations on a device context
(in an OnPaint(), for example), your display sometimes flickers around rather badly.
To circumvent this problem, Keith has designed CMemDC which creates a compatible
"memory" device context in which you draw alternatively.
After you finished drawing to this device context (in memory) you clip it to the original
one - only one blit takes places in the visible context. Therefore the output
doesn't flicker anymore (nearly, at least).
However, his implementation assumes that you always want to draw the entire clipping area
- even if it has already been filled up by WM_ERASEBKGND or sutff.

Why rotating text isn't that easy

Moreover, I recently designed a new button that displays it text rotated.
Now, you can create a CFont that outputs text rotated (use a LOGFONT and set
lfOrientation and lfEscapement) but this works AFAIK for TrueType fonts only - it doesn't
work for MS Sans Serif at last.
At least, a CDC's text functions seem to get up confused if you set up such a font - the
produce "random" results ... try it if you don't believe me

Therefore I had a look at Keith' CMemDC and then at the CDC::PlgBlt() function. And I
decided to extent the CMemDC to a "rotatable" memory context, i.e. a memory
context that does not only clip one memory bitmap to an output device context only but one
that even rotates it if you like it to.

cdxCRot90DC - a rotatable memory DC

Well, cdxCRot90DC takes place to solve these problems. Next to specifing a
"destination" device context (your output device, normally), it allows you to
even provide a rectangle out of it (CMemDC always used the current output context's clip
box) and an "even angle" of 0°, 90°, 180°, 270°, ... (that's why it's called
cdxCRot90DC and not cdxCRotDC .
Note: I use the term "even angle" when I mean an angle dividable by 90 without
any rest.
Moreover, note that if I talk about an angle x the same applies to x+n*360 and x-n*360, n
out of N.

You may acknowledge that the rotation is not limited to text - you can
freely draw into the memory device context as you like and the result will be put into
your original one as having been drawn rotated - unfortunately, you cannot draw into the
cdxCRot90DC using your original rectangle - you need to use the one transformed
automatically for you (get it using GetRotRect()).
If you choose an "even angle" of 0° the cdxCRot90DC class basically acts like
CMemDC with some advantages:

You can freely define the rectangle of your destination DC you want to work with [see constructor/Create()]

You can initially copy the previous device context's contence into your memory bitmap
(that preserverves any work done by WM_ERASEBKGND or stuff that is been drawn by an
ownerdrawn control if you use its DrawItem() function) [see constructor/Create()]

You can discard your changes.CMemDC always copies back its bitmap to your original device context - that may not be
suitable (you found out a text is empty and nothing is in the bitmap, for example)
[function Invalidate()]

Other features for rotated output:

Transparent use of the angle.
You code doesn't need to be changed for different angles (if you use GetRotRect()).

An object can be reused with another destination context rectangle (no new DC is created
and the old bitmap will be reused if possible).
[see Create()]

Please note:

This device context doesn't support printing (in contrast to CMemDC).
If anybody could help me implementing it, I'd be very happy.
I for myself doesn't have a printer that seems to support rotated output ...

Constructs a new object.
The lower four creators immediately call Create() that creates a
new device context for you.
If you use them, use IsCreated() to check whether the device
context has been successfully been set up (otherwise, if Create()
failed, cdxCRot90DC::m_hDC (incorporated from CDC) won't be properly set up and you'll get
thousands of ASSERTs...).
See Create() for further information.

Note that the destructor will automatically call Finish()
if the device context has been set up properly.

Creates the device context.
You can start drawing into it if this function successfully returned.
Call Finish() to copy back the bitmap of the rotated DC
into your destination DC (the destructor will do so automatically - see Finish() for further information).

Note #1:
Since your rectangle (see parameters below) might be rotated, you cannot use the
coordinates of rectDC to draw your data (they may lay outside the rotated
rectangle).
Therefore, use the GetRotRect() function to receive the device
context's rectangle that matches your rectDC.Note #2:
You can call Create() several times - each time you call it it will create a
"new" cdxCRot90DC for you (it won't allocate a new bitmap if the previous one
meets the new denies).
However, if you want to copy your previous bitmap back to its destination device
context, you have to call Finish() first.

Parameters:

destDC, pdestDC
The "original" device context; pDestDC may not be NULL.

rectDC
The rectangle out of the original device context you want to draw to using this memory
context.If not provided, the current clip box will be used.
Note that in either case rectDC will be intersected with the current
original device's clip box.
If the resulting rectangle is empty, this function will fail.

iAngleThe rotation angle for your output.
I.e. if you provide a "90", an arrow you draw from the left to the right will be
displayed pointing from bottom to top (see example image above).
If you use 0, the cdxCRot90DC will act as CMemDC (no rotation will take place).

bCopy
Set to true if you want to copy the contence of rectDC of your destDC
into your newly created device context.
This would be useful if there're already any (non-rotated) graphics that you don't want to
overdraw if the memory device context clips back its bitmap.
Please note that CDC::PlgBlt isn't one of the fastest functions thus you may first created
the rotated data, clip them back and create the other ones afterwards.

This function returns false if
- destDC is used for printing (I don't have a printer that is able to
print rotated images...)
- rectDC intersected with your destDC's clip box is
empty or - if you don't provide rectDC - your destDC's
clip box is empty.

Can be used to receive the rectangle of the rotated memory device context that
matches the rectangleof the destination ("output") device context.
If you refer to the example image, this function returns
(-120,10,-20,250) for the destination rectangle (10,20,250,120) (rotate() can be used to transform other 2D objects).

This function returns (0,0,0,0) if the device context had not been successfully set up.

Invalidates the cdxCRot90DC device context - Finish() won't
draw the bitmap back to the destination DC (and therefore the destructor won't do so,
too).
Moreover, this function will set the current clipping region to an empty rectangle - all
further drawing operations won't affect the memory device context.
You can use this function if you found out that your rotated image would be empty or stuff
to avoid the device context to blit it back to the destination device context
(this will be faster .

Any transformation from a destination 2D object into a rotated 2D
object that has been rotated using the even angle iAngle. If you want to
transform an object from a rotated to a destination DC, use rotate(...,-iAngle).
You can use these functions to perform calculations without creating a device context for
this task.

Drawing 3D text in the rotated device context may need you to know in
which direction in the rotated device context the lower right corner of the destination
lays.
For example, if you want to draw some disabled text (as seen in a disabled CStatic), you
normally would use:

But what happens if you do so in a rotated DC ?
Previously, your "shaddow" text would be up-left. But if the device context
rotates your output by 180°, it would be down-right ... ugly !
The solutuion is to use rotate() to transform the vector (1,1)
into the corresponding vector in the rotated device context:

Note that there's a member function DrawControl() (which is not described here) that
may kill most of your problems

CDC::DrawEdge() and CDC::Draw3dRect() may have the same problem !
I recommend to use some code as initially shown as example 2 to
set up objects that need these functions (although one could easily write a short-cut for
them

Post the following new classes to codeguru:
- cdxCRotButton: A CButton with rotated text & icon.
- cdxCRotStatic: A CStatic with rotated text & icon.
- cdxCRotBevelLine: A bevelline with rotated text.
These classes are already finished but I don't have the time to write a proper text for
codeguru ... I hope to find some time soon.

Llew Goodstadt of the University Laboratory of Physiology, Oxford, UK
has provided an updated project that addresses some of the problems of the original code in that it
moves away from using PlgBlt and instead uses DIBs to perform the rotation. Llew did not
have time to write up his solution, nor complete the code to his satisfaction, and so the demo is provided
here "as is" in case other wish to extend this further.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

I have done this through the WM_PRINT functionality that the EDIT class exposes.

It is not possible to do this "normally" because:

1) Microsoft's implementation of SetWordTransform() is lacking, since it does not transform the Clipping Regions that are in effect in a DC!!!

2) Because of the above, SetWorldTransform() trashes the ExtTextOut() function when using using the lprc parameter and fuOptions==ETO_CLIPPED. Unfortunately Edit Boxes and Memos (multiline Edit Boxes) use ExtTextOut() in that manner profiously.

3) MS-Windows EDIT Box class sets up many individual clipping regions, which become out of synch after the transform is applied. There is no way to fix the clipping regions short of hooking the SelectClipRgn() functions, etc... (This is an example how Microsft forces decent programmers to use hacking techniques to make the World Transform work like it's supposed to)

4) Even if ExtTextOut() and ETO_CLIPPED worked with World Transforms, in order for the World Transform to "stick" permanently to the Edit Box's Device Context, the EDIT class needs to be superclassed with the CS_OWNDC or CS_CLASSDC class style.

5) Unfortunately Microsoft f*#%ed up again, by making it impossible to permanently apply a World Transform to Window Device Contexts. The functions GetWindowDC() or GetDCEx(?, ?, DCX_WINDOW), always obtain Window DCs form the system cache (new DC each time - without World Transform applied of course). CS_OWNDC or CS_CLASSDC class styles do not apply to Window Device Contexts (these styles apply only to Client Device Contexts.)

6) Because of the above, non-client areas of the Window cannot be rotated. Which means that the scroll bars in a Multiline EDIT cannot be rotated either (they are a part of non-client area)

7) Even if the World Transform could be applied permanently to a Client and Window DC, and if all the clipping regions would obey the World Transform - it still would be impossible to rotate an Edit Box using World Transform, because Microsoft uses an archaic function: ScrollWindow(). that doesn't even use a Client Device Context. (despite that a ScrollDC() function has been in existence for a long time). ScrollWindow() ignores all Client DC attributes, including the World Transform. This means that the rotated Edit Box would not scroll horizontally.

8) Rotating the Edit Box image on the screen does not rotate the mouse and keyboard input. These issues need to be tackled separately through subclassing.

9) A rotated EditBox, obviously should have a rotated cursor too (eg. the text select cursor). In order to do this, the cursor ANDbitmap and XORbitmap need to be rotated separately (for animated cursors that means rotating each cursor frame individually). This is hard to do, considering, that as of Windows XP and 2003 Server SP1, original Microsoft functions such as: CopyImage or CopyCursor are incapable of copying all the frames of an animated cursor. Shame on you Microsoft! This is possible by using undocumented functions only, such as GetCursorFrameInfo ().

Summary: Microsoft makes it impossible to create a Vertical Edit box by rotating a regular Edit box with World Transform, due to shortsighted software engineering.
However it is possible via other means, such as WM_PRINT or just writing the whole code of a Vertical Edit box from scratch. I expect that only good System Programmers or talented hackers will be able to accomplish those feats. Most likely using undocumented functions and techniques.

Pardon my interruption, however,
I am currently using, PrintNow screen capture software and occasionally get this error message "Unable to Create Device Context..." when I engage the application using the printscreen button. I click OK to get out of the message, then unload the printnow application then reload it to reinitialize the application and reengage the network printer and it will begin printing the captured screen again. Would anyone have source code, or any idea what I can use with the PrintNow source code that would alleviate the error message?
Thank you for your time.