Introduction
This OCX gives you the possibility to zoom any picture you like on any
window of (at least) your application. There are three major parts,
namely loading the desired picture, re-direction of the Windows Event Handler
and drawing the picture.
Windows NT, Windows 2000 and Windows XP do only allow an application
to overwrite the Windows event handler of an own window. The example
is more or less self explaining. The picture is loaded using OleLoadPicture()
.
The picture will be drawn using the Windows standard graphic API
function strechblt()
.
To supersede the Windows event handler, SetWindowLong()
is used.
Details
The drawing routine
The main drawing is done within the ondraw
method. In case that the
OCX should draw on other windows than itself, it will call ondraw
directly.
As the OCX is generated using MFC, the method header is standard and has
been automatically generated:
void CPictureZoomCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
PictureZoom
is using a flag to identify whether a picture has
already been loaded or not:
if (m_bNoPicture)
return;
The first step is to create a compatible DC which needs to hold the picture:
CDC mdc;
mdc.CreateCompatibleDC(pdc);
CBitmap* pOld = mdc.SelectObject(&m_Picture);
To prevent disturbing colors when drawing a stretched or shrunken picture,
set the color mode properly:
pdc->SetStretchBltMode(COLORONCOLOR);
mdc.SetStretchBltMode(COLORONCOLOR);
In order to work with the picture, we needs its dimension:
m_Picture.UnrealizeObject();
BITMAP BM;
m_Picture.GetBitmap(&BM);
The next parts are a little tricky as we need to discern between 3 ways
of displaying the picture. The first is to draw it without keeping the aspect
ratio of the original picture:
if (!m_bAspectRatio)
pdc->StretchBlt(0, 0, rcBounds.Width(),
rcBounds.Height(),&mdc, 0, 0,
BM.bmWidth, BM.bmHeight, SRCCOPY);
The next block differs between the other two cases:
else
{
double Strech = double(rcBounds.Width()) / double(BM.bmWidth);
int iHeight = Strech * BM.bmHeight;
int iWidth = Strech * BM.bmWidth;
int yMove = ((iHeight - rcBounds.Height()) / 2) / Strech;
The first of these other two cases is that the picture needs to be cut
at the top and bottom to draw it in a zoomed way over the whole given window.
The first step to do so is to calculate the upper left corner
on the source (unscaled!) bitmap:
if (yMove >= 0)
{
int xp = -m_xAdd;
int yp = yMove - m_yAdd;
if (xp < 0)
xp = 0;
if (yp < 0)
yp = 0;
if (xp > iWidth - rcBounds.Width())
xp = iWidth - rcBounds.Width();
if (yp > (iHeight - rcBounds.Height()) / Strech)
yp = (iHeight - rcBounds.Height()) / Strech;
Now as I was a little unsatisfied with center-only display of pictures,
I decided to allow some alignment-settings as well:
if (m_Align == TOP)
yp = 0;
else if (m_Align == BOTTOM)
yp = (iHeight - rcBounds.Height()) / Strech;
m_xAdd = -xp;
m_yAdd = yMove - yp;
The last step in case one is to simply blit the Source-Rectangle
on the target window using strechblt()
:
CRect SourceRect((rcInvalid.left * BM.bmWidth) /
rcBounds.Width() + xp,
(rcInvalid.top * (rcBounds.Height() / Strech)) /
rcBounds.Height() + yp,
(rcInvalid.right * BM.bmWidth) / rcBounds.Width()
+ xp, (rcInvalid.bottom * (rcBounds.Height() /
Strech)) / rcBounds.Height() + yp);
pdc->StretchBlt(rcInvalid.left, rcInvalid.top,
rcInvalid.Width(), rcInvalid.Height(),&mdc,
SourceRect.left, SourceRect.top,
SourceRect.right - SourceRect.left,
SourceRect.bottom - SourceRect.top, SRCCOPY);
}
The second case is more or less equal to the step before.
The only difference is that the picture will now be truncated
on the left and right hand-sides. This happens, when the window
on which to draw has a proportion less than 1 (seen as width against height).
else
{
Strech = double(rcBounds.Height()) / double(BM.bmHeight);
int iHeight = Strech * BM.bmHeight;
int iWidth = Strech * BM.bmWidth;
int xMove = ((iWidth - rcBounds.Width()) / 2) / Strech;
int xp = xMove - m_xAdd;
int yp = -m_yAdd;
if (xp < 0)
xp = 0;
if (yp < 0)
yp = 0;
if (xp > (iWidth - rcBounds.Width()) / Strech)
xp = (iWidth - rcBounds.Width()) / Strech;
if (yp > iHeight - rcBounds.Height())
yp = iHeight - rcBounds.Height();
Again, I decided to allow some alignment (to the left or right border of the window):
if (m_Align == LEFT)
xp = 0;
else if (m_Align == RIGHT)
xp = (iWidth - rcBounds.Width()) / Strech;
m_xAdd = xMove - xp;
m_yAdd = -yp;
CRect SourceRect((rcInvalid.left *
rcBounds.Width()/Strech) / rcBounds.Width() +
xp, (rcInvalid.top * BM.bmHeight) / rcBounds.Height()
+ yp, (rcInvalid.right * rcBounds.Width()/Strech)
/ rcBounds.Width() + xp, (rcInvalid.bottom * BM.bmHeight)
/ rcBounds.Height() + yp);
pdc->StretchBlt(rcInvalid.left, rcInvalid.top,
rcInvalid.Width(), rcInvalid.Height(),&mdc,
SourceRect.left, SourceRect.top,
SourceRect.right - SourceRect.left,
SourceRect.bottom - SourceRect.top, SRCCOPY);
}
}
The last step of drawing is to free all used resources:
mdc.SelectObject(pOld);
mdc.DeleteDC();
}
This article will be continued in the next days as soon as there is
more time between work and studies.