Re: Scaling of data into dc

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



It should scale up and down, I'll have to investigate this. I know I've used it
successfully,but my code was not nearly as convoluted as this code is.

For other comments, see below...
On Mon, 14 May 2007 14:38:35 +0200, Matthias Pospiech <matthias79@xxxxxx> wrote:

Short summery: Skaling up works as expected, scaling down shows only the
fraction windowsize/Datasize which I do not understand at all.


Joseph M. Newcomer schrieb:
I do apply these commands to the dc of MemDC (so it is inside the MemDC
class), and not to the dc of the window like it is done in your example,
see also code at the end.
*****
So what does
SetMapMode(pDX->GetMapMode())
really do? Since SetMapMode is a method of a DC, it would have to be written as
dc.SetMapMode(pDX->GetMapMode());
which would set the map mode of the 'dc' to the map mode of some other DC. But here you
have not said which DC is which, so your code is hard to understand, and it is not clear
why you would set the mapping mode of the memory DC to be the mapping mode of the window
DC, or set the extent and size of the memory DC to be the same as the extent and size of
the window DC. Since the meanings of m_rect_dest and m_rect_source are not specified, the
code is moderately incomprehenisible at best.
First I have to say that the whole class CMemDC is not my product, so
please do not blame me for its content. It is written by Keith Rule and
the example for doing flickerfree drawing (I was pointed to this class
several times).

pDC inside CMemDC is the memoryDC, wheras m_pDC is the dc of the window.

The meaning of m_rect_source and m_rect_dest is the Rect of the data to
draw and the Rect of the window to draw into.

To create a bitmap, you would create a bitmap the same size as the client area of the
window.

m_bitmap.CreateCompatibleBitmap(pDC,
m_rect_dest.Width(),
m_rect_dest.Height());

This is a bitmap of the size of the window (m_rect_dest is equal to rect
of GetClientRect)

You would create a memory DC and select the bitmap into it.
You mean:
CreateCompatibleDC(pDC);

You would then set
the viewport extent to be the dimensions of the bitmap and the window extent to be the
logical coordinate system. For example, if you drew a line from coordinates 0,0 to
1000,1000, you would SetWindowExt(1000, 1000); and if your window was 300x300 you would
SetViewportExt(300,300). Scaling will then be automatic.

So that would be
pDC->SetMapMode(MM_ANISOTROPIC);
// Data source
pDC->SetWindowExt(m_rect_source.Width(), m_rect_source.Height());
// Window Size
pDC->SetViewportExt(m_rect_dest.Width(), m_rect_dest.Height());

****
I believe that is correct. I usually have to think carefully about scalling to get the
parameters correct.
*****

There's a bit more to all of this if the window and viewport are small, so to avoid ugly
roundoff artifacts you sometimes want to set the window extent to be a multiple of the
scale and scale to larger logical values.
I understand.

*****
So for double buffering, you set the Viewport extent to be the dimensions of the bitmap.
****
Bitmap has the same dimensions:
m_bitmap.CreateCompatibleBitmap(pDC, m_rect_dest.Width(),
m_rect_dest.Height());

****
Yes, that's right. It is not clear you even need the extra MemDC and the BitBlt, unless
you have other reasons for doing this
*****
BitBlt is necessary for the double buffering.
****
Then scale your MemDC
****
Is what I am doing not exactly this ?
****
But if your window is scaled, you get additional complications. So if you want to be
sure, you will need to set the mapping mode of the target DC to MM_TEXT before doing the
BitBlt. Otherwise, you may be seeing some of the problems you are reporting. BitBlt
works in logical coordinates, and if there is a mapping mode on the window DC, BitBlt will
copy things in terms of logical coordinates, not physical coordinates. You should be able
to say

ASSERT(m_pDC.GetMapMode() == MM_TEXT);

and it should not assert.
*****


So with the following code using your instructions it now does scale up,
but if the data is greater than the windowsize it does not show the
whole content:

It does scale correct, but shown is only a part of the whole image. If
the data has twice the value, on the screen is only a fourth and if the
data is three times (so lets say 300x300) the screen shows of the window
with size (100x100) only 33x33 so the division of the two.
And that was my problem I already had with the StrechBlt Code.

--------------------------------------
class CMemDC : public CDC {
private:
CBitmap m_bitmap; // Offscreen bitmap
CBitmap* m_oldBitmap; // bitmap originally found in CMemDC
CDC* m_pDC; // Saves CDC passed in constructor
****
Choosing a better name for this, like m_pTargetDC, would make the code a lot clearer.
****
CRect m_rect_dest; // Rectangle of drawing area.
CRect m_rect_source; // Rectangle of source area.
BOOL m_bMemDC; // TRUE if CDC really is a Memory DC.
public:

CMemDC(CDC* pDC, const CRect* pSourceRect = NULL, const CRect*
pDestRect = NULL) : CDC()
{
ASSERT(pDC != NULL);

// Some initialization
m_pDC = pDC;
m_oldBitmap = NULL;
m_bMemDC = !pDC->IsPrinting();

// Get the rectangle to draw
if (pDestRect == NULL) {
pDC->GetClipBox(&m_rect_dest);
} else {
m_rect_dest = *pDestRect;
}
// Get the rectangle to draw
if (pSourceRect == NULL) {
pDC->GetClipBox(&m_rect_source);
} else {
m_rect_source = *pSourceRect;
}

if (m_bMemDC) {
// Create a Memory DC
CreateCompatibleDC(pDC);
pDC->LPtoDP(&m_rect_dest);

m_bitmap.CreateCompatibleBitmap(pDC, m_rect_dest.Width(),
m_rect_dest.Height());
m_oldBitmap = SelectObject(&m_bitmap);

//SetMapMode(pDC->GetMapMode());
pDC->SetMapMode(MM_ANISOTROPIC);
//SetWindowExt(pDC->GetWindowExt());
pDC->SetWindowExt(m_rect_source.Width(), m_rect_source.Height());
//SetViewportExt(pDC->GetViewportExt());
pDC->SetViewportExt(m_rect_dest.Width(), m_rect_dest.Height());


pDC->DPtoLP(&m_rect_dest);
SetWindowOrg(m_rect_dest.left, m_rect_dest.top);
} else {
// Make a copy of the relevent parts of the current DC for printing
m_bPrinting = pDC->m_bPrinting;
m_hDC = pDC->m_hDC;
m_hAttribDC = pDC->m_hAttribDC;
}

// Fill background
FillSolidRect(m_rect_dest, pDC->GetBkColor());
}

~CMemDC()
{
if (m_bMemDC) {

//m_pDC->StretchBlt(
// m_rect_dest.left, // x-coord of destination upper-left corner
// m_rect_dest.top, // y-coord of destination upper-left corner
// m_rect_dest.Width(), // width of destination rectangle
// m_rect_dest.Height(), // height of destination rectangle
// this, // handle to source DC
// 0, // x-coord of source upper-left corner
// 0, // y-coord of source upper-left corner
// m_rect_source.Width(), // width of source rectangle
// m_rect_source.Height(), // height of source rectangle
// SRCCOPY);

m_pDC->BitBlt(
m_rect_dest.left, // x-coord of destination upper-left corner
m_rect_dest.top, // y-coord of destination upper-left corner
m_rect_dest.Width(), // width of destination rectangle
m_rect_dest.Height(), // height of destination rectangle
this,
m_rect_dest.left, // x-coord of destination upper-left corner
m_rect_dest.top, // y-coord of destination upper-left corner
SRCCOPY);


//Swap back the original bitmap.
SelectObject(m_oldBitmap);
} else {
// All we need to do is replace the DC with an illegal value,
// this keeps us from accidently deleting the handles associated with
// the CDC that was passed to the constructor.
m_hDC = m_hAttribDC = NULL;
}
}

// Allow usage as a pointer
CMemDC* operator->()
{
return this;
}

// Allow usage as a pointer
operator CMemDC*()
{
return this;
}
};




------------ Painting in the overloaded CStatic Class
void CGraphCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting

CRect (rc_dest);
****
What's with the unnecessary parentheses? I'm surprised this even compiles!
Was copied from somewhere else, but I understand why you are surprised.
****
rc_dest=GetSize();
*****
THis seems a remarkably clumsy way to write
GetClientRect(&rc_dest);
The reason is that I can use the function as a parameter elsewhere.
It is just a wrapper that is true.
*****
CRect (rc_source);
*****
This is not a declaration; it is a constructor that creates a rectangle, initializes it to
the values in rc_source, then discards it completely. Learning C++ syntax will be a major
step in the right direction.
*****
I understand your concerns and I do know enough C++ to understand why
this should be written differend.
****
Mostly because it appears that this way is wrong. I see a CRect constructor creating a
temporary CRect, iniitialiing it to rc_source, and then discarding it entirely.
CRect rc_source;
would declare a variable.
*****

rc_source=GetPlotDataSize();
*****
A clumsy way to write the code of the function that is called; and what are the meanings
of those numbers in that function?
*****
They return the size of the data (in difference to the window size).
They are set with
----------
void CGraphCtrl::SetPlotDataSize(const int x, const int y)
{
PixelNumber.x = x;
PixelNumber.y = y;
if ((x>=0) && (y>=0))
{
PlotData.resize(PixelNumber.x);
for(int x = 0; x < PixelNumber.x; x++)
PlotData[x].resize(PixelNumber.y);
}
}
*****
This code seems a bit odd. What is the representation here? What is the resize method
doing? There are massive pieces of missing information.

Given the point seems to be that this creates a new bitrmap, why doesn't it just create a
new bitmap?
*****
----------


CMemDC pDC(&dc,&rc_source, &rc_dest); // Double Buffering
****
Since this is not a pointer to a DC, why are you using the HN that indicates it is a
pointer? If you do not understand Hungarian Notation, DON'T USE IT AT ALL!!!!!
*****
Fixed. I probably have just copied the pDC, it is no pointer so you are
right.

PlotToDC(& pDC);
*****
You have a perfectly good DC. Why are you taking the & of it?
*****
good question, have changed it to &.
****
{
COLORREF c;
*****
Since 'c' is not used until the inner part of the loop, why declare it out here?
I am not a C++ guru. I thought it would be standard to declare at the
top of a function.
*****
The methodology of declaring things at the top of a function died with Pascal. C never
required this; it only requires declarations be at the start of a block. Therefore, what
I'm suggesting is both valid C and valid C++. C++ additionally allows declarations
anywhere, without the top-of-block restriction.

It is good policy to declare as late as possible, with the smallest possible scope.
*****
*****

if ((m_PixelNumberX>0) && (m_PixelNumberY > 0))
****
This test seems unnecesary since the for loops already do nothing if the values are 0. So
why add a pointless test to the computation?
*****
Have not thought of that.
*****
c=PlotData[ix][iy];
pDC->SetPixel(ix,iy,c);
*****
This is going to be REALLY SLOW! Why not just copy the data directly into the bitmap?
I do believe you, but I did not get anywhere the answer how to do it
different...


Performance doesn't matter until it matters, then it matters a lot. In this case, doing
SetPixel calls is known to be unbelievably slow, and should be avoided unless you are
dealing with really tiny bitmaps.
*****
Sizes range from 128x128 to 1024x1024.
*****
1024x1024 is a million pixels. SetPixel will take many, many seconds to copy a bitmap
this size. The delay will be noticeable and aggravating.
*****
-------------- Sizes
CRect CGraphCtrl::GetSize()
{
CRect rc;
GetClientRect(rc);
return rc;
}
*****
This whole functions eems pointless (it is also syntactically incorrect, since the correct
call is
GetClientRect(&rc);
and what you wrote should not even compile.
*****
It however does compile, so I was not pointed at this error.
--------------
CRect CGraphCtrl::GetPlotDataSize() // Size of dataarray to be plotted
{
CRect rc(0,0,m_PixelNumberX, m_PixelNumberY);
return rc;
}
*****
This seems equally unnecessary.
How else should I create the rect of my datasize ?
============== MemDC class
class CMemDC : public CDC {
private:
*****
private is usually a bad idea. Use protected in most cases
not my code, so please do not blame me...
[...]
*****
Why is a destructor doing a BitBlt? This seems an odd program organization...
***
This is the concept of the whole class. As long as the class instance is
valid one only prints inside the memDC. If the class instance gets
deconstructed it plots to the screen.
*****
And that's what I find as odd. What if you want to abort without drawing?
*****

[...]

*****
I find this code bewildering. If it is this hard to understand, it needs either better
comments or it needs to be simplified.

Since this class is the first reference to flickerfree drawing with MFC
and you have so many complains I believe it would be great if a newer
version of this class with less problems and better commenting would be
published.
*****
Probably true. But I can't control that.
*****

Matthias
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.



Relevant Pages

  • Getting the functionality of -command with tk_optionMenu (longish)
    ... I am writing a Tcl/Tk program to display a small database. ... I have a tk_optionMenu to choose the table, a scale to pick the row to ... and a text window for the display. ... label .label ...
    (comp.lang.tcl)
  • Re: Print Preview colors
    ... Printing goes fine at every scale. ... // make compatible dc for bitmap selection ... interBM.CreateCompatibleBitmap;// final sizes ... // put the memory image into the main window DC ...
    (microsoft.public.vc.mfc)
  • Re: Scaling of data into dc
    ... class), and not to the dc of the window like it is done in your example, see also code at the end. ... pDC inside CMemDC is the memoryDC, wheras m_pDC is the dc of the window. ... scale and scale to larger logical values. ... CMemDC(CDC* pDC, const CRect* pSourceRect = NULL, const CRect* pDestRect = NULL): ...
    (microsoft.public.vc.mfc)
  • Re: Getting the functionality of -command with tk_optionMenu (longish)
    ... > display, and a text window for the display. ... > made on the optionMenu I want the scale and text to update, ... the bind commands do not get called at all. ...
    (comp.lang.tcl)
  • Re: The Heartbreak of Resolution Independence
    ... the one I cared about the most was Resolution Independent UI; ... Win32 will scale drawing, ... It would not scale window size and position data. ...
    (comp.sys.mac.advocacy)