cxnet 发表于 2024-11-19 10:43:29

图像打印不全

请帮我看一下我的这个代码,我是将一个1000mm宽,2000mm高,1200dpi的二值图通过按图像宽度和100像素高度分块加载后依次将分块图像绘制到打印机DC上进行打印,但始终只能打印到300多mm就是空白区域了,页面大小还是正常,就是后面就全是空白,没有内容,不知道为什么,而且我用这种方式打印出来的打印机缓存文件(*.spl)达到3.99G,而同一个图通过coreldraw加载打印出来的缓存文件只有300MB左右,不知道是什么原因


void CMy001Dlg::OnBnClickedOk()
{
        cv::Mat image = cv::imread("605x780_P1_GRAY.tif", cv::IMREAD_UNCHANGED);
        if (image.empty()) {
                AfxMessageBox(_T("无法加载图像!"));
                return;
        }

        int width = image.cols;
        int height = image.rows;
        int blockSize = 100;
        HANDLE hPrinter = NULL;
        BYTE* pPrinterInfo = NULL;
        PRINTER_INFO_2* pInfo2 = NULL;
        DEVMODE* pDevMode = NULL;
        CDC printerDC;
        int dpi = 1200;
        CString strPrintDevice = _T("HP Designjet T730");

        cv::Mat result = cv::Mat::zeros(height, width, image.type());//创建拼接大图尺寸

        for (int y = 0; y < height; y += blockSize) {//按blackheight指定调试加载图像
                for (int x = 0; x < width; x += image.cols) {//按图像宽度分割加载
                        int currentBlockHeight = std::min(blockSize, image.rows - y);

                        cv::Rect roi(x, y, std::min(image.cols, width - x), std::min(blockSize, height - y));//定义切割矩形块区域
                        cv::Mat block = image(roi);//按roi区域分割图像

                        cv::Mat bgr;
                        cv::cvtColor(block , bgr, cv::COLOR_BGR2BGRA);// Make sure type is correct

                        // Step 2: Create bitmap info
                        BITMAPINFO bitmapInfo = { 0 };
                        bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                        bitmapInfo.bmiHeader.biWidth = bgr.cols;
                        bitmapInfo.bmiHeader.biHeight = -bgr.rows;// Flip image vertically
                        bitmapInfo.bmiHeader.biPlanes = 1;
                        bitmapInfo.bmiHeader.biBitCount = 32;
                        bitmapInfo.bmiHeader.biCompression = BI_RGB;



                        // 打开打印机
                        LPWSTR ptName = strPrintDevice.GetBuffer();

                        if (!OpenPrinter(ptName, &hPrinter, NULL)) {
                                AfxMessageBox(_T("打印机端口打开失败!"));
                                return;
                        }


                        // 获取打印机信息
                        DWORD needed;
                        GetPrinter(hPrinter, 2, NULL, 0, &needed);
                        pPrinterInfo = new BYTE;
                        if (!GetPrinter(hPrinter, 2, pPrinterInfo, needed, &needed)) {
                                AfxMessageBox(_T("打印机信息获取失败!"));
                                delete[] pPrinterInfo;
                                ClosePrinter(hPrinter);
                                return;
                        }

                        pInfo2 = (PRINTER_INFO_2*)pPrinterInfo;
                        int posX = 30; // 偏移位置
                        int posY = 30;

                        // 设置 DEVMODE
                        pDevMode = (DEVMODE*)GlobalLock(pInfo2->pDevMode);

                        if (pDevMode) {
                                // 设置自定义纸张
                                pDevMode->dmFields |= DM_PAPERWIDTH | DM_PAPERLENGTH | DM_PRINTQUALITY | DM_PAPERSIZE;
                                pDevMode->dmPaperSize = DMPAPER_USER;
                                pDevMode->dmPaperWidth = width / dpi * 25.4 * 10;// 1/10 mm
                                pDevMode->dmPaperLength = height / dpi * 25.4 * 10; // 1/10 mm LcinkRIP不能超过32740
                                pDevMode->dmPrintQuality = dpi; // DPI

                                // 释放 DEVMODE
                                GlobalUnlock(pInfo2->pDevMode);
                        }

                        // 创建和启动打印机上下文
                        if (y == 0) {
                                // 准备 DOCINFO
                                DOCINFO docInfo;
                                memset(&docInfo, 0, sizeof(DOCINFO));
                                docInfo.cbSize = sizeof(DOCINFO);
                                docInfo.lpszDocName = _T("test");
                                docInfo.lpszOutput = NULL;

                                printerDC.Attach(CreateDC(NULL, pInfo2->pPrinterName, NULL, pDevMode));

                                printerDC.StartDoc(&docInfo);
                                printerDC.StartPage();
                                SetStretchBltMode(printerDC, HALFTONE);
                                ::StretchDIBits(printerDC, x + posX, y + posY, bgr.cols, bgr.rows, 0, 0, bgr.cols, bgr.rows,
                                        bgr.data, &bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
                        }
                        else {
                                SetStretchBltMode(printerDC, HALFTONE);
                                ::StretchDIBits(printerDC, x + posX, y + posY, bgr.cols, bgr.rows, 0, 0, bgr.cols, bgr.rows,
                                        bgr.data, &bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
                        }
                }
        }

        printerDC.EndPage();
        printerDC.EndDoc();
        // 释放资源
        delete[] pPrinterInfo;
        printerDC.DeleteDC();
        ClosePrinter(hPrinter);
}

FishC 发表于 2024-11-19 10:48:44

你的问题主要涉及打印过程中图像块处理和内存使用的问题。以下是一些可能的解决方案和改进建议:

1. 检查内存泄漏:
- 请确保在分配内存空间后及时释放。确保所有`new`和`GlobalLock`使用的对象都在适当的地方释放。你应该对`pPrinterInfo`申请的内存使用`delete[]`来释放。

2. 错误处理:
- 在代码中,每次调用`CreateDC`创建`printerDC`的时候,请检查返回值是否为空。如果`CreateDC`失败,`printerDC`将不被更新且可能导致后续操作失败。

3. 检查图像数据处理:
- 重新检查代码中`cvtColor`步骤,因为你使用的是二值图像,可能不需要`BGR到BGRA`的转换。确保图像像素格式与打印机的要求一致。

4. 调整文档和页面处理:
- `StartDoc`和`EndDoc`应该在整个图像处理之前和之后仅调用一次,而不是在每个块处理时。
- `StartPage`和`EndPage`应该是跨越每一个图像块的绘制周期,因此需要检查循环结构正确性。

5. 修改缓存文件大小:
- 大缓存文件通常表明传送给打印机的数据量过大,可能 due于图像格式或分辨率的缘故。尝试降低DPI(1200 DPI可能过高)来检查是否能缩小生成的`*.spl`文件大小。

6. 设置仅一次:
- 打印机的`DEVMODE`设置应在循环外完成一次,而不是在每个块循环中设置。

根据这些建议,对代码进行修改和测试,看问题是否得到解决。调整设置,控制内存使用并减少不必要的处理步骤,可以有效提高效率和效果。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-19 10:50:46

FishC 发表于 2024-11-19 10:48
你的问题主要涉及打印过程中图像块处理和内存使用的问题。以下是一些可能的解决方案和改进建议:

1. 检 ...

请帮我在我的代码基础上进行修改一下谢谢

FishC 发表于 2024-11-19 11:02:50

cxnet 发表于 2024-11-19 10:50
请帮我在我的代码基础上进行修改一下谢谢

当然可以。下面是一些针对你代码的改进建议:

void CMy001Dlg::OnBnClickedOk()
{
        cv::Mat image = cv::imread("605x780_P1_GRAY.tif", cv::IMREAD_UNCHANGED);
        if (image.empty()) {
                AfxMessageBox(_T("无法加载图像!"));
                return;
        }

        int width = image.cols;
        int height = image.rows;
        int blockSize = 100;
        HANDLE hPrinter = NULL;
        BYTE* pPrinterInfo = NULL;
        PRINTER_INFO_2* pInfo2 = NULL;
        DEVMODE* pDevMode = NULL;
        CDC printerDC;
        int dpi = 1200;
        CString strPrintDevice = _T("HP Designjet T730");

        // 打开打印机
        LPWSTR ptName = strPrintDevice.GetBuffer();
        if (!OpenPrinter(ptName, &hPrinter, NULL)) {
                AfxMessageBox(_T("打印机端口打开失败!"));
                return;
        }

        // 获取打印机信息
        DWORD needed;
        GetPrinter(hPrinter, 2, NULL, 0, &needed);
        pPrinterInfo = new BYTE;// 修正内存分配大小
        if (!GetPrinter(hPrinter, 2, pPrinterInfo, needed, &needed)) {
                AfxMessageBox(_T("打印机信息获取失败!"));
                delete[] pPrinterInfo;
                ClosePrinter(hPrinter);
                return;
        }

        pInfo2 = (PRINTER_INFO_2*)pPrinterInfo;
       
        // 设置 DEVMODE
        pDevMode = (DEVMODE*)GlobalLock(pInfo2->pDevMode);
        if (pDevMode) {
                pDevMode->dmFields |= DM_PAPERWIDTH | DM_PAPERLENGTH | DM_PRINTQUALITY | DM_PAPERSIZE;
                pDevMode->dmPaperSize = DMPAPER_USER;
                pDevMode->dmPaperWidth = width / dpi * 25.4 * 10;// 1/10 mm
                pDevMode->dmPaperLength = height / dpi * 25.4 * 10;
                pDevMode->dmPrintQuality = dpi;
                GlobalUnlock(pInfo2->pDevMode);
        }

        printerDC.Attach(CreateDC(NULL, pInfo2->pPrinterName, NULL, pDevMode));
        if (printerDC.m_hDC == NULL) {
                AfxMessageBox(_T("创建打印机DC失败!"));
                delete[] pPrinterInfo;
                ClosePrinter(hPrinter);
                return;
        }

        DOCINFO docInfo;
        memset(&docInfo, 0, sizeof(DOCINFO));
        docInfo.cbSize = sizeof(DOCINFO);
        docInfo.lpszDocName = _T("test");

        if (printerDC.StartDoc(&docInfo) < 0) {
                AfxMessageBox(_T("启动打印文档失败!"));
                delete[] pPrinterInfo;
                printerDC.DeleteDC();
                ClosePrinter(hPrinter);
                return;
        }

        for (int y = 0; y < height; y += blockSize) {
                printerDC.StartPage();
               
                int currentBlockHeight = std::min(blockSize, height - y);
                cv::Rect roi(0, y, width, currentBlockHeight);
                cv::Mat block = image(roi);
                cv::Mat bgr;
                cv::cvtColor(block, bgr, cv::COLOR_GRAY2BGRA);

                BITMAPINFO bitmapInfo = {0};
                bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                bitmapInfo.bmiHeader.biWidth = bgr.cols;
                bitmapInfo.bmiHeader.biHeight = -bgr.rows;
                bitmapInfo.bmiHeader.biPlanes = 1;
                bitmapInfo.bmiHeader.biBitCount = 32;
                bitmapInfo.bmiHeader.biCompression = BI_RGB;

                SetStretchBltMode(printerDC, HALFTONE);
                ::StretchDIBits(printerDC, 0, y, bgr.cols, bgr.rows, 0, 0, bgr.cols, bgr.rows,
                        bgr.data, &bitmapInfo, DIB_RGB_COLORS, SRCCOPY);

                printerDC.EndPage();
        }

        printerDC.EndDoc();

        delete[] pPrinterInfo;
        printerDC.DeleteDC();
        ClosePrinter(hPrinter);
}

主要的修改与建议:
1. 修正了`pPrinterInfo`的内存分配大小。
2. 在循环外部处理了打印机的`DEVMODE`设置。
3. 确保`StartDoc`和`EndDoc`只调用一次。
4. 改正了颜色空间转换步骤,使其适应灰度图像。
5. 确保了`CreateDC`的结果检查。
6. 使用`delete[]`释放内存。

请根据测试结果进一步调整与优化其他设置。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-19 11:32:33

FishC 发表于 2024-11-19 11:02
当然可以。下面是一些针对你代码的改进建议:




如果按你现在修改的代码打印出来分块图片就会打印成多个页面上,我是要将这些图片打印在一张纸上,所以StartDoc和StartPage只能是在y==0时才能执行一次,当y>0时就不能再执行它们,否则它就会重新创建新的页面,而EndPage和EndDoc也只能在所有分块图片都打印到一张纸上以后才能执行它,至于delete[] pPrinterInfo它也只能出现在EndPage和EndDoc之后,如果在分块处理过程后执行它就会出错,所以请帮我修改成将所有分块图片打印到同一个页面上,谢谢

FishC 发表于 2024-11-19 11:40:37

cxnet 发表于 2024-11-19 11:32
如果按你现在修改的代码打印出来分块图片就会打印成多 ...

了解了你的需求,下面是修改后的代码,确保所有分块图像都被打印在同一个页面上:

void CMy001Dlg::OnBnClickedOk()
{
        cv::Mat image = cv::imread("605x780_P1_GRAY.tif", cv::IMREAD_UNCHANGED);
        if (image.empty()) {
                AfxMessageBox(_T("无法加载图像!"));
                return;
        }

        int width = image.cols;
        int height = image.rows;
        int blockSize = 100;
        HANDLE hPrinter = NULL;
        BYTE* pPrinterInfo = NULL;
        PRINTER_INFO_2* pInfo2 = NULL;
        DEVMODE* pDevMode = NULL;
        CDC printerDC;
        int dpi = 1200;
        CString strPrintDevice = _T("HP Designjet T730");

        // 打开打印机
        LPWSTR ptName = strPrintDevice.GetBuffer();
        if (!OpenPrinter(ptName, &hPrinter, NULL)) {
                AfxMessageBox(_T("打印机端口打开失败!"));
                return;
        }

        // 获取打印机信息
        DWORD needed;
        GetPrinter(hPrinter, 2, NULL, 0, &needed);
        pPrinterInfo = new BYTE;// 修正内存分配大小
        if (!GetPrinter(hPrinter, 2, pPrinterInfo, needed, &needed)) {
                AfxMessageBox(_T("打印机信息获取失败!"));
                delete[] pPrinterInfo;// 确保删除动态分配的内存
                ClosePrinter(hPrinter);
                return;
        }

        pInfo2 = (PRINTER_INFO_2*)pPrinterInfo;
       
        // 设置 DEVMODE
        pDevMode = (DEVMODE*)GlobalLock(pInfo2->pDevMode);
        if (pDevMode) {
                pDevMode->dmFields |= DM_PAPERWIDTH | DM_PAPERLENGTH | DM_PRINTQUALITY | DM_PAPERSIZE;
                pDevMode->dmPaperSize = DMPAPER_USER;
                pDevMode->dmPaperWidth = width / dpi * 25.4 * 10;// 1/10 mm
                pDevMode->dmPaperLength = height / dpi * 25.4 * 10;
                pDevMode->dmPrintQuality = dpi;
                GlobalUnlock(pInfo2->pDevMode);
        }

        printerDC.Attach(CreateDC(NULL, pInfo2->pPrinterName, NULL, pDevMode));
        if (printerDC.m_hDC == NULL) {
                AfxMessageBox(_T("创建打印机DC失败!"));
                delete[] pPrinterInfo;
                ClosePrinter(hPrinter);
                return;
        }

        DOCINFO docInfo;
        memset(&docInfo, 0, sizeof(DOCINFO));
        docInfo.cbSize = sizeof(DOCINFO);
        docInfo.lpszDocName = _T("test");
        if (printerDC.StartDoc(&docInfo) < 0) {
                AfxMessageBox(_T("启动打印文档失败!"));
                delete[] pPrinterInfo;
                printerDC.DeleteDC();
                ClosePrinter(hPrinter);
                return;
        }

        printerDC.StartPage();// 在页循环外启动页面

        for (int y = 0; y < height; y += blockSize) {
                int currentBlockHeight = std::min(blockSize, height - y);
                cv::Rect roi(0, y, width, currentBlockHeight);
                cv::Mat block = image(roi);
                cv::Mat bgr;
                cv::cvtColor(block, bgr, cv::COLOR_GRAY2BGRA);

                BITMAPINFO bitmapInfo = {0};
                bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                bitmapInfo.bmiHeader.biWidth = bgr.cols;
                bitmapInfo.bmiHeader.biHeight = -bgr.rows;
                bitmapInfo.bmiHeader.biPlanes = 1;
                bitmapInfo.bmiHeader.biBitCount = 32;
                bitmapInfo.bmiHeader.biCompression = BI_RGB;

                SetStretchBltMode(printerDC, HALFTONE);
                ::StretchDIBits(printerDC, 0, y, bgr.cols, bgr.rows, 0, 0, bgr.cols, bgr.rows,
                        bgr.data, &bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
        }

        printerDC.EndPage();
        printerDC.EndDoc();

        delete[] pPrinterInfo;
        printerDC.DeleteDC();
        ClosePrinter(hPrinter);
}

这个修改确保了整幅图像是在一个页面内打印完成的,而不需要在每个块打印时叫`StartPage`和`EndPage`。使得代码逻辑清晰,减少了不必要的页面操作,使其更高效。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。

cxnet 发表于 2024-11-19 12:25:42

FishC 发表于 2024-11-19 11:40
了解了你的需求,下面是修改后的代码,确保所有分块图像都被打印在同一个页面上:




现在确实已经在一个版面了,但问题依然是图案只能绘制一部份,后面仍然是空白

FishC 发表于 2024-11-19 12:26:00

cxnet 发表于 2024-11-19 12:25
现在确实已经在一个版面了,但问题依然是图案只能绘制 ...

如果图像只打印出一部分,而后面是空白,这可能与坐标计算、绘制方法或者纸张大小设置不当有关。以下是一些建议并在代码中进行了调整:

1. **纸张大小和DPI设置**:确保打印机的纸张大小足够,并且DPI设置正确。如果设置过高,可能会导致内存不足的问题。

2. **目标坐标调整**:确保`StretchDIBits`的目标坐标和尺寸正确,确保整个图像可以被正确定位打印。

3. **代码改进**:

void CMy001Dlg::OnBnClickedOk()
{
        cv::Mat image = cv::imread("605x780_P1_GRAY.tif", cv::IMREAD_UNCHANGED);
        if (image.empty()) {
                AfxMessageBox(_T("无法加载图像!"));
                return;
        }

        int width = image.cols;
        int height = image.rows;
        int blockSize = 100;
        HANDLE hPrinter = NULL;
        BYTE* pPrinterInfo = NULL;
        PRINTER_INFO_2* pInfo2 = NULL;
        DEVMODE* pDevMode = NULL;
        CDC printerDC;
        int dpi = 1200;
        CString strPrintDevice = _T("HP Designjet T730");

        // 打开打印机
        LPWSTR ptName = strPrintDevice.GetBuffer();
        if (!OpenPrinter(ptName, &hPrinter, NULL)) {
                AfxMessageBox(_T("打印机端口打开失败!"));
                return;
        }

        // 获取打印机信息
        DWORD needed;
        GetPrinter(hPrinter, 2, NULL, 0, &needed);
        pPrinterInfo = new BYTE;// 修正内存分配大小
        if (!GetPrinter(hPrinter, 2, pPrinterInfo, needed, &needed)) {
                AfxMessageBox(_T("打印机信息获取失败!"));
                delete[] pPrinterInfo;// 确保删除动态分配的内存
                ClosePrinter(hPrinter);
                return;
        }

        pInfo2 = (PRINTER_INFO_2*)pPrinterInfo;
       
        // 设置 DEVMODE
        pDevMode = (DEVMODE*)GlobalLock(pInfo2->pDevMode);
        if (pDevMode) {
                pDevMode->dmFields |= DM_PAPERWIDTH | DM_PAPERLENGTH | DM_PRINTQUALITY | DM_PAPERSIZE;
                pDevMode->dmPaperSize = DMPAPER_USER;
                pDevMode->dmPaperWidth = static_cast<int>(width / static_cast<double>(dpi) * 25.4 * 10);// 1/10 mm
                pDevMode->dmPaperLength = static_cast<int>(height / static_cast<double>(dpi) * 25.4 * 10);
                pDevMode->dmPrintQuality = dpi;
                GlobalUnlock(pInfo2->pDevMode);
        }

        printerDC.Attach(CreateDC(NULL, pInfo2->pPrinterName, NULL, pDevMode));
        if (printerDC.m_hDC == NULL) {
                AfxMessageBox(_T("创建打印机DC失败!"));
                delete[] pPrinterInfo;
                ClosePrinter(hPrinter);
                return;
        }

        DOCINFO docInfo;
        memset(&docInfo, 0, sizeof(DOCINFO));
        docInfo.cbSize = sizeof(DOCINFO);
        docInfo.lpszDocName = _T("test");
        if (printerDC.StartDoc(&docInfo) < 0) {
                AfxMessageBox(_T("启动打印文档失败!"));
                delete[] pPrinterInfo;
                printerDC.DeleteDC();
                ClosePrinter(hPrinter);
                return;
        }

        printerDC.StartPage();// 在页循环外启动页面

        for (int y = 0; y < height; y += blockSize) {
                int currentBlockHeight = std::min(blockSize, height - y);
                cv::Rect roi(0, y, width, currentBlockHeight);
                cv::Mat block = image(roi);

                BITMAPINFO bitmapInfo = {0};
                bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                bitmapInfo.bmiHeader.biWidth = block.cols;
                bitmapInfo.bmiHeader.biHeight = -block.rows;
                bitmapInfo.bmiHeader.biPlanes = 1;
                bitmapInfo.bmiHeader.biBitCount = 8;// 对于灰度图像
                bitmapInfo.bmiHeader.biCompression = BI_RGB;

                SetStretchBltMode(printerDC, HALFTONE);
                ::StretchDIBits(printerDC, 0, y, block.cols, block.rows, 0, 0, block.cols, block.rows,
                        block.data, &bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
        }

        printerDC.EndPage();
        printerDC.EndDoc();

        delete[] pPrinterInfo;
        printerDC.DeleteDC();
        ClosePrinter(hPrinter);
}

**改进点**:

1. 修正了内存分配`new BYTE`。
2. 将颜色转换去掉,因为是灰度图。
3. `StartDoc`和`EndDoc`只调用一次,确保所有块打印在同一页面。
4. 更新了纸张大小和DPI计算,确保设置的纸张大小正确。

确认以上更改并测试图像是否能够完整打印出来。同时,确保打印机设置也没有限制纸张的大小。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: 图像打印不全