【问题标题】:Double Buffering an image with GDI+使用 GDI+ 对图像进行双重缓冲
【发布时间】:2012-11-19 04:29:09
【问题描述】:

我有一个存储在 GDI+ 图像数据类型中的图像 (.png)。谁能告诉我如何获取存储在内存 DC 内的 GDI+ 图像变量中的图像数据?

这是一些代码:

Graphics graphics(hdc);
Image image(pStream);

int image_width;
int image_height;

image_width= image.GetWidth();
image_height=image.GetHeight();

graphics.DrawImage(&image, posX,posY, image_width, image_height);    

目标是能够双缓冲这个 GDI+ 图像(为了动画!)。

我知道如何使用 GDI 进行双重缓冲,但不知道如何使用 GDI+。使用 GDI,只需将 HBITMAP 选择到内存 DC 中,但是,使用 GDI+,图像不在 HBITAP 中,而是在图像变量中。谁能告诉我如何将不是 HBITMAP 的图像放入内存 DC 中?谢谢。

【问题讨论】:

    标签: c++ winapi


    【解决方案1】:

    这是我用于从磁盘加载图像的代码。

    // BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
    HBITMAP mLoadImg(WCHAR *szFilename)
    {
       HBITMAP result=NULL;
    
       Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(szFilename,false);
       bitmap->GetHBITMAP(NULL, &result);
       delete bitmap;
       return result;
    }
    

    编辑: 除了一些基本功能外,我自己不要使用 GDI+。每当我使用图像时,我通常都希望代码尽可能小、速度快——GDI 在这方面做得更好。

    这是从一个类中提取的代码,该类将显示具有透明像素的图像,并附有类定义,以便(希望)避免与类变量产生任何歧义。

    void CStaticImg::displayImage()
    {
        RECT myRect;
        BITMAP bm;
        HDC screenDC, memDC;
        HBITMAP oldBmp;
        BLENDFUNCTION bf;
    
        GetObject(mBmp, sizeof(bm), &bm);
    
        bf.BlendOp = AC_SRC_OVER;
        bf.BlendFlags = 0;
        bf.SourceConstantAlpha = 0xff;
    
        bf.AlphaFormat = AC_SRC_ALPHA;
    
        screenDC = GetDC(mHwnd);
        GetClientRect(mHwnd, &myRect);
    
        if (mBmp == NULL)
            FillRect(screenDC, &myRect, WHITE_BRUSH);
    
        else
        {
            memDC = CreateCompatibleDC(screenDC);
            oldBmp = (HBITMAP)SelectObject(memDC, mBmp);
            AlphaBlend (screenDC, 0, 0, myRect.right,myRect.bottom, memDC, 0, 0, bm.bmWidth,bm.bmHeight, bf);
            SelectObject(memDC, oldBmp);
            DeleteDC(memDC);
            ReleaseDC(mHwnd, screenDC);
        }
    }
    
    class CStaticImg
    {
    public:
        CStaticImg();
        ~CStaticImg();
        void setImg(HBITMAP img);
        HBITMAP getImgCopy();
        void attach(HWND tgt);
        void detach();
        void setBkMode(bool transparent);
    
    protected:
        HWND mHwnd;
        HBITMAP mBmp;
        WNDPROC mOldWndProc;
        void displayImage();
        virtual LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
        bool isBkgTransparent;
    private:
    //    virtual LRESULT onPaint();
        LRESULT onCreate();
        static CStaticImg *GetObjectFromWindow(HWND hWnd);
        static LRESULT CALLBACK stWinMsgHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    };
    

    【讨论】:

    • 对不起.. 不知何故错过了您的回复!我试过你的代码,它没有工作?这是我尝试过的: DrawImage(hdc, 0, 0, (mLoadImg((WCHAR*)"myimage.png"); DrawImage 是一个双缓冲 hbitmap 并将其绘制到屏幕上的函数!
    • 没有压力的伙伴。 :) 我将启动编译器并检查 - 我认为问题在于您传递文件名的方式。您需要使用 L(或者可能是 TEXT)宏来确保您实际上传递的是一个宽字符串(即每个 char 2 个字节),而不是将 char* 转换为 WCHAR*,只需几分钟,我会回来的
    • @JamesDiaz - 是的,这将是你的问题。考虑以下两个具有相同效果: (1) mBmp = (HBITMAP) LoadImage(NULL, "bitmap32.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE); (2) mBmp = mLoadImg(L"bitmap32.bmp"); 注意: 文件名前面的 L。 :)
    • 太棒了,它成功了!但是,我注意到使用此功能,不会保留 png 中的 alpha 透明度 :( 我在上面发布了我的完整代码,关于如何显示具有 alpha 透明度的 png 图像。希望有人能够告诉我如何加倍缓冲用这种方法制作一个png,谢谢。
    • 感谢您经历了这么多来回答这个问题。我还没有尝试过你的代码,但我相信它会起作用!我接受了您的回答,仅仅是因为您为此付出了巨大的努力(实际上您确实以多种方式回答了我的问题。)谢谢。
    【解决方案2】:

    你看过Bitmap 类吗?它继承自 Image,可用于访问原始图像数据:

    Bitmap bmp(pStream);
    BitmapData bitmapData;
    Rect rect(0, 0, 200, 200);
    // lock area of the image for writing
    bmp.LockBits(&rect, ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData);
    

    更新要获得 HBITMAP,可以使用类似的方法(注意:我没有测试下面的代码):

       HBITMAP hbmp = CreateCompatibleBitmap(hdc, width, height);
       BITMAPINFO bmi;
       bmi.bmiHeader.biSize =  sizeof(BITMAPINFOHEADER);
       bmi.bmiHeader.biWidth = width;
       bmi.bmiHeader.biHeight = height;
       bmi.bmiHeader.biPlanes = 1;
       bmi.bmiHeader.biBitCount = 24;
       bmi.bmiHeader.biCompression = BI_RGB;
       bmi.bmiHeader.biSizeImage = 0;
       bmi.bmiHeader.biXPelsPerMeter = 0;
       bmi.bmiHeader.biYPelsPerMeter = 0;
       bmi.bmiHeader.biClrUsed = 0;
       bmi.bmiHeader.biClrImportant = 0;
       bmi.bmiColors = NULL;
    
       SetDIBits(hdc, hbmp, 0, height,  bitmapData.Scan0, &bmi, 0);
    

    【讨论】:

    • 抱歉,我是 Win32 新手 ;) 有没有办法将 Gdiplus::Bitmap 转换为 HBITMAP?
    • 我已经发布了一种获取 HBITMAP 答案的可能方法。为什么还需要 HBITMAP?
    • 我希望将 HBITMAP 双重缓冲到内存 DC 中,但是,我发现 HBITMAP 不会保留 png 图像的 alpha 透明度:P 我将我的完整代码发布在我的原始文件中发布,希望有人能够帮助我使用我最初用来显示图像的方法来双重缓冲我的图像..
    最近更新 更多