【问题标题】:Mac OS X Quartz CGWindowListCreateImage scrambles image on 1366x768 resolutionsMac OS X Quartz CGWindowListCreateImage 在 1366x768 分辨率上打乱图像
【发布时间】:2013-04-23 13:57:36
【问题描述】:

我在 Mac OS X 上的 C++ 项目中实现了以下代码,用于捕获桌面屏幕:

int ScreenCaptureRoutines::CaptureImage(int width, int height)
{
  CGRect captureRect;

  captureRect.origin.x = 0;
  captureRect.origin.y = 0;
  captureRect.size.width = width;
  captureRect.size.height = height;

  CGImageRef img = CGWindowListCreateImage(captureRect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault);

  if(img == NULL)
  {
      fprintf(stderr, "CGWindowListCreateImage failed\n!");
      return -1;
  }

  /* get pixels */
  CGDataProviderRef provider = CGImageGetDataProvider(img);
  CFDataRef dataRef = CGDataProviderCopyData(provider);
  uint8_t* pixels = (uint8_t*)CFDataGetBytePtr(dataRef);

  BitmapUtility::SaveBitmapToFile(pixels, width, height, 32, "/Users/Main/test32.bmp");
}

该代码适用于 1024x768、1280x1024、1280x960、1280x768 等分辨率,但它会将图像完全打乱为 1366x768。

这是用于保存像素的 SaveBitmapFile 方法:

void BitmapUtility::SaveBitmapToFile(u8* pBitmapBits, long lWidth, long lHeight, unsigned short wBitsPerPixel, char* lpszFileName)
{
     BITMAPINFOHEADER bmpInfoHeader = {0};
     bmpInfoHeader.biSize = sizeBITMAPINFOHEADER;
     bmpInfoHeader.biBitCount = wBitsPerPixel;
     bmpInfoHeader.biClrImportant = 0;
     bmpInfoHeader.biClrUsed = 0;
     bmpInfoHeader.biCompression = BI_RGB;
     bmpInfoHeader.biHeight = lHeight;
     bmpInfoHeader.biWidth = lWidth;
     bmpInfoHeader.biPlanes = 1;
     bmpInfoHeader.biSizeImage = lWidth* lHeight * (wBitsPerPixel / 8);

     BITMAPFILEHEADER bfh = {0};
     bfh.bfType=0x4D42;
     bfh.bfOffBits = sizeBITMAPINFOHEADER + sizeBITMAPFILEHEADER;
     bfh.bfSize = bfh.bfOffBits + bmpInfoHeader.biSizeImage;

     //Creating bitmap file
     int fileDescriptor = open(lpszFileName, O_CREAT | O_WRONLY);
     if (fileDescriptor == -1)
     {
        //returning if error encountered when opening file
        return;
     }

     ByteBufferOutputStream bufferInfoHeader;

     bufferInfoHeader.write(&bmpInfoHeader.biSize, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biWidth, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biHeight, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biPlanes, 2);
     bufferInfoHeader.write(&bmpInfoHeader.biBitCount, 2);
     bufferInfoHeader.write(&bmpInfoHeader.biCompression, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biSizeImage, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biXPelsPerMeter, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biYPelsPerMeter, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biClrUsed, 4);
     bufferInfoHeader.write(&bmpInfoHeader.biClrImportant, 4);

     ByteBufferOutputStream bufferFileHeader;

     bufferFileHeader.write(&bfh.bfType, 2);
     bufferFileHeader.write(&bfh.bfSize, 4);
     bufferFileHeader.write(&bfh.bfReserved1, 2);
     bufferFileHeader.write(&bfh.bfReserved2, 2);
     bufferFileHeader.write(&bfh.bfOffBits, 4);

     //Writing bitmap to file
     ssize_t bytesWritten = write(fileDescriptor, bufferFileHeader.get()->getData(), sizeBITMAPFILEHEADER);
     bytesWritten = write(fileDescriptor, bufferInfoHeader.get()->getData(), sizeBITMAPINFOHEADER);
     bytesWritten = write(fileDescriptor, pBitmapBits, bmpInfoHeader.biSizeImage);

     //Closing bitmap file
     close(fileDescriptor);
 }

SaveBitmapToFile 方法仅用于测试,在实际场景中,我实际上是通过 libavcodec 传递捕获的像素以将帧转换并保存为 FLV 格式,但最终结果是相同的,图像得到乱码。

有谁知道为什么代码不适用于 1366x768(或其他非标准 4:3 或 16:9 分辨率)?

【问题讨论】:

    标签: macos resolution quartz-graphics distortion


    【解决方案1】:

    首先,您应该尝试在捕获后立即将 CGImageRef 渲染到屏幕上,以确保数据有效。

    查看捕获的 bmp 文件。如果图像看起来像伪随机噪声,则您的像素格式错误。您需要调用 CGImageGetWidthCGImageGetHeightCGImageGetBitsPerPixel(以及CGImage.h 中的其他函数)来确保像素格式正确。

    如果您仍然可以辨认出一些像素,但行未对齐,则您的字节步长是错误的。 1024 和 1280 是 32、64 和 128 的倍数。另一方面,1366 的 2 除数的最大幂是 2。驱动程序通常使用 64 字节对齐的像素步幅,因为这是常见的高速缓存行大小。步幅等于宽度 + 零填充。调用CGImageGetBytesPerRow获取从上一行起始地址到下一行起始地址的字节偏移量。

    用跨步写入图像的伪代码:

    ptr = pixels
    bytesPerRow = CGImageGetBytesPerRow()
    bytesPerPixel = (CGImageGetBitsPerPixel() + 7) / 8
    
    for row = 0 to height:
        write(ptr, width*bytesPerPixel)
        ptr += bytesPerRow
    

    【讨论】:

    • 谢谢@ErdiChen,我认为你的回答是正确的,我最终在我的应用程序中使用了以下代码:width = width & 0xFFFFFFF0;。无论如何,您的回答似乎抓住了问题的本质。
    • 感谢 Erdi 的精彩解释。它解决了我遇到的一个问题。 :)
    猜你喜欢
    • 1970-01-01
    • 2017-08-23
    • 1970-01-01
    • 2021-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-01
    相关资源
    最近更新 更多