Re: Scaling of data into dc



See below...
On Sun, 13 May 2007 22:57:44 +0200, Matthias Pospiech <matthiaspospiech@xxxxxxxx> wrote:

Joseph M. Newcomer schrieb:
See below...
On Sun, 13 May 2007 12:18:03 +0200, Matthias Pospiech <matthiaspospiech@xxxxxxxx> wrote:

Joseph M. Newcomer schrieb:
This is a completely wrong approach. Use SetViewportExt/SetWindowExt to accomplish this.
Otherwise, you end up scaling the bitmap which gets really lousy representation for the
graphics.

I tried to implement the scaling with SetViewportExt/SetWindowExt.

That however worked only partly

- What is the difference between the two ?
****
One sets the specificaiton of the size of the viwport and one sets the specification of
the size the window. The ratio of the two provides the mapping between physical and
logical coordinates. RTM.
****
Well, I understand the sizing of the viewport, but sizing of the window
does not resize the window, so what does is resize it then ?
*****
I didn't say it set the size; I said it set the specificaiton of the size. It tells the
DC how big the window is.
*****
Maybe the concept is just confusing to me - I know the one from VB6
which has a scale property of a window which I thought is what the
viewport does. But that is not the most important part of my problem...

- I changed in CMemDC.h the lines

SetMapMode(pDC->GetMapMode());
SetWindowExt(pDC->GetWindowExt());
SetViewportExt(pDC->GetViewportExt());

to

SetMapMode(MM_ANISOTROPIC);
SetWindowExt(CSize(m_rect_dest.Width(), m_rect_dest.Height()));
SetViewportExt(CSize(m_rect_source.Width(), m_rect_source.Height()));
*****
You have not specified what pDC is. Note that you would apply the mapping mode to EITHER
the MemDC or the actual DC, but not both, because then the two mapping modes compound each
other.
Clearly.
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.

To create a bitmap, you would create a bitmap the same size as the client area of the
window. You would create a memory DC and select the bitmap into it. 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.

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.
*****

If you feel the compulsion to use the intermediate memory DC, then you would apply
it there, although it appeared that the only reason you were doing this is so you could do
a StretchBlt,
No that is not the reason. I use CMemDC because it does the double
buffering I need. The StretchBlt was implemented by myself as my first
approach, but as I had written I changed it back to its original state
with BitBlt, so below.
*****
So for double buffering, you set the Viewport extent to be the dimensions of the bitmap.
****


and changed from StrechBlt to BitBlt again, because I do not want to mix
two differend streching ways.
****
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
****

The generated bitmap is now scaled, BUT

- only the size of the data (if smaller the the dc size) is
invalidated/shown, although the UpdatePlot invalidates the whole area:
*****
This is probably caused by the apparent rescaling of the original DC. Scale only one.
I only scale the dc applied to MemDC by
SetViewportExt(CSize(m_rect_source.Width(), m_rect_source.Height()));
*****
Since there is no specification of these values, it is hard to guess what they mean.
Normally the viewport extent is the size of the window, but in the case of double
buffering it is the size of the target window (and the bitmap is the same size as the
target window client area)
*****

There appears no other scaling.

*****
Scale only the one you are drawing into. The MemDC needs to have the same dimensions as
the target window.
I thought is would be doing that already, if not I do not see where.

Here more code to make clear what I do:


------------ 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!
****
rc_dest=GetSize();
*****
THis seems a remarkably clumsy way to write
GetClientRect(&rc_dest);
*****

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.
*****
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?
*****

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!!!!!
*****

PlotToDC(& pDC);
*****
You have a perfectly good DC. Why are you taking the & of it?
*****
}
------------ Painting the data
void CGraphCtrl::PlotToDC(CMemDC* pDC)
*****
WhHy not
void CGraphCtrl::PlotToDC(CMemDC & DC)

which avoids silly things like having to use & to pass it in, having to use -> to access
its methods, etc.
****
{
COLORREF c;
*****
Since 'c' is not used until the inner part of the loop, why declare it out here?
*****
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?
*****
{
for (int ix=0; ix < m_PixelNumberX; ix++)
{
for (int iy=0; iy < m_PixelNumberY; iy++)
{
****
COLORREF c = PlotData[ix][iy]
Avoids the need to have declared a variable at a scope larger than required
*****
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?

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
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.
*****
--------------
CRect CGraphCtrl::GetPlotDataSize() // Size of dataarray to be plotted
{
CRect rc(0,0,m_PixelNumberX, m_PixelNumberY);
return rc;
}
*****
This seems equally unnecessary.
*****
-------------- Update
void CGraphCtrl::UpdatePlot()
{
Invalidate();
UpdateWindow();
}
============== MemDC class
class CMemDC : public CDC {
private:
*****
private is usually a bad idea. Use protected in most cases
*****
CBitmap m_bitmap; // Offscreen bitmap
CBitmap* m_oldBitmap; // bitmap originally found in CMemDC
CDC* m_pDC; // Saves CDC passed in constructor
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()
*****
CMemDC(CDC * pDC, LPCRECT pSourceRect = NULL, LPCRECT pDestRect = NULL) : CDC()
****
{
ASSERT(pDC != NULL);
*****
If pDC cannot be NULL, why is it declared as a CDC*? Why not a CDC&, then the compiler
will catch most silly errors
*****

// Some initialization
m_pDC = pDC;
m_oldBitmap = NULL;
m_bMemDC = !pDC->IsPrinting();
*****
What is m_bMemDC, and why is a MemDC not a MemDC if it is printing?
*****

// 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);
****
This assumes the pDC has a mapping mode other than MM_TEXT. However, if it does, and you
are cascading scalings, things become very confused. None of the code you showed handles
compound transformations.
*****

m_bitmap.CreateCompatibleBitmap(pDC, m_rect_dest.Width(),
m_rect_dest.Height());
*****
Your target bitmap is only as big as the invalidated area (which might be empty?)
*****
m_oldBitmap = SelectObject(&m_bitmap);

//SetMapMode(pDC->GetMapMode());
SetMapMode(MM_ANISOTROPIC);

//SetWindowExt(pDC->GetWindowExt());
//SetViewportExt(pDC->GetViewportExt());
SetWindowExt(CSize(m_rect_dest.Width(), m_rect_dest.Height()));
SetViewportExt(CSize(m_rect_source.Width(), m_rect_source.Height()));
*****
This is going to produce different scalings depending on how big an area you invalidated,
so this code is just wrong. It will scale different ways for different invalidations.

Create a bitmap as large as your window, no matter how big an area is invalidated. Assume
the source DC is in MM_TEXT mode (if it isn't, complain and ASSERT). SetViewportExt as
big as your target window. SetWindowExt to be 0..max logical coordinate for x and y. The
code here is quite incorrect as it creates bitmaps smaller than the total area and then
doesn't compensate for the change in origin. You need to have a better grasp of
coordinate transform systems before you start doing this sort of thing.
*****



pDC->DPtoLP(&m_rect_dest);
****
I'm already confused. Why are you doing this conversion?
****
SetWindowOrg(m_rect_dest.left, m_rect_dest.top);
*****
OK, I now see you are changing the origin, but I'm not sure I believe the logic here. You
should be working in your bitmap in logical coordinates, and the mapping mode you set
(MM_ISOTROPIC/MM_ANISOTROPIC) combined with the extent settings should handle all scaling.
Then you simply BitBlt the subpiece you've drawn across; since the bitmap is in physical
coordinates everything should transfer smoothly.
****
} 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;
****
Why this last line?
****
}

// 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;
}
*****
Why is a destructor doing a BitBlt? This seems an odd program organization...
***
}

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

// Allow usage as a pointer
operator CMemDC*()
{
return this;
}
};
*****
I find this code bewildering. If it is this hard to understand, it needs either better
comments or it needs to be simplified.
joe
*****
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.



Relevant Pages

  • Re: Copying Bitmap
    ... Remember I said that my code does not work when the window is obscured by another window. ... CDC dc_bitmap; ... // Copy output bitmap onto display target ... Add your control notification handler code here ...
    (microsoft.public.vc.mfc)
  • Re: how to get a bitmap from the screen and then inside lisp?
    ... Getting pixels off the screen quickly is a bit complicated and you ... have to do some decoding of Windows bitmap data structures. ... int x, int y, int w, int h, ... // get the window handle ...
    (comp.lang.lisp)
  • Re: How to use DrawDibDraw
    ... You may choose the entire bitmap or part of the bitmap to render(e.g., ... you may choose to render to the entire window or some part of the ... seem to be missing from "bitmapinfoheader" that I see in the hex edittor. ...
    (microsoft.public.dotnet.languages.vc)
  • Re: Painting error when window resized quickly
    ... each WM_PAINT message while the window is being resized. ... as the window is enlarged and copy it into the bitmap in the memory ... device context. ... //* Create a memory device context compatible with the screen ...
    (microsoft.public.win32.programmer.gdi)
  • Re: Pattern brushes and PostScript (scaling problem)
    ... Scaling factor is /. ... I use StretchBlt to scale the bitmap. ... int horizPrintRes =::GetDeviceCaps(gPrinterDC, LOGPIXELSX); ...
    (microsoft.public.win32.programmer.gdi)

Loading