views:

501

answers:

6

I am coding in c++, gdi I use stretchDIBits to draw Images to dc.

        ::SetStretchBltMode(hDC, HALFTONE);
     ::StretchDIBits(
      hDC,
      des.left,des.top,des.right - des.left,des.bottom - des.top,
      0, 0,
      img.getWidth(),
      img.getHeight(),
      (img.accessPixels()), 
      (img.getInfo()),
      DIB_RGB_COLORS,
      SRCCOPY
      );

However It is slow. So I changed to use DrawDib function.

::SetStretchBltMode(hDC, HALFTONE);
DrawDibDraw(
               hdd,
               hDC,
               des.left,des.top,des.right - des.left,des.bottom - des.top,
               (LPBITMAPINFOHEADER)(img.getInfo()),
               (img.accessPixels()), 
               0, 0,
               img.getWidth(),
               img.getHeight(),
               DDF_HALFTONE
               );

However the result is just like draw by COLORONCOLOR Mode. How can I improve the drawing quality?

A: 

When you shrink your bitmap image you have a couple of choices. You can do it the slow way where you "blur and subsample" the full sized image so that places where you have one pixel and the source image had 2+ pixels, the new pixel becomes the average. If you don't do that you get a lot faster perforamce, but your image won't be as smooth as you might want.

StretchDIBits might be averaging pixels in 2D, meaning that it looks to a 4+ pixel block from the old image to get the average color of the new pixel. If that's the case you can write your own 1D averaging that just averages the pixels same scanline of the old image to get the new pixel. A windows api guru might know a cool option to DrawDIBDraw that does something like this.

Al Crowley
+1  A: 

Well DrawDibDraw is outdated.

Have you considered trying to speed up StretchDIBits? There is a good answer here

You can of course do it without using StretchDIBits at all.

if you initially load your image by

hBitmap = LoadImage( NULL, _T( "c:\\Path\File.bmp" ), IMAGE_BITMAP, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_LOADFROMFILE );

SIZE size;
BITMAP bmp;
GetObject( (HGDIOBJ)hBitmap, sizeof( BITMAP ), &bmp );
size.cx = bmp.bmWidth;
size.cy = bmp.bmHeight;

You can then render the bitmap as follows.

HDC hBitmapDC   = CreateCompatibleDC( hDC );

HGDIOBJ hOld    = SelectObject( hBitmapDC, (HGDIOBJ)hBitmap );

SetStretchBltMode( hDc, HALFTONE );

StretchBlt( hDC, rcItem.left,rcItem.top, rcItem.right,rcItem.bottom, hBitmapDC, 0, 0, size.cx, size.cy, SRCCOPY );

SelectObject( hBitmapDC, hOld );
DeleteObject( hBitmapDC );

Of course its worth bearing in mind that you don't actually need to create the compatible DC each time you blt which will speed things up considerably. Just create the compatible DC and select the bitmap object to it when you load the image. Then hold it around until you need it. On shutdown simply DeleteObject as shown above.

Goz
Thanks Goz, It is a good solution when the Image is not too large. because CompatibleDC occupies memory of Videocard, if there is not enough memory on Videocard. this method will not work
A: 

In this article described way to perform antialiasing using just plain Windows GDI. I didn't tried it myself(since I've used GDI+ finally), but it looks promising.

Kirill V. Lyadvinsky
A: 

I don't know what else you're doing in your app or how important drawing stretched bitmaps is for you, but you may just want to use a more modern API like GDI+ or DirectX or OpenGL.

Nobody has changed how StretchDIBits or DrawDibDraw work since 1995 or so--you really want something that will take advantage of the GPU. (I would have expected that the drawing would be "fast enough" even with the CPU just because processors are a lot faster than they were back then, but apparently not.)

David Maymudes
A: 

If you don't want to use opengl/directx/gdi+ but are stuck in DIBs, I found the fastest is to just do it yourself.

Only thing is you need access to the actual bytes of the image. Furthermore, the destination needs to be a DIBbitmap, since this needs to be blitted (not stretchblitted) to your windows form.

so given you have 2 byte pointers, 1 src and 1 dest. Both pointing to the rawbytes (each pixel consists of 3 bytes (RGB)) then your algorithm would look like this

 //these need to be filled out by you
 //byte *src;   //points to the first raw byte of your source RGB picture
 //byte *dest;  //points to the first raw byte of your destination RGB picture
 //int srcWidth = 640; //width of original image
 //int srcHeight = 480; //height of original image
 //int destWidth = 200; //width of destination image
 //int destHeight = 100; //height of destination image


 void scaleImage(byte *src, byte*dest, int srcWidth, int srcHeight, int destWidth, int destHeight)
 {
      //these are internal counters
      register int srcx;
      register int srcy;
      register int skipx;
      register int skipy;

      skipx = (destWidth>>8)/srcWidth;
      skipy = (destHeight>>8)/srcHeight;

      for(int y=0; y<destHeight; y++)
      {
           //calc from which y coord we need to copy pixel
           register byte *src2 = src + ((srcy>>8)*srcWidth*3);

           for(int x=0; x<destWidth; x++)
           {
                //calc from which x coord we need to copy pixel
                register byte *src3 = src2 + ((srcx>>8)*3);

                //copy rgb
                *dest++ = *src3++;
                *dest++ = *src3++;
                *dest++ = *src3++;

                //go to next x pixel
                srcx += skipx;
           }

           //go to next y pixel
           srcy += skipy;
      }
 }

The >>8 and <<8 which you see scattered through the code is the use of 'fixed point'.

To make the scaling look better (since this scale is done using 'nearest neighbour'), you should take the average between the adjacent right pixel, the adjacent bottom pixel and the adjacent bottom right pixel. Make sure not to use a division ('/') but use >>2 which is much faster.

p.s. ah right..the code is off the top of my head so beware of small typos

Toad
A: 

Whenever you see the word HALFTONE in the old GDI documentation, it is not about better interpolation, but with dithering images "intelligently" to palettes. It only does something when your image is full-color and your screen is 8 bit palette, and even then you can do a better job yourself.

I used to work on a C++ imaging workstation. We did the interpolation ourselves, for various reasons, and then BitBlt'ed to the screen. Works great for grayscale, would'nt reccomend it for color.

GDI+ is not accellerated, and therefore too slow for video. If you need good performance(30+ frames/second?), go for Direct3d. You don't need to learn about matrix transforms to do 2D in Direct3d. There is some COM involved, though...

jdv
Not true. [`SetStretchBltMode`][1] to HALFTONE actually causes averaging of source pixels when shrinking a bitmap. It works quite well with 24- and 32-bit bitmaps and displays. [1]: http://msdn.microsoft.com/en-us/library/dd145089(VS.85).aspx
Adrian McCarthy