Invert (mirror) a bitmap in-place

It is possible to invert a bitmap (laterally or vertically) IN-PLACE - ie. without creating any intermediate or temporary bitmaps. You can modify the original bitmap directly. This can be very useful if you are wanting to reflect a LARGE bitmap, and don't want to allocate the extra storage. The operation requires 50% more StretchBlt operations, but does not have the overhead of creating the extra bitmaps and device contexts. Compare this to GetInvertedBitmap presented elsewhere which needs to make a copy of the original bitmap.

The Windows SDK appears to provide very few functions for manipulating the contents of a bitmap. Sure, you can draw on a bitmap using GDI functions. And you can copy a rectangular part of a bitmap onto another bitmap with BitBlt or StretchBlt, but that's about it.

However, the BitBlt and StretchBlt routines are quite powerful little beasts, and you can do all sorts of manipulations using them.

For example, you can use StretchBlt to copy a bitmap onto another bitmap, but reflect it in the x or y axis along the way. And if you do both at once, you can copy the image flipped upside down. To do this, you specify a negative value for the destination width (for a y-axis reflection) or destination height (or a x-axis reflection).

You can also use the various raster operations (or ROPs) to achieve various interesting effects by specifying how the source and destination bits interact with each other.

All these operations are good for copying from one bitmap to another ( including copy from a bitmap to the screen - which is just another bitmap anyway). But what about actually CHANGING a bitmap itself?

Well, you could make a second bitmap, do your magic with it, and then copy the result back. However, that means you need an extra BitBlt to copy back, AND you need extra storage for the temporary bitmap.

A better solution is to do all the changes in place on the original bitmap. This is not always possible, but when it is, you get savings in both memory and processing.

In this article we will look at using StretchBlt to do x and y axis reflections in-place, WITHOUT needing another bitmap.

Before leaping straight into some code, lets go back to one of the simplest functions a beginning program learns how to write - the function to swap the values of two variables.

void Swap (int& a, int &b) {
     int temp = a;
     a = b;
     b = temp;
}

This function swaps two integer variables. Note that this requires the use of a temporary variable, which I have given the unoriginal name 'temp'. We make a copy of 'a' in 'temp', overwrite 'a' with 'b' (but we still have a copy in 'temp') and then finally clobber 'b' with the saved value in 'temp'. All very simple - and a temporary int is not a problem.

But what if a and b are large structures or blocks of memory instead? Then a temporary could be an expensive thing indeed? But how can we swap the values without it?

Elementary, my dear Watson, we use logic!

One of the basic primitive operations that you can do to a lump of bits is called an exclusive-or ('XOR') operation. It is a cousin of the more familiar 'AND' and 'OR' operations that we all know and love. When you combine two values with an 'XOR', you get a 1-bit for each bit that is different and a '0' bit for each that is the same. That is, it sets a bit if and only if one or the other (but not both) of the input bits are set.

Here are some 'truth tables' that might clear it up. Compare the table for XOR with those for AND and OR.

XOR     0       1         AND   0       1         OR    0       1
0       0       1           0   0       0           0   0       1
1       1       0           1   0       1           1   1       1

By looking at the truth table for XOR, you can see that it effectively toggles (or swaps) the state of a bit of one input when the corresponding bit of the other input is 1.

Also not that '(A XOR B) XOR B)' returns A, regardless of B. The first XOR with B swaps the state of some of the bits, and then the second XOR swaps the same states back again.

In C (and C++) the XOR operator is '^', so 'a ^ b' will do a bit-wise XOR operation between a and b and return the result. There is also a XOR-assignment operator '^=', so 'a ^= b' will do a bit-wise XOR of b ont a, replacing the original value of a.

Now comes some magic, we can use XOR's bit state swapping abilities to do our entire integer swapping WITHOUT a temporary.

void InPlaceSwap (int& a, int &b) {
     a ^= b;
     b ^= a;
     a ^= b;
}

Let's look at some bits to see this in action. We'll start off with a=12 and b = 10 (in binary a=1100, b=1010). Watch what happens to the bits as each statement of InPlaceSwap executes:

                  a     b      Algebra
               1100  1010
    a ^= b;    0110  1010      a' = a^b
    b ^= a;    0110  1100      b' = b^a' = b^a^b = a
    a ^= b;    1010  1100      a" = a'^b' = a^b^a = b

The end result is that a and b are swapped.

You can use the same technique to swap to large blocks of memory by XOR-ing their bits using the same algorithm. In fact, you can swap chunks of bits in a BITMAP using this algorithm - by using BitBlt with the SRCINVERT raster op, which does an XOR operation.

void SwapBlt (CDC* pDC1, int x1, int y1,
                 int nWidth, int nHeight,
                 CDC* pDC2, int x2, int y2
                 ) {
     if (! nWidth || ! nHeight) return;
     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);
}

The SwapBlt function above uses exactly the same algorithm as the SwapInPlace function previously to swap the rectangle of size nWidth x nHeight at [x1, y1] with the one at [x2,y2]. Each BitBlt simply does an XOR of two chunks of bitmap memory.

By creating a compatible device context and selecting a bitmap into it, you can use SwapBlt to swap the left hand side and right hand side of the bitmap image as shown below.

First we need a helper function to give us the actual width and height of the bitmap.

void GetWidthAndHeight(CBitmap* pBitmap, int* pw, int* ph) const {
     if (! pBitmap && ! pBitmap->GetSafeHandle()) {     // no bitmap yet
          if (pw) *pw = 0;
          if (ph) *ph = 0;
     } else {
          BITMAP bm;
          pBitmap->GetObject(sizeof(bm), &bm);
          if (pw) *pw = bm.bmWidth;
          if (ph) *ph = bm.bmHeight;
     }
}

Here's the swapper:

void SwapLeftAndRightHalves (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;
     ::SwapBlt(pDC,0,0,nHalfWidth,nHeight,pDC,nWidth-nHalfWidth,0);
     // reselect old bitmap into DC
     pDC->SelectObject(pBmpOldImage);
}

This function simply works out the width of the two halves, by halving the bitmap width. Then it swaps the left and right sides by passing the same DC for both arguments of SwapBlt, and by giving positions and sizes for the left and right hand sides.

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.

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);
}

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.

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.

The magic here is to write a version of SwapBlt the also does the reflection at the same time.

Lets see if this will work with some test data nd some pseudo-code

                          a     b
                       1100  1010
     a ^= reverse(b);  1001  1010
     b ^= reverse(a);  1001  0011
     a ^= reverse(b);  0101  0011

We do indeed end up with a = reverse(b) and b = reverse(a). Overall we have a mirror of the original bit pattern.

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

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
          );
}

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.

Now we can rewrite our 'SwapLeftAndRightHalves' function to not just swap, but to actually do the mirror.

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);
}

This require only 3 StretchBlt operations, each on a half of the bitmap, so is very efficient.

Of course the same technique can be used to mirror top and bottom.

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

//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 );
     }
}



Comments

  • Replica Oakley Miss Conduct wholesale store

    Posted by izyypwniq on 06/27/2013 11:58am

    ray ban outlet ,The sunglasses also called sun mirror (or sunglasses) generally must prevent glare, UV and glare. She wore beautiful, she is only some of the one. Anybody are searhing for (celebrities), to determine what are the latest trends, they wear is a huge support and recognition. Fak Oakley Sunglasses ,Oakley Eyewear lenses in bright light top in sunny conditions, each of the lens use a truer color perception. Amazing fan following may be because all of Oakley is virtually achieved after years of investigation and progression of excellence and also a symbol of excellence. Oakley Pit Boss ,If you plan to purchase sunglasses, usually do not waste your time elsewhere! Oakley out of your shelf, tailored for the art of optical wonders, might strike envy from the minds of others. Oakley sunglasses are stylish and beautiful lady, Oakley sunglasses, wife of colors and fashoins to ensure they've got more fashion. Suggests that a particular glasses, which assists to make certain it truly is along with his or her actual eye quality, a variety of factors unique importance. In mid-summer day, UV radiation might be often 3 x more in winter. Above all, the extent of UV trigger is essentially the greatest harm, ultraviolet radiation, will probably be around 10-fold increase. The utmost protection of the eyes, HDO upgrade lens 100% UV filtering also produces the best and finest quality materials to make the certain durability. Oakley sunglasses will provide you with 100% of harmful UV protection, for the reason that design of these lenses besides provides you with the best convergence, even so the highest a higher level security, performance and luxury. The integration of science and art Oakley won in excess of 600 patents worldwide. Today, Jannard's brand has turned into a prominent symbol of success. Is visible clearly and luxury: Oakley sunglasses can readily improve visual comfort and image quality because of the glare elizabeth.h maintain your fundamental of the human eye. For the people using grocery list externally addicts, polarized sunglasses, the tough sunlight and glare perfect handling, and applicable with other sports as skiing, fishing, or actively play. The trendy appearance and fashion style, it will be your sensible choice. Select your discount Oakley sunglasses collection, you find the right sunglasses.

    Reply
  • Excellent

    Posted by Legacy on 07/08/2003 12:00am

    Originally posted by: cyberman_77

    Excellent, Keep up the great work.

    Reply
  • VC++

    Posted by Legacy on 05/11/2000 12:00am

    Originally posted by: R. Senthil Velan

    I want do a program in VC++ Graphics from my project. 
    
    

    I must be a MID Window
    In each window, Iwant to draw a Independent object like Animation.

    Please help me, to do this in successful manner.

    Thanking you

    R. Senthil Velan

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • A majority of organizations are operating under the assumption that their network has already been compromised, or will be, according to a survey conducted by the SANS Institute. With many high profile breaches in 2013 occurring on endpoints, interest in improving endpoint security is top-of-mind for many information security professionals. The full results of the inaugural SANS Endpoint Security Survey are summarized in this white paper to help information security professionals track trends in endpoint …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds