访问 CGImageRef 底层字节并修改?
Access CGImageRef underlying bytes and modify?
我正在开发一个 OSX 应用程序,它可以进行一些像素级图像处理。我正在使用以下代码访问像素颜色分量 (RGBA) 作为常规字节转换为 uint8 指针。
NSImage *image = self.iv.image;
NSRect imageRect = NSMakeRect(0, 0, image.size.width, image.size.height);
CGImageRef cgImage = [image CGImageForProposedRect:&imageRect context:NULL hints:nil];
NSData *data = (NSData *)CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(cgImage)));
uint8 *pixels = (uint8 *)[data bytes];
此时我应用了一些字节级别的更改:
for (int i = 0; i < [data length]; i += 4) { ... }
更改此内存区域似乎对原始 CGImageRef
(当时显示在 NSImageView
中)没有任何影响。我必须执行以下操作才能相应地查看图像更新:
CGImageRef newImageRef = CGImageCreate (width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
bitmapInfo,
provider,
NULL,
false,
kCGRenderingIntentDefault);
NSSize size = NSMakeSize(CGImageGetWidth(newImageRef),
CGImageGetHeight(newImageRef));
NSImage * newIm = [[NSImage alloc] initWithCGImage:newImageRef size:size];
self.iv.image = newIm;
换句话说,我返回修改的字节只是原始字节的副本,大概是CGDataProviderCopyData(CGImageGetDataProvider(cgImage)
的结果。
我的问题如下。有没有一种方法可以直接访问 CGImageRef 的底层字节,这样当我修改它们时,图像会在我操作它们时在屏幕上更新?
没有。 CGImage
是不可变的。一旦创建就无法更改。
在您的代码中,对 [data bytes]
的调用给出了指向 const
void
的指针。您已经放弃了 const
,它可以在没有警告的情况下进行编译,但这违反了设计合同。写入支持数据提供程序的缓冲区是不合法的,并且不能保证有效,即使您从中创建一个新的 CGImage
。
我还要指出,缓冲区中数据的格式可能与您预期的完全不同。没有充分的理由期望数据为每像素 32 位,RGBA vs. BGRA vs. ARGB vs. ...,或任何东西。
我强烈建议您阅读 10.6 AppKit release notes 中有关各种图像对象的部分。向下滚动到 "NSImage, CGImage, and CoreGraphics impedance matching" 并通读以下所有与图像相关的部分,直到您点击 "NSComboBox"。 "NSBitmapImageRep: CoreGraphics impedance matching and performance notes" 部分是对您的目的而言更重要的部分之一。
除此之外,您还可以维护一个像素缓冲区,您可以按照自己喜欢的格式自行分配该像素缓冲区。然后,当你想要它的 CGImage
时,从缓冲区创建它,用它绘制,然后丢弃它。任何像素操作都将在该缓冲区上完成。
我正在开发一个 OSX 应用程序,它可以进行一些像素级图像处理。我正在使用以下代码访问像素颜色分量 (RGBA) 作为常规字节转换为 uint8 指针。
NSImage *image = self.iv.image;
NSRect imageRect = NSMakeRect(0, 0, image.size.width, image.size.height);
CGImageRef cgImage = [image CGImageForProposedRect:&imageRect context:NULL hints:nil];
NSData *data = (NSData *)CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(cgImage)));
uint8 *pixels = (uint8 *)[data bytes];
此时我应用了一些字节级别的更改:
for (int i = 0; i < [data length]; i += 4) { ... }
更改此内存区域似乎对原始 CGImageRef
(当时显示在 NSImageView
中)没有任何影响。我必须执行以下操作才能相应地查看图像更新:
CGImageRef newImageRef = CGImageCreate (width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
bitmapInfo,
provider,
NULL,
false,
kCGRenderingIntentDefault);
NSSize size = NSMakeSize(CGImageGetWidth(newImageRef),
CGImageGetHeight(newImageRef));
NSImage * newIm = [[NSImage alloc] initWithCGImage:newImageRef size:size];
self.iv.image = newIm;
换句话说,我返回修改的字节只是原始字节的副本,大概是CGDataProviderCopyData(CGImageGetDataProvider(cgImage)
的结果。
我的问题如下。有没有一种方法可以直接访问 CGImageRef 的底层字节,这样当我修改它们时,图像会在我操作它们时在屏幕上更新?
没有。 CGImage
是不可变的。一旦创建就无法更改。
在您的代码中,对 [data bytes]
的调用给出了指向 const
void
的指针。您已经放弃了 const
,它可以在没有警告的情况下进行编译,但这违反了设计合同。写入支持数据提供程序的缓冲区是不合法的,并且不能保证有效,即使您从中创建一个新的 CGImage
。
我还要指出,缓冲区中数据的格式可能与您预期的完全不同。没有充分的理由期望数据为每像素 32 位,RGBA vs. BGRA vs. ARGB vs. ...,或任何东西。
我强烈建议您阅读 10.6 AppKit release notes 中有关各种图像对象的部分。向下滚动到 "NSImage, CGImage, and CoreGraphics impedance matching" 并通读以下所有与图像相关的部分,直到您点击 "NSComboBox"。 "NSBitmapImageRep: CoreGraphics impedance matching and performance notes" 部分是对您的目的而言更重要的部分之一。
除此之外,您还可以维护一个像素缓冲区,您可以按照自己喜欢的格式自行分配该像素缓冲区。然后,当你想要它的 CGImage
时,从缓冲区创建它,用它绘制,然后丢弃它。任何像素操作都将在该缓冲区上完成。