【问题标题】:Legacy C++ code not displaying bitmap under Windows 10旧版 C++ 代码在 Windows 10 下不显示位图
【发布时间】:2020-01-27 10:24:46
【问题描述】:

我必须让一些旧版 (XP) MFC 代码在 Windows 10 下运行。它在 Windows 7 下运行良好。我没有尝试任何其他版本。

这个想法是内存中有一个缓冲区,其中包含 RGB 中的图像像素值。 然后将图像显示到屏幕上,将其转换为位图,然后复制到 DC。

这是将对象显示到屏幕上的函数。 它似乎可以工作,但屏幕上除了一个白框外什么都没有显示。

(cmets 已经在那里了!其他人一定也从中获得了一些乐趣!)

WORD* CFBuffer24::getBitmap(int nBitsPerPixel)
{
    // check operation is valid
    ASSERT(m_pFB[RED] && m_pFB[GREEN] && m_pFB[BLUE]);

    if (nBitsPerPixel == 24)
    {
        if(NULL == m_pbm24)
            m_pbm24 = new UCHAR[((m_fbSize.cx*3+3) & ~3)*m_fbSize.cy];
        ASSERT(m_pbm24);
        UCHAR* rptr = m_pFB[RED]->getPointer(0,0);
        UCHAR* gptr = m_pFB[GREEN]->getPointer(0,0);
        UCHAR* bptr = m_pFB[BLUE]->getPointer(0,0);
        UCHAR* sptr = m_pbm24;
        if (m_dGamma == 1.0)
        {
            for (int i = 0; i < m_fbSize.cx*m_fbSize.cy; i++, rptr++, gptr++, bptr++)
            {
                // Assumes 24bit display ie. B R G format
                *sptr++ = *bptr; 
                *sptr++ = *gptr; 
                *sptr++ = *rptr;
            }
        }
        else
        {
            UCHAR* wLUT = new UCHAR[256];
            for (int i = 0; i < 256; i++)
            {
                int val = (int) (255.0 * pow((double) i / 255.0,1.0/m_dGamma) + 0.5);
                if (val > 255)
                    val = 255;
                wLUT[i] = UCHAR(val);
            }
            for (i = 0; i < m_fbSize.cx*m_fbSize.cy; i++, rptr++, gptr++, bptr++)
            {
                // Assumes 16bit display ie. 5R:6G:5B format
                *sptr++ = wLUT[*bptr]; 
                *sptr++ = wLUT[*gptr]; 
                *sptr++ = wLUT[*rptr];
            }
            delete [] wLUT;
        }
        return((WORD*)m_pbm24);
    }
    // 
    if (nBitsPerPixel == 16)
    {
        if(NULL == m_pbm16)
            m_pbm16= new WORD[((m_fbSize.cx*2+3) & ~3)*m_fbSize.cy];
        ASSERT(NULL != m_pbm16);

        UCHAR* rptr = m_pFB[RED]->getPointer(0,0);
        UCHAR* gptr = m_pFB[GREEN]->getPointer(0,0);
        UCHAR* bptr = m_pFB[BLUE]->getPointer(0,0);
        WORD* sptr = m_pbm16;
        if (m_dGamma == 1.0)
        {
            for (int i = 0; i < m_fbSize.cx*m_fbSize.cy; i++, rptr++, gptr++, bptr++)
            {
                // Assumes 16bit display ie. 5R:6G:5B format
                *sptr++ = (WORD) ((((WORD)*bptr>>3)&0x001F) | 
                        (((WORD)*gptr<<3)&0x07E0) | 
                        (((WORD)*rptr<<8)&0xF800));
            }
        }
        else
        {
            WORD* wLUT = new WORD[256];
            for (int i = 0; i < 256; i++)
            {
                wLUT[i] = (WORD) (255.0 * pow((double) i / 255.0,1.0/m_dGamma) + 0.5);
                if (wLUT[i] > 255)
                    wLUT[i] = 255;
            }
            for (i = 0; i < m_fbSize.cx*m_fbSize.cy; i++, rptr++, gptr++, bptr++)
            {
                // Assumes 16bit display ie. 5R:6G:5B format
                *sptr++ = (WORD) (((wLUT[*bptr]>>3)&0x001F) | 
                        ((wLUT[*gptr]<<3)&0x07E0) | 
                        ((wLUT[*rptr]<<8)&0xF800));
            }
            delete [] wLUT;
        }
        return(m_pbm16);
    }
    // Bits per pixel must be wrong
    return 0;
}

bool CImDisplay::CheckDisplaySize(CSize Imsize)
{
    if (Imsize != m_Imsize)     // resize the buffer
    {
        m_Imsize = Imsize;
        // check the display capabilities
        CDC* pDC;
        pDC = m_pWnd->GetDC();
        //Get the display capabilities
        m_nBitPlanes = pDC->GetDeviceCaps( PLANES );    //Usually 1
        m_nBitsPerPixel = pDC->GetDeviceCaps( BITSPIXEL );//Usually number of colours
        m_pWnd->ReleaseDC(pDC);
        if( m_nBitsPerPixel != 16 && m_nBitsPerPixel != 24 )
        {
            ::MessageBox(::GetActiveWindow(),"Video mode is not compatable with\noutput resolution. Change Screen/Settings to 16 or 24 bits.","Unable to Display image", MB_ICONSTOP);
            return false;
        }
        if (m_pBuf != 0)
            delete [] m_pBuf;
        m_pBuf = new UCHAR[m_Imsize.cx * m_Imsize.cy * m_nBitsPerPixel / 8];
        if( !m_Map.CreateBitmap( m_Imsize.cx, m_Imsize.cy, m_nBitPlanes, m_nBitsPerPixel, m_pBuf ) )
            ::MessageBox(::GetActiveWindow(),"Can not create bitmap","Unable to Display image", MB_ICONSTOP);
    }
    return true;
}

void CImDisplay::Display(CFBuffer24* Image, CRect subRect)
{
    CheckWindowPointer();
    // Build the bastard bitmap
    CSize imsize = Image->getBufferSize();
    CheckDisplaySize(imsize);

    // Write the Data to the Bit map
    // Copies the image from our 24bit buffer into a bitmap and returns the pointer to the bitmap
    DWORD dRet = m_Map.SetBitmapBits(imsize.cx*imsize.cy*m_nBitsPerPixel/8, Image->getBitmap(m_nBitsPerPixel) );
    //Draw the bastard thing
    CDC* pDC;
    pDC = m_pWnd->GetDC();
    CDC MemDC;
    MemDC.CreateCompatibleDC( pDC );
    CBitmap *pOldBitmap  = MemDC.SelectObject( &m_Map );
    pDC->SetStretchBltMode(COLORONCOLOR);   // this gets the colours looking correct
    pDC->StretchBlt(  m_Location.TopLeft().x, m_Location.TopLeft().y, 
                            m_Location.Width(), m_Location.Height(),
                            &MemDC, imSubRect.left, imSubRect.top, 
                            imSubRect.Width(), imSubRect.Height(), SRCCOPY  );
    MemDC.SelectObject( pOldBitmap );               //Release the object
    m_pWnd->ReleaseDC(pDC);
}

我在 Windows 10 上以兼容的 16 位颜色运行程序。这样它才能真正运行,否则运行良好。

当我使用 CreateCompatibleBitmap() 而不是 CreateBitmap() 时,我已经能够在 Windows 10 上显示一些东西,但是颜色都是乱码。在 Windows 7 上,它们很好。也许这是一个线索,但我无法弄清楚它的含义。

【问题讨论】:

  • 如果你关闭那个 16 位颜色并让它运行原生会发生什么?
  • 没有该设置软件将无法运行。它检查窗口的颜色深度的属性,然后如果它不是 16 位或 24 位颜色则终止。它对这些缓冲区进行了大量的图像处理,如果我能提供帮助,我宁愿不改变这一切。
  • 我找到了这段代码:link,但它使用了某种 RGBQUAD 对象,我不确定是否可以创建。如果有某种方法,那么它可能会起作用
  • 那么为什么不修复它让它以原生颜色深度运行呢?
  • 把条件改成if( m_nBitsPerPixel != 16 &amp;&amp; m_nBitsPerPixel != 24 &amp;&amp; m_nBitsPerPixel != 32 ) 剩下的还是错。在您的代码中的某处,您可能有与CFBuffer24 相关的GetDIBitsGetBitmapBits,请显示该函数。

标签: c++ winapi bitmap mfc


【解决方案1】:

如果宽度不是 4 的倍数,您的代码可能会错误计算位图所需的空间。它应该是 required_size = ( (width * bits_per_pixel / 8 + 3) &amp; ~3 ) * height

如果CFBuffer24 正确处理步幅(每行字节数),您可以直接将 blit 拉伸到设备上下文:

void CImDisplay::Display(CFBuffer24* Image, CRect subRect)
{
    CheckWindowPointer();

    CDC* pDC = m_pWnd->GetDC();

    m_nBitPlanes = pDC->GetDeviceCaps( PLANES );    //Usually 1
    m_nBitsPerPixel = pDC->GetDeviceCaps( BITSPIXEL );
    if( m_nBitsPerPixel != 16 && m_nBitsPerPixel != 24 && m_nBitsPerPixel != 32 )
    {
        ::MessageBox(::GetActiveWindow(),"Video mode is not compatable with\noutput resolution. Change Screen/Settings to 16 or 24 bits.","Unable to Display image", MB_ICONSTOP);
        return;
    }

    if ( m_nBitsPerPixel == 32 )
        m_nBitsPerPixel = 24;

    m_Imsize = Image->getBufferSize();

    // Bitmap rows are aligned to multiplies of 4 bytes
    int stride = (m_Imsize.cx * m_nBitsPerPixel / 8 + 3) & ~3;

    BITMAPINFO  bi =
    {
        sizeof( BITMAPINFOHEADER ),
        stride,
        -m_Imsize.cy,    // If bitmap looks upside down remove minus
        1,
        m_nBitsPerPixel,
        BI_RGB,
        0,
        0,
        0,
        0,
        0
    };

    pDC->SetStretchBltMode(COLORONCOLOR);

    ::StretchDIBits(
            pDC,
            m_Location.TopLeft().x,
            m_Location.TopLeft().y,
            m_Location.Width(),
            m_Location.Height(),
            // !!!! I don't know what imSubRect is. Is it related to subRect?
            imSubRect.left,
            imSubRect.top,
            imSubRect.Width(),
            imSubRect.Height(),
            Image->getBitmap(m_nBitsPerPixel),
            &bi,
            DIB_RGB_COLORS,
            SRCCOPY );

    m_pWnd->ReleaseDC(pDC);
}

上面的代码将 24 位到 32 位的转换留给了StretchDIBits。性能方面应该没问题。或者,您可以在 CFBuffer24 中实现 32 位大小写。

【讨论】:

  • 我将此代码放入函数中,结果是乱码。我已经用负责从缓冲区创建位图的代码更新了我的问题。里面肯定有什么地方不太对劲。
  • 好消息是它能够在Win10上显示相同的输出,所以我认为这是正确的轨道!
  • 谢谢!我已经能够让它工作了。问题是我需要通过并消除所有 16/24 位颜色限制,然后以 32 位颜色运行。使用您的代码,它能够显示!
猜你喜欢
  • 1970-01-01
  • 2013-12-22
  • 1970-01-01
  • 2011-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-29
  • 1970-01-01
相关资源
最近更新 更多