how to Improve DrawDIB's quality?

蹲街弑〆低调 提交于 2019-12-08 04:53:28

问题


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?


回答1:


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.




回答2:


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.




回答3:


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




回答4:


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



来源:https://stackoverflow.com/questions/1117537/how-to-improve-drawdibs-quality

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!