【问题标题】:How do I paint/draw/render a dot or color a pixel on the canvas of my window with only a few lines in Obj-C on Mac OS X using Xcode?如何使用 Xcode 在 Mac OS X 上的 Obj-C 中仅用几行在窗口的画布上绘制/绘制/渲染一个点或着色一个像素?
【发布时间】:2020-02-11 01:12:48
【问题描述】:

[编辑:] 从我的项目的第一阶段继续前进:How do I re-define size/origin of app window in code, overriding nib/xib file parameters, with Obj-C, Xcode 11.3.1 on Mac OS X 10.15.2 (Catalina)?

我当前的目标非常简单(至少在抽象方面):我想(重新)为我空白的 Mac OS X 应用程序窗口中的像素着色。我想经济地做到这一点,尽可能少的代码行,因为查看大块代码真的很碍眼。

我真的不想处理图像和图像缓冲区或绘制最小的可见线和矩形,但我愿意尝试所有这些。最后,我阅读的 StackOverflow 和 Apple Kit 文章数不胜数。

最初的议程是:

1) 访问我的空白应用程序窗口的“内容”

2) 在该内容中指定我感兴趣的像素

3) 使用其颜色空间值访问该像素

4) 如果可能,直接重写值

看起来非常紧凑和简单,对吧?

这就是我的 AppDelegate 中发生的事情。我打开构造函数:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    /*
     For a more complex app, it's probably better to not put a window into the Main Menu NIB.
     Instead, make a separate NIB for the window.
     Then, load it using a window controller object and ask that for its window.
     */

    NSRect frame = [_window frame];
    frame.origin.x = 0;
    frame.origin.y = 0;
    frame.size.width = 1425;
    frame.size.height = 810;

    [_window setFrame: frame display: YES];

接下来是我尝试绘制某种东西的尝试。

策略 #1 - NSRectFill:

NSRect pxl;
    pxl.origin.x = 0;
    pxl.origin.y = 0;
    pxl.size.width = 128;
    pxl.size.height = 128;
    //_window.draw(_ dirtyRect: pxl)
    NSRectFill(pxl);
    //draw(_ dirtyRect: pxl);

策略 #2(我真的不想尝试这个,因为它让我觉得既便宜又懒惰) - NSBezierPath:

NSBezierPath * path = [NSBezierPath bezierPath];
    [path setLineWidth: 4];
    NSPoint startPoint = {  0, 0 };
    NSPoint endPoint   = { 128, 128 };
    [path  moveToPoint: startPoint];
    [path lineToPoint: endPoint];
    [[NSColor redColor] set];
    [path stroke];

我知道我在这里画了一条线,但每一点都有帮助。

策略 #3 - 使用图像和图像代表对象:

NSImage * image;

    //create and define a colorspace object:
    NSColor * rgb = [NSColor colorWithDeviceRed:1.0f
                                green:1.0f
                                 blue:1.0f
                                alpha:1.0f];

    //load the defined colorspace data into image rep:
    NSBitmapImageRep * imgRep = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];
    [imgRep setColor:rgb atX:128.0 y:128.0];

当然,这个问题是图像对象不包含任何东西。那我在里面放什么?我必须在其中加载 XIB 文件吗?我在网上看到了一些解决方案,它们打败了“尽可能少写几行”的观点。

策略 #4 - 使用复杂而冗长的函数:

NSColor *MyColorAtScreenCoordinate(CGDirectDisplayID displayID, NSInteger x, NSInteger y)
    { //Function definition is not allowed here
        CGImageRef image = CGDisplayCreateImageForRect(displayID, CGRectMake(x, y, 1, 1));
        NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:image];
        CGImageRelease(image);
        NSColor *color = [bitmap colorAtX:0 y:0];
        [bitmap release];
    }

然后它抱怨: Function definition is not allowed here 所以我用- (void) 把它带到了全球范围内,但它还是抱怨: Expected method body

策略 #5 - 使用 bitmapImageRepForCachingDisplayInRect()

NSData *data;
    NSBitmapImageRep *rep;
    rep = [self bitmapImageRepForCachingDisplayInRect:[self frame]];
    [self cacheDisplayInRect:[self frame] toBitmapImageRep:rep];
    data = [rep TIFFRepresentation];

它给了我: No visible @interface for 'AppDelegate' declares the selector 'frame' 我不是刚刚定义了同一范围内的框架吗?

我用这个关闭我的委托构造函数:

    [_window setFrame: frame display: YES];
}

在我尝试过的范围之外

策略 #6 - 自定义 drawRect():

- (void)drawRect:(NSRect)dirtyRect {
    // set any NSColor for filling, say white:
    [[NSColor whiteColor] setFill];
    NSRectFill(dirtyRect);
    [super drawRect:dirtyRect];
}
No visible @interface for 'NSObject' declares the selector 'drawRect:'

这是否意味着它不知道自己的 drawRect 函数?

策略 #7 - 从Mac OS Cocoa: Draw a simple pixel on a canvas 复制和粘贴一大块:

NSInteger width = 128;
    NSInteger height = 128;
    NSInteger dataLength = width * height * 4;
    UInt8 *data = (UInt8*)malloc(dataLength * sizeof(UInt8));

    //Fill pixel buffer with color data
    for (int j=0; j<height; j++) {
        for (int i=0; i<width; i++) {

            //Here I'm just filling every pixel with red
            float red   = 1.0f;
            float green = 0.0f;
            float blue  = 0.0f;
            float alpha = 1.0f;

            int index = 4*(i+j*width);
            data[index]  =255*red;
            data[++index]=255*green;
            data[++index]=255*blue;
            data[++index]=255*alpha;

        }
    }

    // Create a CGImage with the pixel data
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, dataLength, NULL);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGImageRef img = CGImageCreate(width, height, 8, 32, width * 4, colorspace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast,

                            provider, NULL, true, kCGRenderingIntentDefault);

    //Clean up
    CGColorSpaceRelease(colorspace);
    CGDataProviderRelease(provider);
    // Don't forget to free(data) when you are done with the CGImage

这些方法我都试过了;什么都没有。我在这里做错了吗?我的操作顺序错了吗?我试图让编译器和构建器保持所有语法兼容和可理解。

旁注1:我真的希望我可以使用尽可能少的类/对象来在空白窗口上放置一些东西。

旁注 2:我看过这么多 MVC 图,读过这么多文章,仍然不清楚什么是做什么的。我已经看到很多代码片段试图实现相同的目标,以至于很难相信有不止一种方法可以做一些基本的事情。

在窗口框架内为像素着色的简单行为已成为一个加载冒险,标记为过度利用缓冲区和 Xcode“无法识别”的对象。

No visible @interface declares this selector.

【问题讨论】:

    标签: objective-c xcode macos cocoa graphics


    【解决方案1】:

    为您的自定义绘图创建一个NSView 子类:

    @interface MyView : NSView
    @end
    
    @implementation MyView
    
    - (void)drawRect:(NSRect)dirtyRect
    {
        [[NSColor redColor] setFill];
        NSRectFill(dirtyRect);
    }
    
    @end
    

    确保将其插入到 AppDelegate.m 的顶层 - 即,任何其他 @interface@implementation 块之外。

    创建一个视图实例并将其分配给窗口的内容视图:

    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    {
        // ... Existing code that sets the window frame
    
        MyView* myView = [[MyView alloc] initWithFrame:self.window.contentView.bounds];
        self.window.contentView = myView;
    }
    

    我在这里做错了吗?我的操作顺序错了吗?

    applicationDidFinishLaunching: 是一个委托方法,允许您响应应用程序生命周期的特定部分。这不是尝试在屏幕上绘制的合适位置。考虑您的应用程序可以有多个窗口,每个窗口都包含多个视图或绘图表面。

    是不是说它不知道自己的drawRect函数?

    应用程序委托没有drawRect 函数。

    然后它抱怨:这里不允许函数定义所以我用 - (void) 把它带到了全局范围,但它仍然抱怨:预期的方法体

    一些纯粹是建设性的反馈:看起来你最好从初学者专门介绍 C 或 Objective C 开始,而不是直接跳入编程AppKit。将代码块复制粘贴到applicationDidFinishLaunching: 而不了解他们在做什么不太可能得到你想要的结果。

    【讨论】:

    • 如果我必须备份并了解更多关于 Obj-C 的知识,您是否可以根据个人经验推荐任何关于学习语言和向 3D 图形渲染进展的好的文献?我来自一个教师向你扔代码的地方,希望你能弄清楚其余的并取得好成绩。关于我与那种语言的关系,你可能是对的。我在大学时用 C 语言做过一些编程,并处理过 3D 渲染。为了得出 Obj-C 和 C/C++ 之间的相似性,我在网上查阅了很多资料,希望这一切都有意义。
    • 我想我明白了。我使用了您的代码并在实例化视图时重新定义了dirtyRect 的参数。我也在搞乱self.window.contentView,以便让我自己了解它填补了什么样的空白以及它如何填补这个空白,但我想我稍后会得到它。谢谢。
    • Aaron Hillegass 的 Mac OS X Cocoa 编程 是多年前的圣经,我不知道它是否仍然是最新的,但这些概念应该仍然适用。如果您正在致力于 3D 图形渲染,我不确定深入学习 Objective C 和 Cocoa 是否有巨大的好处。设置好可以绘制的视图后,几乎所有您想做的事情都将使用 C(或特定的着色器语言)..
    猜你喜欢
    • 2011-05-20
    • 2011-12-10
    • 2016-07-08
    • 2011-12-10
    • 2012-01-14
    • 2015-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多