为什么我的Stretchblt图像丢失了颜色?

发布于 2025-02-09 03:33:33 字数 6733 浏览 2 评论 0原文

我在正确填充clistctrl的缩略图时遇到了一些困难。

在我的cdialog的右侧,我有一个静态控件,我在这样的白色画布上渲染图像:

void CCenterCursorOnScreenDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    if (nIDCtl == IDC_STATIC_MONITOR && !m_imgPreview.IsNull())
    {
        // Set the mode
        SetStretchBltMode(lpDrawItemStruct->hDC, HALFTONE);

        // Wipe the canvas
        FillRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));

        // Get canvas rectangle
        const CRect rectCanvas(lpDrawItemStruct->rcItem);

        // Calculate ratio factors
        const float nRatioImage = m_imgPreview.GetWidth() / static_cast<float>(m_imgPreview.GetHeight());
        const float nRatioCanvas = rectCanvas.Width() / static_cast<float>(rectCanvas.Height());

        // Calculate new rectangle size
        // Account for portrait images (negative values)
        CRect rectDraw = rectCanvas;
        if (nRatioImage > nRatioCanvas)
            rectDraw.SetRect(0, 0, rectDraw.right, static_cast<int>(rectDraw.right / nRatioImage));
        else if (nRatioImage < nRatioCanvas)
            rectDraw.SetRect(0, 0, static_cast<int>((rectDraw.bottom * nRatioImage)), rectDraw.bottom);

        // Add a margin
        rectDraw.DeflateRect(5, 5);

        // Move to center
        const CSize ptOffset = rectCanvas.CenterPoint() - rectDraw.CenterPoint();
        rectDraw.OffsetRect(ptOffset);

        // Add a black frame
        FrameRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));

        // Draw
        m_imgPreview.Draw(lpDrawItemStruct->hDC, rectDraw);

        return;
    }

    CDialogEx::OnDrawItem(nIDCtl, lpDrawItemStruct);
}

上面的功能非常好:

“在此处输入映像说明”

但是我对<<代码> clistctrl 图像的版本。例如,如您所见,我正在失去着色。

我的cimagelist是这样创建的:

m_ImageListThumb.Create(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, ILC_COLOR32, 0, 1);
m_ListThumbnail.SetImageList(&m_ImageListThumb, LVSIL_NORMAL);

创建所有缩略图

void CCenterCursorOnScreenDlg::DrawThumbnails()
{
    int monitorIndex = 0;

    m_ListThumbnail.SetRedraw(FALSE);

    for (auto& strMonitor : m_monitors.strMonitorNames)
    {
        CImage img;
        CreateMonitorThumbnail(monitorIndex, img, true);

        CBitmap* pImage = new CBitmap();
        pImage->Attach((HBITMAP)img);
        m_ImageListThumb.Add(pImage, nullptr);

        CString strMonitorDesc = m_monitors.strMonitorNames.at(monitorIndex);

        strMonitorDesc.AppendFormat(L" (Screen %d)", monitorIndex + 1);

        m_ListThumbnail.InsertItem(monitorIndex, strMonitorDesc, monitorIndex);


        monitorIndex++;

        delete pImage;
    }

    m_ListThumbnail.SetRedraw(TRUE);
}

然后,我通过调用drawthumbnails() in oninitdialogcreatsemonemoniorThumbnibnail函数:

BOOL CCenterCursorOnScreenDlg::CreateMonitorThumbnail(const int iMonitorIndex, CImage &rImage, bool bSmall)
{
    const CRect rcCapture = m_monitors.rcMonitors.at(iMonitorIndex);

    // destroy the currently contained bitmap to create a new one
    rImage.Destroy();

    auto nWidth = rcCapture.Width();
    auto nHeight = rcCapture.Height();
    if (bSmall)
    {
        nWidth = THUMBNAIL_WIDTH;
        nHeight = THUMBNAIL_HEIGHT;
    }

    // create bitmap and attach it to this object 
    if (!rImage.Create(nWidth, nHeight, 32, 0))
    {
        AfxMessageBox(L"Cannot create image!", MB_ICONERROR);
        return FALSE;
    }


    // create virtual screen DC
    CDC dcScreen;
    dcScreen.CreateDC(_T("DISPLAY"), nullptr, nullptr, nullptr);

    // copy the contents from the virtual screen DC 

    BOOL bRet = FALSE;
    if (bSmall)
    {
        CRect rt(0, 0, nWidth, nHeight);

        //::FillRect(rImage.GetDC(), rt, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
        bRet = ::StretchBlt(rImage.GetDC(), 0, 0, 
            nWidth, 
            nHeight, 
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, 
            rcCapture.Width(), 
            rcCapture.Height(), SRCCOPY | CAPTUREBLT);

    }
    else
    {
        bRet = ::BitBlt(rImage.GetDC(), 0, 0, 
            rcCapture.Width(), 
            rcCapture.Height(),
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, SRCCOPY | CAPTUREBLT);
    }

    // do cleanup and return
    dcScreen.DeleteDC();
    rImage.ReleaseDC();

    return bRet;
}

理想情况下,我希望拥有与右侧完全相同的视觉图像,但显然调整了大小。我该如何解决?


我简化了从cimage转换为cbitmap,但这没有区别:

void CCenterCursorOnScreenDlg::DrawThumbnails()
{
    int monitorIndex = 0;

    // Stop redrawing the CListCtrl
    m_ListThumbnail.SetRedraw(FALSE);

    // Loop monitor info
    for (auto& strMonitor : m_monitors.strMonitorNames)
    {
        // Create the thumbnail image
        CImage monitorThumbnail;
        CreateMonitorThumbnail(monitorIndex, monitorThumbnail, true);

        // Convert it to a CBitmap
        CBitmap* pMonitorThumbnailBitmap = CBitmap::FromHandle(monitorThumbnail);

        // Add the CBitmap to the CImageList
        m_ImageListThumb.Add(pMonitorThumbnailBitmap, nullptr);

        // Build the caption description
        CString strMonitorDesc = m_monitors.strMonitorNames.at(monitorIndex);
        strMonitorDesc.AppendFormat(L" (Screen %d)", monitorIndex + 1);

        // Add the item to the CListCtrl
        m_ListThumbnail.InsertItem(monitorIndex, strMonitorDesc, monitorIndex);

        monitorIndex++;
    }

    // Start redrawiung the CListCtrl again
    m_ListThumbnail.SetRedraw(TRUE);
}

如果我将代码更改为最后一个参数false,以便它使用原始捕获的图像而无需缩放:

”在此处输入图像描述

颜色是上帝,所以当我这样做时,

if (bSmall)
{
    CRect rt(0, 0, nWidth, nHeight);

    //::FillRect(rImage.GetDC(), rt, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
    bRet = ::StretchBlt(rImage.GetDC(), 0, 0, 
        nWidth, 
        nHeight, 
        dcScreen.m_hDC, 
        rcCapture.left, 
        rcCapture.top, 
        rcCapture.Width(), 
        rcCapture.Height(), SRCCOPY | CAPTUREBLT);

}

它会弄乱。

I am having some difficulties in correctly populating a CListCtrl with thumbnails of monitor displays.

On the right of my CDialog I have a static control and I render the image on a white canvas like this:

void CCenterCursorOnScreenDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    if (nIDCtl == IDC_STATIC_MONITOR && !m_imgPreview.IsNull())
    {
        // Set the mode
        SetStretchBltMode(lpDrawItemStruct->hDC, HALFTONE);

        // Wipe the canvas
        FillRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));

        // Get canvas rectangle
        const CRect rectCanvas(lpDrawItemStruct->rcItem);

        // Calculate ratio factors
        const float nRatioImage = m_imgPreview.GetWidth() / static_cast<float>(m_imgPreview.GetHeight());
        const float nRatioCanvas = rectCanvas.Width() / static_cast<float>(rectCanvas.Height());

        // Calculate new rectangle size
        // Account for portrait images (negative values)
        CRect rectDraw = rectCanvas;
        if (nRatioImage > nRatioCanvas)
            rectDraw.SetRect(0, 0, rectDraw.right, static_cast<int>(rectDraw.right / nRatioImage));
        else if (nRatioImage < nRatioCanvas)
            rectDraw.SetRect(0, 0, static_cast<int>((rectDraw.bottom * nRatioImage)), rectDraw.bottom);

        // Add a margin
        rectDraw.DeflateRect(5, 5);

        // Move to center
        const CSize ptOffset = rectCanvas.CenterPoint() - rectDraw.CenterPoint();
        rectDraw.OffsetRect(ptOffset);

        // Add a black frame
        FrameRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));

        // Draw
        m_imgPreview.Draw(lpDrawItemStruct->hDC, rectDraw);

        return;
    }

    CDialogEx::OnDrawItem(nIDCtl, lpDrawItemStruct);
}

The above works beautifully:

enter image description here

But I have problems with the CListCtrl versions of the images. For instance, I am losing the colouring as you can see.

My CImageList is created like this:

m_ImageListThumb.Create(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, ILC_COLOR32, 0, 1);
m_ListThumbnail.SetImageList(&m_ImageListThumb, LVSIL_NORMAL);

I then create all the thumbnails by calling DrawThumbnails() in OnInitDialog:

void CCenterCursorOnScreenDlg::DrawThumbnails()
{
    int monitorIndex = 0;

    m_ListThumbnail.SetRedraw(FALSE);

    for (auto& strMonitor : m_monitors.strMonitorNames)
    {
        CImage img;
        CreateMonitorThumbnail(monitorIndex, img, true);

        CBitmap* pImage = new CBitmap();
        pImage->Attach((HBITMAP)img);
        m_ImageListThumb.Add(pImage, nullptr);

        CString strMonitorDesc = m_monitors.strMonitorNames.at(monitorIndex);

        strMonitorDesc.AppendFormat(L" (Screen %d)", monitorIndex + 1);

        m_ListThumbnail.InsertItem(monitorIndex, strMonitorDesc, monitorIndex);


        monitorIndex++;

        delete pImage;
    }

    m_ListThumbnail.SetRedraw(TRUE);
}

The CreateMonitorThumbnail function:

BOOL CCenterCursorOnScreenDlg::CreateMonitorThumbnail(const int iMonitorIndex, CImage &rImage, bool bSmall)
{
    const CRect rcCapture = m_monitors.rcMonitors.at(iMonitorIndex);

    // destroy the currently contained bitmap to create a new one
    rImage.Destroy();

    auto nWidth = rcCapture.Width();
    auto nHeight = rcCapture.Height();
    if (bSmall)
    {
        nWidth = THUMBNAIL_WIDTH;
        nHeight = THUMBNAIL_HEIGHT;
    }

    // create bitmap and attach it to this object 
    if (!rImage.Create(nWidth, nHeight, 32, 0))
    {
        AfxMessageBox(L"Cannot create image!", MB_ICONERROR);
        return FALSE;
    }


    // create virtual screen DC
    CDC dcScreen;
    dcScreen.CreateDC(_T("DISPLAY"), nullptr, nullptr, nullptr);

    // copy the contents from the virtual screen DC 

    BOOL bRet = FALSE;
    if (bSmall)
    {
        CRect rt(0, 0, nWidth, nHeight);

        //::FillRect(rImage.GetDC(), rt, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
        bRet = ::StretchBlt(rImage.GetDC(), 0, 0, 
            nWidth, 
            nHeight, 
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, 
            rcCapture.Width(), 
            rcCapture.Height(), SRCCOPY | CAPTUREBLT);

    }
    else
    {
        bRet = ::BitBlt(rImage.GetDC(), 0, 0, 
            rcCapture.Width(), 
            rcCapture.Height(),
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, SRCCOPY | CAPTUREBLT);
    }

    // do cleanup and return
    dcScreen.DeleteDC();
    rImage.ReleaseDC();

    return bRet;
}

Ideally I want to have exactly the same kind of visual image as on the right, but obviously resized down. How do I fix this?


I simplified the converting from CImage to CBitmap but it made no difference:

void CCenterCursorOnScreenDlg::DrawThumbnails()
{
    int monitorIndex = 0;

    // Stop redrawing the CListCtrl
    m_ListThumbnail.SetRedraw(FALSE);

    // Loop monitor info
    for (auto& strMonitor : m_monitors.strMonitorNames)
    {
        // Create the thumbnail image
        CImage monitorThumbnail;
        CreateMonitorThumbnail(monitorIndex, monitorThumbnail, true);

        // Convert it to a CBitmap
        CBitmap* pMonitorThumbnailBitmap = CBitmap::FromHandle(monitorThumbnail);

        // Add the CBitmap to the CImageList
        m_ImageListThumb.Add(pMonitorThumbnailBitmap, nullptr);

        // Build the caption description
        CString strMonitorDesc = m_monitors.strMonitorNames.at(monitorIndex);
        strMonitorDesc.AppendFormat(L" (Screen %d)", monitorIndex + 1);

        // Add the item to the CListCtrl
        m_ListThumbnail.InsertItem(monitorIndex, strMonitorDesc, monitorIndex);

        monitorIndex++;
    }

    // Start redrawiung the CListCtrl again
    m_ListThumbnail.SetRedraw(TRUE);
}

If I change my code to pass false for the last parameter, so that it uses the original captured images without scaling down:

enter image description here

The colours are god there, so it is when I do:

if (bSmall)
{
    CRect rt(0, 0, nWidth, nHeight);

    //::FillRect(rImage.GetDC(), rt, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
    bRet = ::StretchBlt(rImage.GetDC(), 0, 0, 
        nWidth, 
        nHeight, 
        dcScreen.m_hDC, 
        rcCapture.left, 
        rcCapture.top, 
        rcCapture.Width(), 
        rcCapture.Height(), SRCCOPY | CAPTUREBLT);

}

that it messes up.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦里泪两行 2025-02-16 03:33:33

我的问题与OnDrawItem没有任何关系。我简单地将其包括在内,以指示右侧的图像是如何渲染的。我认为它可能会作为背景信息有所帮助。但这可能使这个问题感到困惑,从长远来看,我可能会把它取出!

基于评论,提醒我setStretchbltmode,它在creatementerthumbnail中缺少。因此,我现在拥有此功能:

BOOL CCenterCursorOnScreenDlg::CreateMonitorThumbnail(const int iMonitorIndex, CImage &rImage, bool bResizeAsThumbnail)
{
    const CRect rcCapture = m_monitors.rcMonitors.at(iMonitorIndex);

    // Destroy the currently contained bitmap to create a new one
    rImage.Destroy();

    // Massage the dimensions as we want a thumbnail
    auto nWidth = rcCapture.Width();
    auto nHeight = rcCapture.Height();
    if (bResizeAsThumbnail)
    {
        nWidth = m_iThumbnailWidth;

        auto dRatio = rcCapture.Width() / nWidth;
        //nHeight = m_iThumbnailHeight;
        nHeight = static_cast<int>(rcCapture.Height() / dRatio);

        if (nHeight > m_iThumbnailHeight)
        {
            AfxMessageBox(L"Need to investigate!");
        }
    }

    // Create bitmap and attach it to this object 
    if (!rImage.Create(nWidth, nHeight, 32, 0))
    {
        AfxMessageBox(L"Cannot create image!", MB_ICONERROR);
        return FALSE;
    }


    // Create virtual screen DC
    CDC dcScreen;
    dcScreen.CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);

    // Copy (or resize) the contents from the virtual screen DC 

    BOOL bRet = FALSE;
    auto dcImage = rImage.GetDC();
    if (bResizeAsThumbnail)
    {
        // Set the mode first!
        SetStretchBltMode(dcImage, COLORONCOLOR);

        CPen penBlack;
        penBlack.CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
        ::Rectangle(dcImage, 0, 0, m_iThumbnailWidth, m_iThumbnailHeight);

        int iTop = (m_iThumbnailHeight - nHeight) / 2;

        // Copy (and resize)
        bRet = ::StretchBlt(dcImage, 0, iTop,
            nWidth, 
            nHeight, 
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, 
            rcCapture.Width(), 
            rcCapture.Height(), SRCCOPY | CAPTUREBLT);
    }
    else
    {
        // Copy
        bRet = ::BitBlt(dcImage, 0, 0, 
            rcCapture.Width(), 
            rcCapture.Height(),
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, SRCCOPY | CAPTUREBLT);
    }

    // Do cleanup and return
    dcScreen.DeleteDC();
    rImage.ReleaseDC();

    return bRet;
}

那是获得带有正确颜色的缩略图的关键:

“在此处输入图像说明”

My issue did not have anything to do with OnDrawItem. I simply included that to indicate how the image on the right was being rendered. I thought it may helped as background information. But it has probably confused the question and I may take it out in the long run!

Based on the comments I was reminded about SetStretchBltMode which was missing from CreateMonitorThumbnail. So, I now have this function:

BOOL CCenterCursorOnScreenDlg::CreateMonitorThumbnail(const int iMonitorIndex, CImage &rImage, bool bResizeAsThumbnail)
{
    const CRect rcCapture = m_monitors.rcMonitors.at(iMonitorIndex);

    // Destroy the currently contained bitmap to create a new one
    rImage.Destroy();

    // Massage the dimensions as we want a thumbnail
    auto nWidth = rcCapture.Width();
    auto nHeight = rcCapture.Height();
    if (bResizeAsThumbnail)
    {
        nWidth = m_iThumbnailWidth;

        auto dRatio = rcCapture.Width() / nWidth;
        //nHeight = m_iThumbnailHeight;
        nHeight = static_cast<int>(rcCapture.Height() / dRatio);

        if (nHeight > m_iThumbnailHeight)
        {
            AfxMessageBox(L"Need to investigate!");
        }
    }

    // Create bitmap and attach it to this object 
    if (!rImage.Create(nWidth, nHeight, 32, 0))
    {
        AfxMessageBox(L"Cannot create image!", MB_ICONERROR);
        return FALSE;
    }


    // Create virtual screen DC
    CDC dcScreen;
    dcScreen.CreateDC(L"DISPLAY", nullptr, nullptr, nullptr);

    // Copy (or resize) the contents from the virtual screen DC 

    BOOL bRet = FALSE;
    auto dcImage = rImage.GetDC();
    if (bResizeAsThumbnail)
    {
        // Set the mode first!
        SetStretchBltMode(dcImage, COLORONCOLOR);

        CPen penBlack;
        penBlack.CreatePen(PS_SOLID, 3, RGB(0, 0, 0));
        ::Rectangle(dcImage, 0, 0, m_iThumbnailWidth, m_iThumbnailHeight);

        int iTop = (m_iThumbnailHeight - nHeight) / 2;

        // Copy (and resize)
        bRet = ::StretchBlt(dcImage, 0, iTop,
            nWidth, 
            nHeight, 
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, 
            rcCapture.Width(), 
            rcCapture.Height(), SRCCOPY | CAPTUREBLT);
    }
    else
    {
        // Copy
        bRet = ::BitBlt(dcImage, 0, 0, 
            rcCapture.Width(), 
            rcCapture.Height(),
            dcScreen.m_hDC, 
            rcCapture.left, 
            rcCapture.top, SRCCOPY | CAPTUREBLT);
    }

    // Do cleanup and return
    dcScreen.DeleteDC();
    rImage.ReleaseDC();

    return bRet;
}

That was the key to getting the thumbnail showing with the right colours:

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文