📄 invert_bitmap_inplace.shtml.htm
字号:
DC
for both arguments of SwapBlt, and by giving positions and sizes for the
left
and right hand sides.
<p>This is the first step toward doing a reflection. Now if only we could
reflect each half we would be done. That is exactly what we can do - with
a
little bit of recursive programming.
<PRE><TT><FONT COLOR="#990000">
void MirrorLeftAndRightHalves (CBitmap* pBitmap) {
if (!pBitmap || ! pBitmap->GetSafeHandle()) return;
// create DC and select bitmap into it
CDC dc;
CDC* pDC = &dc;
pDC->CreateCompatibleDC( NULL );
CBitmap* pBmpOldImage = pDC->SelectObject(pBitmap);
// get bitmap size
int nWidth,nHeight;
::GetWidthAndHeight(pBitmap,&nWidth,&nHeight);
// do the mirror
::RecursiveMirrorBlt(pDC,0,0,nWidth,nHeight);
// reselect old bitmap into DC
pDC->SelectObject(pBmpOldImage);
}
BOOL RecursiveMirrorBlt (CDC* pDC1, int x1, int y1,
int nWidth, int nHeight
) {
int nHalfWidth = nWidth/2;
if (nHalfWidth < 1) return;
int x2 = x1+nWidth-nHalfWidth;
::SwapBlt(pDC,x1,y1,nHalfWidth,nHeight,pDC,x2,y1);
RecursiveMirrorBlt(pDC,x1,y1,nHalfWidth,nHeight);
RecursiveMirrorBlt(pDC,x2,y1,nHalfWidth,nHeight);
}
</FONT></TT></PRE>
<p>The 'MirrorLeftAndRightHalves' function is similar to our earlier
'SwapLeftAndRightHalves'. However, instead of calling SwapBlt it calls
'RecursiveMirrorBlt'. This helper function works out the width of the two
halves and calls SwapBlt to swap them. Then it calls itself recursively to
mirror each half. The recursion stops when we get down to a single pixel
wide column to mirror, at which stage we have successfully mirrored the
bitmap image.
<p>Now, this recursive method is not very efficient - it involves move BiBlt
operations than are necessary. We could expand this out to an iterative
method, but there is an even easier way of achieving the same result
without
looping or recursion.
<p>The magic here is to write a version of SwapBlt the also does the
reflection at
the same time.
<p>Lets see if this will work with some test data nd some pseudo-code
<PRE><TT><FONT COLOR="#990000">
a b
1100 1010
a ^= reverse(b); 1001 1010
b ^= reverse(a); 1001 0011
a ^= reverse(b); 0101 0011
</FONT></TT></PRE>
<p>We do indeed end up with a = reverse(b) and b = reverse(a). Overall we
have
a mirror of the original bit pattern.
<p>We can do this reverse and XOR using StretchBlt with a negative value for
the
width. The function to do this is very much like SwapBlt
<PRE><TT><FONT COLOR="#990000">
void SwapYInvertBlt (CDC* pDC1, int x1, int y1,
int nWidth, int nHeight,
CDC* pDC2, int x2, int y2
) {
if (! nWidth || ! nHeight) return;
pDC1->StretchBlt (
x1,y1, nWidth,nHeight,
pDC2,
x2+nWidth-1,y2, -nWidth, nHeight,
SRCINVERT
);
pDC2->StretchBlt (
x2,y2, nWidth,nHeight,
pDC1,
x1+nWidth-1,y1, -nWidth, nHeight,
SRCINVERT
);
pDC1->StretchBlt (
x1,y1, nWidth,nHeight,
pDC2,
x2+nWidth-1,y2, -nWidth, nHeight,
SRCINVERT
);
}
</FONT></TT></PRE>
<p>Notice the negative sign in each StretchBlt for the width. And also notice
that the destination x value is adjusted to take into account the negative
width.
<p>Now we can rewrite our 'SwapLeftAndRightHalves' function to not just swap,
but to actually do the mirror.
<PRE><TT><FONT COLOR="#990000">
void MirrorLeftAndRightHalves (CBitmap* pBitmap) {
if (!pBitmap || ! pBitmap->GetSafeHandle()) return;
// create DC and select bitmap into it
CDC dc;
CDC* pDC = &dc;
pDC->CreateCompatibleDC( NULL );
CBitmap* pBmpOldImage = pDC->SelectObject(pBitmap);
// get bitmap size
int nWidth,nHeight;
::GetWidthAndHeight(pBitmap,&nWidth,&nHeight);
// do the swap
int nHalfWidth = nWidth/2;
if (nHalfWidth < 1) return;
::SwapYInvertBlt(pDC,0,0,nHalfWidth,nHeight,pDC,nWidth-nHalfWidth,0);
// reselect old bitmap into DC
pDC->SelectObject(pBmpOldImage);
}
</FONT></TT></PRE>
<p>This require only 3 StretchBlt operations, each on a half of the bitmap, so
is very efficient.
<p>Of course the same technique can be used to mirror top and bottom.
<p>We can use these techniques to derive from class CBitmap and add in-place
inverting functionality to it. Here, I have single function 'Invert' which
takes a BOOL value indicating whether we want left/right or top/bottom
mirroring. Also note that some of the functions above now appears as
member
functions of this class
<PRE><TT><FONT COLOR="#990000">
//MyBitmap.h
class CMyBitmap : public CBitmap {
public:
void GetWidthAndHeight (int* pw, int* ph) const;
public:
void Invert (BOOL bLateral);
};
// Global helpers
void SwapBlt (CDC* pDC1, int x1, int y1, int dx, int dy, CDC* pDC2, int x2, int y2);
void SwapXReflectBlt (CDC* pDC1, int x1, int y1, int dx, int dy, CDC* pDC2, int x2, int y2);
void SwapYReflectBlt (CDC* pDC1, int x1, int y1, int dx, int dy, CDC* pDC2, int x2, int y2);
//MyBitmap.cpp
#include "stdafx.h"
#include "MyBitmap.h"
void CMyBitmap::GetWidthAndHeight(int* pw, int* ph) const {
if (! GetSafeHandle()) { // no bitmap yet
if (pw) *pw = 0;
if (ph) *ph = 0;
} else {
BITMAP bm; GetObject(sizeof(bm), &bm);
if (pw) *pw = bm.bmWidth;
if (ph) *ph = bm.bmHeight;
}
}
void CMyBitmap::Invert (BOOL bLateral) {
// must have a bitmap
if (! GetSafeHandle()) return;
// create DC and select this into it
CDC dc; CDC* pDC = &dc;
pDC->CreateCompatibleDC( NULL );
int nWidth,nHeight; GetWidthAndHeight(&nWidth,&nHeight);
CBitmap* pBmpOldImage = pDC->SelectObject(this);
// do the reflection
if (bLateral) {
int nHalfWidth = nWidth/2; if (nHalfWidth < 1) return;
SwapYInvertBlt(pDC,0,0,nHalfWidth,nHeight,pDC,nWidth-nHalfWidth,0);
} else {
int nHalfHeight = nHeight/2; if (nHalfHeight < 1) return;
SwapXReflectBlt(pDC,0,0,nWidth,nHalfHeight,pDC,0,nHeight-nHalfHeight);
}
pDC->SelectObject(pBmpOldImage);
}
// swap two rectangles in two (different or same) DC's (ie. bitmaps)
void SwapBlt (CDC* pDC1, int x1, int y1, int nWidth, int nHeight, CDC*pDC2, int x2, int y2) {
if (nWidth && nHeight) { // ignore degenerate cases
pDC1->BitBlt( x1,y1, nWidth,nHeight, pDC2, x2,y2, SRCINVERT );
pDC2->BitBlt( x2,y2, nWidth,nHeight, pDC1, x1,y1, SRCINVERT );
pDC1->BitBlt( x1,y1, nWidth,nHeight, pDC2, x2,y2, SRCINVERT );
}
}
// swap two rectangles in two (different or same) DC's (ie. bitmaps) and reflect in X asis
void SwapXReflectBlt (CDC* pDC1, int x1, int y1, int nWidth, int nHeight, CDC* pDC2, int x2, int y2) {
if (nWidth && nHeight) { // ignore degenerate cases
pDC1->StretchBlt( x1,y1, nWidth,nHeight, pDC2, x2,y2+nHeight-1,nWidth, -nHeight, SRCINVERT );
pDC2->StretchBlt( x2,y2, nWidth,nHeight, pDC1, x1,y1+nHeight-1,nWidth, -nHeight, SRCINVERT );
pDC1->StretchBlt( x1,y1, nWidth,nHeight, pDC2, x2,y2+nHeight-1,nWidth, -nHeight, SRCINVERT );
}
}
// swap two rectangles in two (different or same) DC's (ie. bitmaps) and reflect in Y axis
void SwapYInvertBlt (CDC* pDC1, int x1, int y1, int nWidth, int nHeight,CDC* pDC2, int x2, int y2) {
if (nWidth && nHeight) { // ignore degenerate cases
pDC1->StretchBlt( x1,y1, nWidth,nHeight, pDC2, x2+nWidth-1,y2,-nWidth, nHeight, SRCINVERT );
pDC2->StretchBlt( x2,y2, nWidth,nHeight, pDC1, x1+nWidth-1,y1,-nWidth, nHeight, SRCINVERT );
pDC1->StretchBlt( x1,y1, nWidth,nHeight, pDC2, x2+nWidth-1,y2,-nWidth, nHeight, SRCINVERT );
}
}
</FONT></TT></PRE>
<P>
<HR>
<TABLE BORDER=0 WIDTH="100%" >
<TR>
<TD WIDTH="33%"><FONT SIZE=-1><A HREF="../index.htm" tppabs="http://www.codeguru.com/">Goto HomePage</A></FONT></TD>
<TD WIDTH="33%">
<CENTER><FONT SIZE=-2>© 1997 Zafir Anjum</FONT> </CENTER>
</TD>
<TD WIDTH="34%">
<DIV ALIGN=right><FONT SIZE=-1>Contact me: <A HREF="mailto:zafir@home.com">zafir@home.com</A> </FONT></DIV>
</TD>
</TR>
</TABLE>
<CENTER><FONT SIZE=-2>7275</FONT></CENTER>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -