将 NSBitmapImageRep 保存为 NSBMPFileType 文件。错误的 BMP 标头和位图内容

发布于 2024-08-30 13:00:59 字数 1537 浏览 5 评论 0原文

我将 NSBitmapImageRep 保存到 BMP 文件(Snow Leopard)。当我在macOS上打开它时似乎没问题。但它在我的多媒体设备上出错(它可以显示来自互联网的任何 BMP 文件)。我不知道出了什么问题,但是当我查看文件内部时(使用 macos 上很酷的 hexfiend 应用程序),

  • 有两处错误:标头的 biHeight 参数值错误:4294966216(十六进制 = C8FBFFFF) 标头具有正确的 biWidth 参数:1920 位
  • 图内容中的第一个像素(BMP 格式的 54 字节标头之后)对应于原始图像的左上角。在原始 BMP 文件中,按照 BMP 格式的规定,它应该首先是左下角像素。

为了解释我的应用程序中的完整工作流程,我有一个 NSImageView,我可以在其中拖动 BMP 图像。该视图绑定到 NSImage。 拖拽后drop 我有一个操作可以将此图像(上面绘制了一些文本)保存到 BMP 文件。

以下是保存新 BMP 文件的代码:

CGColorSpaceRefcolorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGContextRefcontext = CGBitmapContextCreate(NULL, (int)1920, (int)1080, 8, 4*(int)1920, colorSpace, kCGImageAlphaNoneSkipLast);

[duneScreenViewdrawBackgroundWithDuneFolder:self inContext:context inRect:NSMakeRect(0,0,1920,1080) needScale:NO];
if(folderType==DXFolderTypeMovie) {

    [duneScreenViewdrawSynopsisContentWithDuneFolder:self inContext:context inRect:NSMakeRect(0,0,1920,1080) withScale:1.0];
}


CGImageRef backgroundImageRef = CGBitmapContextCreateImage(context);
NSBitmapImageRep*bitmapBackgroundImageRef = [[NSBitmapImageRepalloc] initWithCGImage:backgroundImageRef];


NSData*data = [destinationBitmap representationUsingType:NSBMPFileType properties:nil];
[data writeToFile:[NSStringstringWithFormat:@"%@/%@", folderPath,backgroundPath] atomically: YES];

duneScreenViewdrawSynopsisContentWithDuneFolder 方法使用 CGContextDrawImage 来绘制图像。 duneScreenViewdrawSynopsis 方法使用 CoreText 在同一上下文中绘制一些文本。

你知道出了什么问题吗?

I save a NSBitmapImageRep to a BMP file (Snow Leopard). It seems ok when i open it on macos. But it makes an error on my multimedia device (which can show any BMP file from internet). I cannot figure out what is wrong, but when i look inside the file (with the cool hexfiend app on macos), 2 things wrong:

  • the header have a wrong value for the biHeight parameter : 4294966216 (hex=C8FBFFFF)
    the header have a correct biWidth parameter : 1920
  • the first pixel in the bitmap content (after 54 bytes headers in BMP format) correspond to the upper left corner of the original image. In the original BMP file and as specified in the BMP format, it should be the down left corner pixel first.

To explain the full workflow in my app, i have an NSImageView where i can drag a BMP image. This View is bind to an NSImage.
After a drag & drop i have an action to save this image (with some text drawing over it) to a BMP file.

Here's the code for saving the new BMP file :

CGColorSpaceRefcolorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGContextRefcontext = CGBitmapContextCreate(NULL, (int)1920, (int)1080, 8, 4*(int)1920, colorSpace, kCGImageAlphaNoneSkipLast);

[duneScreenViewdrawBackgroundWithDuneFolder:self inContext:context inRect:NSMakeRect(0,0,1920,1080) needScale:NO];
if(folderType==DXFolderTypeMovie) {

    [duneScreenViewdrawSynopsisContentWithDuneFolder:self inContext:context inRect:NSMakeRect(0,0,1920,1080) withScale:1.0];
}


CGImageRef backgroundImageRef = CGBitmapContextCreateImage(context);
NSBitmapImageRep*bitmapBackgroundImageRef = [[NSBitmapImageRepalloc] initWithCGImage:backgroundImageRef];


NSData*data = [destinationBitmap representationUsingType:NSBMPFileType properties:nil];
[data writeToFile:[NSStringstringWithFormat:@"%@/%@", folderPath,backgroundPath] atomically: YES];

The duneScreenViewdrawSynopsisContentWithDuneFolder method uses CGContextDrawImage to draw the image.
The duneScreenViewdrawSynopsis method uses CoreText to draw some text in the same context.

Do you know what's wrong?

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

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

发布评论

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

评论(1

野鹿林 2024-09-06 13:00:59

我刚刚注册了一个 openid 帐户,所以我无法编辑我自己的问题。我找到了解决这个问题的方法,并想发布我的解决方案。

有两个问题:

  • BMP 标头中的 biHeight 参数错误,
  • 垂直翻转位图内容中的数据(从左上角开始,而不是先从左下角开始)

对于 biHeight 参数,我将 biHeight 字节替换为正确的值(1080 为我的图像)

对于翻转问题,我只是翻转内容位图字节中的所有行。

也许这不是最优雅的解决方案,但它工作得很好。如果您有其他解决方案,请告诉我。

这是代码:

intwidth = 1920;
intheight = 1080;

CGColorSpaceRefcolorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRefcontext = CGBitmapContextCreate(NULL, (int)width, (int)height, 8, 4* (int)width, colorSpace, kCGImageAlphaNoneSkipLast);

[duneScreenViewdrawBackgroundWithDuneFolder:selfinContext:context inRect:NSMakeRect(0,0,width,height) needScale:NO];
if(folderType==DXFolderTypeMovie) {

    [duneScreenViewdrawSynopsisContentWithDuneFolder:selfinContext:context inRect:NSMakeRect(0,0,width,height) withScale:1.0];
}

CGImageRefbackgroundImageRef = CGBitmapContextCreateImage(context);

NSBitmapImageRep*bitmapBackgroundImageRef = [[NSBitmapImageRepalloc] initWithCGImage:backgroundImageRef];

NSData*data = [bitmapBackgroundImageRef representationUsingType:NSBMPFileTypeproperties:nil];

NSMutableData*mutableData = [[NSMutableDataalloc] init];

intbitmapBytesOffset = 54;

//headers
[mutableData appendData:[data subdataWithRange:NSMakeRange(0,bitmapBytesOffset)]];

//bitmap data
intlineIndex=height-1;

while(lineIndex>=0) {

    [mutableData appendData:[data subdataWithRange:NSMakeRange(bitmapBytesOffset+lineIndex*width*3,width*3)]];

    lineIndex--;
}

//force biHeight header parameter to 1080
NSString*biHeightString = @"\x38\x04\x00\x00";
NSData*biHeightData = [biHeightString dataUsingEncoding:NSUTF8StringEncoding];
[mutableData replaceBytesInRange:NSMakeRange(22,4) withBytes:[biHeightData bytes]];

[mutableData writeToFile:[NSStringstringWithFormat:@"%@/%@", folderPath,backgroundPath] atomically: YES];

[mutableData release];


CGImageRelease(backgroundImageRef);
[bitmapBackgroundImageRef release];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);

I just registered an openid account, so i can not edit my own question. I found a way to manage this problem and wanted to post my solution.

There was 2 problems :

  • wrong biHeight parameter in BMP header
  • vertically flipped data in the bitmap content (start with upper left corner in the place of down left corner first)

For the biHeight parameter, i replaced the biHeight bytes to the good value (1080 for my image)

For the flipped problem, i just flip all rows in the content bitmap bytes.

May be this is not the most elegant solution, but it work fine. Just let me know if you if you have other solutions.

Here's the code :

intwidth = 1920;
intheight = 1080;

CGColorSpaceRefcolorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRefcontext = CGBitmapContextCreate(NULL, (int)width, (int)height, 8, 4* (int)width, colorSpace, kCGImageAlphaNoneSkipLast);

[duneScreenViewdrawBackgroundWithDuneFolder:selfinContext:context inRect:NSMakeRect(0,0,width,height) needScale:NO];
if(folderType==DXFolderTypeMovie) {

    [duneScreenViewdrawSynopsisContentWithDuneFolder:selfinContext:context inRect:NSMakeRect(0,0,width,height) withScale:1.0];
}

CGImageRefbackgroundImageRef = CGBitmapContextCreateImage(context);

NSBitmapImageRep*bitmapBackgroundImageRef = [[NSBitmapImageRepalloc] initWithCGImage:backgroundImageRef];

NSData*data = [bitmapBackgroundImageRef representationUsingType:NSBMPFileTypeproperties:nil];

NSMutableData*mutableData = [[NSMutableDataalloc] init];

intbitmapBytesOffset = 54;

//headers
[mutableData appendData:[data subdataWithRange:NSMakeRange(0,bitmapBytesOffset)]];

//bitmap data
intlineIndex=height-1;

while(lineIndex>=0) {

    [mutableData appendData:[data subdataWithRange:NSMakeRange(bitmapBytesOffset+lineIndex*width*3,width*3)]];

    lineIndex--;
}

//force biHeight header parameter to 1080
NSString*biHeightString = @"\x38\x04\x00\x00";
NSData*biHeightData = [biHeightString dataUsingEncoding:NSUTF8StringEncoding];
[mutableData replaceBytesInRange:NSMakeRange(22,4) withBytes:[biHeightData bytes]];

[mutableData writeToFile:[NSStringstringWithFormat:@"%@/%@", folderPath,backgroundPath] atomically: YES];

[mutableData release];


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