gpt4 book ai didi

objective-c - 子类化 NSImageRep 以支持新文件格式 (pgm)

转载 作者:行者123 更新时间:2023-12-03 16:58:07 24 4
gpt4 key购买 nike

大家好!这是我在这里发表的第一篇文章。

我对 Objective-C 很陌生,所以也许我的问题不是那么难,但这对我来说是一个问题。我在网上搜索过,没有找到任何对我有用的提示...我正在 Xcode 中用 Objective-c 编写一个程序。我需要读取并显示 pgm文件(P5,每个像素两个字节)。为此,我尝试对 NSImageRep 进行子类化但我不知道如何为该文件创建正确的位图以及如何绘制它。以下是到目前为止我的代码:

标题:

@interface CTPWTPGMImageRep : NSImageRep

@property (readonly)NSInteger width;
@property (readonly)NSInteger height;
@property (readonly)NSInteger maxValue;

+ (void)load;
+ (NSArray *)imageUnfilteredTypes;
+ (NSArray *)imageUnfilteredFileTypes;
+ (BOOL)canInitWithData:(NSData *)data;
+ (id)imageRepWithContentsOfFile:(NSString*)file;
+ (id)imageRepWithData:(NSData*)pgmData;

- (id)initWithData:(NSData *)data;
- (BOOL)draw;

@end

和实现:

#import "CTPWTPGMImageRep.h"

@implementation CTPWTPGMImageRep

@synthesize width;
@synthesize height;
@synthesize maxValue;

#pragma mark - class methods
+(void) load
{
NSLog(@"Called 'load' method for CTPWTPGMImageRep");
[NSImageRep registerImageRepClass:[CTPWTPGMImageRep class]];
}

+ (NSArray *)imageUnfilteredTypes
{
// This is a UTI
NSLog(@"imageUnfilteredTypes called");
static NSArray *types = nil;
if (!types) {
types = [[NSArray alloc] initWithObjects:@"public.unix-executable", @"public.data", @"public.item", @"public.executable", nil];
}

return types;
}

+ (NSArray *)imageUnfilteredFileTypes
{
// This is a filename suffix
NSLog(@"imageUnfilteredFileTypes called");

static NSArray *types = nil;
if (!types)
types = [[NSArray alloc] initWithObjects:@"pgm", @"PGM", nil];
return types;
}

+ (BOOL)canInitWithData:(NSData *)data;
{
// FIX IT
NSLog(@"canInitWithData called");
if ([data length] >= 2) // First two bytes for magic number magic number
{
NSString *magicNumber = @"P5";
const unsigned char *mNum = (const unsigned char *)[magicNumber UTF8String];
unsigned char aBuffer[2];
[data getBytes:aBuffer length:2];
if(memcmp(mNum, aBuffer, 2) == 0)
{
NSLog(@"canInitWithData: YES");
return YES;
}
}
NSLog(@"canInitWithData: NO");

// end
return NO;
}

+ (id)imageRepWithContentsOfFile:(NSString*)file {
NSLog(@"imageRepWithContentsOfFile called");
NSData* data = [NSData dataWithContentsOfFile:file];
if (data)
return [CTPWTPGMImageRep imageRepWithData:data];
return nil;
}

+ (id)imageRepWithData:(NSData*)pgmData {
NSLog(@"imageRepWithData called");
return [[self alloc] initWithData:pgmData];
}


#pragma mark - instance methods

- (id)initWithData:(NSData *)data;
{
NSLog(@"initWithData called");
self = [super init];

if (!self)
{
return nil;
}

if ([data length] >= 2) {
NSString *magicNumberP5 = @"P5";
const unsigned char *mnP5 = (const unsigned char *)[magicNumberP5 UTF8String];
unsigned char headerBuffer[20];
[data getBytes:headerBuffer length:2];

if(memcmp(mnP5, headerBuffer, 2) == 0)
{
NSArray *pgmParameters = [self calculatePgmParameters:data beginingByte:3];
width = [[pgmParameters objectAtIndex:0] integerValue];
height = [[pgmParameters objectAtIndex:1] integerValue];
maxValue = [[pgmParameters objectAtIndex:2] integerValue];

if (width <= 0 || height <= 0)
{
NSLog(@"Invalid image size: Both width and height must be > 0");
return nil;
}

[self setPixelsWide:width];
[self setPixelsHigh:height];
[self setSize:NSMakeSize(width, height)];
[self setColorSpaceName:NSDeviceWhiteColorSpace];
[self setBitsPerSample:16];
[self setAlpha:NO];
[self setOpaque:NO];

//What to do here?
//CTPWTPGMImageRep *imageRep =
[NSBitmapImageRep alloc] initWithBitmapDataPlanes:];

//if (imageRep) {
/* code to populate the pixel map */
//}
}
else
{
NSLog(@"It is not supported pgm file format.");
}
}
return self;
//return imageRep;
}

- (BOOL)draw
{
NSLog(@"draw method1 called");
return NO;
}

有趣的是我的canInitWithData:方法从未被调用。你能给我一个如何智能读取位图的提示吗?我认为我需要使用 initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bytesPerRow:bitsPerPixel:但我不知道如何使用它。如何从我的 NSData 创建智能创建(无符号字符 **)平面目的?我需要它吗?

我可以使用 NSDeviceWhiteColorSpace它有白色和阿尔法成分(我不需要阿尔法)

[self setColorSpaceName:NSDeviceWhiteColorSpace];

最困难的部分 - 我完全不知道如何实现绘制方法。有什么建议或提示吗?

预先感谢您的帮助。

编辑:

好的。现在我已经按照 NSGod 的指示实现了:

@implementation CTPWTPGMImageRep

//@synthesize width;
//@synthesize height;

#pragma mark - class methods

+(void) load
{
NSLog(@"Called 'load' method for CTPWTPGMImageRep");
[NSBitmapImageRep registerImageRepClass:[CTPWTPGMImageRep class]];
}

+ (NSArray *)imageUnfilteredTypes
{
// This is a UTI
NSLog(@"imageUnfilteredTypes called");
static NSArray *types = nil;
if (!types) {
types = [[NSArray alloc] initWithObjects:@"public.unix-executable", @"public.data", @"public.item", @"public.executable", nil];
}

return types;
}

+ (NSArray *)imageUnfilteredFileTypes
{
// This is a filename suffix
NSLog(@"imageUnfilteredFileTypes called");

static NSArray *types = nil;
if (!types)
types = [[NSArray alloc] initWithObjects:@"pgm", nil];
return types;
}

+ (NSArray *)imageRepsWithData:(NSData *)data {
NSLog(@"imageRepsWithData called");
id imageRep = [[self class] imageRepWithData:data];
return [NSArray arrayWithObject:imageRep];
}

- (id)initWithData:(NSData *)data {
NSLog(@"initWithData called");
CTPWTPGMImageRep *imageRep = [[self class] imageRepWithData:data];

if (imageRep == nil) {
return nil;
}
return self;
}

#pragma mark - instance methods

+ (id)imageRepWithData:(NSData *)data {
NSLog(@"imageRepWithData called");
if (data.length < 2) return nil;
NSString *magicNumberP5 = @"P5";
const unsigned char *mnP5 = (const unsigned char *)[magicNumberP5 UTF8String];
unsigned char headerBuffer[2];
[data getBytes:headerBuffer length:2];

if (memcmp(mnP5, headerBuffer, 2) != 0) {
NSLog(@"It is not supported pgm file format.");
return nil;
}
NSArray *pgmParameters = [self calculatePgmParameters:data beginingByte:3];

NSInteger width = [[pgmParameters objectAtIndex:0] integerValue]; // width in pixels
NSInteger height = [[pgmParameters objectAtIndex:1] integerValue]; // height in pixels

NSUInteger imageLength = width * height * 2; // two bytes per pixel

// imageData contains bytes of Bitmap only. Without header

NSData *imageData = [data subdataWithRange:
NSMakeRange(data.length - imageLength, imageLength)];

CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)CFBridgingRetain(imageData));
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray); // kCGColorSpaceGenericGrayGamma2_2
CGImageRef imageRef = CGImageCreate(width,
height,
16,
16,
width * 2,
colorSpace,
kCGImageAlphaNone,
provider,
NULL,
false,
kCGRenderingIntentDefault);
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(provider);

if (imageRef == NULL) {
NSLog(@"CGImageCreate() failed!");
}
CTPWTPGMImageRep *imageRep = [[CTPWTPGMImageRep alloc] initWithCGImage:imageRef];
return imageRep;
}

如您所见,我在该部分中保留了 0-255 之间的扩展值,因为我的像素的值在 0-65535 之间。

但是这不起作用。当我从面板中选择 pgm 文件时,没有任何反应。下面是我的 openPanel 代码:

- (IBAction)showOpenPanel:(id)sender
{
NSLog(@"showPanel method called");

__block NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setAllowedFileTypes:[NSImage imageFileTypes]];
[panel beginSheetModalForWindow:[pgmImageView window] completionHandler:^ (NSInteger result) {
if (result == NSOKButton) {
CTPWTPGMImageRep *pgmImage = [[CTPWTPGMImageRep alloc] initWithData:[NSData dataWithContentsOfURL:[panel URL]]];

// NSLog(@"Bits per pixel: %ld",[pgmImage bitsPerPixel]); // BUG HERE!

NSImage *image = [[NSImage alloc] init];
[image addRepresentation:pgmImage];
[pgmImageView setImage:image];
}
panel = nil; // prevent strong ref cycle
}];
}

此外,当我用代码 // NSLog(@"Bits per pixel: %ld",[pgmImage bitsPerPixel]); // BUG HERE! 取消注释行时只是为了检查我的 Xcode 卡住了一会儿,我得到了 EXC_BAD_ACCESS:

AppKit`__75-[NSBitmapImageRep _withoutChangingBackingPerformBlockUsingBackingCGImage:]_block_invoke_0:
0x7fff8b4823e8: pushq %rbp
0x7fff8b4823e9: movq %rsp, %rbp
0x7fff8b4823ec: pushq %r15
0x7fff8b4823ee: pushq %r14
0x7fff8b4823f0: pushq %r13
0x7fff8b4823f2: pushq %r12
0x7fff8b4823f4: pushq %rbx
0x7fff8b4823f5: subq $312, %rsp
0x7fff8b4823fc: movq %rsi, %rbx
0x7fff8b4823ff: movq %rdi, %r15
0x7fff8b482402: movq 10625679(%rip), %rax
0x7fff8b482409: movq (%rax), %rax
0x7fff8b48240c: movq %rax, -48(%rbp)
0x7fff8b482410: movq %rbx, %rdi
0x7fff8b482413: callq 0x7fff8b383148 ; BIRBackingType //EXC_BAD_ACCESS (code=2, adress=...)

有什么帮助吗???我不知道出了什么问题......

最佳答案

实际上比您想象的要容易得多。

首先,我建议将 CTPWTPGMImageRep 设为 NSBitmapImageRep 的子类,而不是 NSImageRep。这将解决“最难”的问题,因为不需要实现自定义的绘制方法,因为 NSBitmapImageRep 已经知道如何绘制自己。 (在 OS X 10.5 及更高版本中,NSBitmapImageRep 基本上是 CoreGraphics CGImageRef 的直接包装器)。

我对 PGM 格式不太熟悉,但您基本上要做的就是以与源格式匹配的最接近的目标格式创建图像的表示形式。为了使用一个具体的例子,我们将采用 PGM example FEEP image来自维基百科。

P2
# Shows the word "FEEP" (example from Netpbm main page on PGM)
24 7
15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0
0 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0
0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

enter image description here

能够描绘示例图像中的值的最接近的 native 图像将是单 channel 灰度图像,每像素 8 位,无 Alpha。接下来的计划是使用指定的设置创建一个 CGImageRef,然后使用 NSBitmapImageRep 的 initWithCGImage: 方法来初始化自定义子类。

我首先重写以下两个方法,使其都依赖于单数 +imageRepWithData::

+ (NSArray *)imageRepsWithData:(NSData *)data {
id imageRep = [[self class] imageRepWithData:data];
return [NSArray arrayWithObject:imageRep];
}

- (id)initWithData:(NSData *)data {
CTPWTPGMImageRep *imageRep = [[self class] imageRepWithData:data];
if (imageRep == nil) {
[self release];
return nil;
}
self = [imageRep retain];
return self;
}

对我来说,有必要实现 +imageRepsWithData: 方法来调用奇异方法,然后才能正确加载图像。

然后我会将单数 +imageRepWithData: 方法更改如下:

+ (id)imageRepWithData:(NSData *)data {
if (data.length < 2) return nil;
NSString *magicNumberP5 = @"P5";
const unsigned char *mnP5 = (const unsigned char *)[magicNumberP5 UTF8String];
unsigned char headerBuffer[20];
[data getBytes:headerBuffer length:2];

if (memcmp(mnP5, headerBuffer, 2) != 0) {
NSLog(@"It is not supported pgm file format.");
return nil;
}
NSArray *pgmParameters = [self calculatePgmParameters:data beginingByte:3];

NSUInteger width = [[pgmParameters objectAtIndex:0] integerValue];
NSUInteger height = [[pgmParameters objectAtIndex:1] integerValue];
NSUInteger maxValue = [[pgmParameters objectAtIndex:2] integerValue];

NSUInteger imageLength = width * height * 1;

NSData *imageData = [data subdataWithRange:
NSMakeRange(data.length - imageLength, imageLength)];
const UInt8 *imageDataBytes = [imageData bytes];
UInt8 *expandedImageDataBytes = malloc(imageLength);

for (NSUInteger i = 0; i < imageLength; i++) {
expandedImageDataBytes[i] = 255 * (imageDataBytes[i] / (CGFloat)maxValue);
}

NSData *expandedImageData = [NSData dataWithBytes:expandedImageDataBytes
length:imageLength];
free(expandedImageDataBytes);
CGDataProviderRef provider = CGDataProviderCreateWithCFData(
(CFDataRef)expandedImageData);
CGColorSpaceRef colorSpace =
CGColorSpaceCreateWithName(kCGColorSpaceGenericGrayGamma2_2);
CGImageRef imageRef = CGImageCreate(width,
height,
8,
8,
width * 1,
colorSpace,
kCGImageAlphaNone,
provider,
NULL,
false,
kCGRenderingIntentDefault);
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(provider);
if (imageRef == NULL) {
NSLog(@"CGImageCreate() failed!");
}
CTPWTPGMImageRep *imageRep = [[[CTPWTPGMImageRep alloc]
initWithCGImage:imageRef] autorelease];
CGImageRelease(imageRef);
return imageRep;
}

如您所见,我们需要循环遍历原始图像中的字节,并创建这些字节的第二个副本,其完整扩展范围在 0 到 255 之间。

要使用此图像代表,您可以像下面这样调用它(确保使用 NSImageinitWithData: 方法):

// if it hasn't been done already:
[NSImageRep registerImageRepClass:[CTPWTPGMImageRep class]];

NSString *path = [[NSBundle mainBundle] pathForResource:@"feep" ofType:@"pgm"];
NSData *data = [NSData dataWithContentsOfFile:path];
NSImage *image = [[[NSImage alloc] initWithData:data] autorelease];
[self.imageView setImage:image];

关于 +imageUnfilteredFileTypes 方法的另一个注意事项:文件扩展名不区分大小写,因此无需同时指定小写和大写 @"PGM",您只需做小写:

+ (NSArray *)imageUnfilteredFileTypes {
static NSArray *types = nil;
if (!types) types = [[NSArray alloc] initWithObjects:@"pgm", nil];
return types;
}

关于objective-c - 子类化 NSImageRep 以支持新文件格式 (pgm),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14270496/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com