Mac OS Cocoa:在画布上绘制一个简单的像素

发布于 2024-10-05 16:15:02 字数 559 浏览 0 评论 0 原文

我希望我能找到这个问题的答案。我搜索了又搜索,找不到正确的答案。这是我的情况:

在 Mac OS Cocoa 应用程序中,我想在应用程序窗口的专用区域上绘制一个像素(实际上是几个像素)。我想,最好将 NSImageView 放置在那里(我使用 IB 进行此操作,并将插座连接到我的应用程序委托)并利用它而不是我的 NSWindow 进行绘制。

我到底怎样才能做到这一点? Mac OS 似乎提供 NSBezierPath 作为最基本的绘图工具 - 这是真的吗?这让我完全震惊。我有悠久的 Windows 编程历史,通常在画布上绘制像素是最简单的事情。

我不想使用 OpenGL,并且我不确定 Quartz 在多大程度上参与其中。

我想要的只是关于如何在真正的 Objective-C/Cocoa 中实现这个伪代码的一些帮助:

imageObj.drawPixel(10,10,blackColor);

我很想听到您对此的回答,我相信这将帮助很多从 Cocoa 开始的人。

谢谢!

I wish I would find an answer for this. I have searched and searched and couldn't the right answer. Here is my situation:

In a Mac OS Cocoa Application, I want to draw a pixel (actually a few pixels) onto a dedicated area on my application window. I figured, it would be nicer to have a NSImageView placed there (I did so with IB and connected the outlet to my app delegate) and draw on that instead of my NSWindow.

How in the world can I do that? Mac OS seems to offer NSBezierPath as the most basic drawing tool — is that true? This is completely shocking to me. I come from a long history of Windows programming and drawing a pixel onto a canvas is the most simple thing, typically.

I do not want to use OpenGL and I am not sure to what extent Quartz is involved in this.

All I want is some help on how I can pull off this pseudocode in real Objective-C/Cocoa:

imageObj.drawPixel(10,10,blackColor);

I would love to hear your answers on this and I am sure this will help a lot of people starting with Cocoa.

Thanks!

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

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

发布评论

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

评论(8

听不够的曲调 2024-10-12 16:15:03

也许我误解了这个问题,但 Quartz 具有填充矩形的能力:

void CGContextFillRect (CGContextRef c, CGRect rect);

Maybe I am misunderstanding the question, but Quartz has the ability to fill rectangles:

void CGContextFillRect (CGContextRef c, CGRect rect);
末蓝 2024-10-12 16:15:03

我在这里发现你的问题有点晚了,因为我也有同样的问题。也许苹果的开发者文档可以在这里为您提供帮助。我自己没有测试过,但看看这个文档:

http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/CocoaDrawingGuide/Images/Images.html

大致在文档的中心,您将找到“部分”创建位图”。它告诉您创建像素数据的不同方法。

I found your question here a bit late because I have the same problem. Perhaps Apples developer documentation can help you here. I have not tested it myself, but take a look at this document:

http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/CocoaDrawingGuide/Images/Images.html

Roughly in the center of the document you will find the section "Creating a Bitmap". It tells you different ways of creating pixel data.

感情洁癖 2024-10-12 16:15:03
class CMKViewController: NSViewController {


  override func viewDidLoad() {
        super.viewDidLoad()

         let testIV = TestImageView(image: NSImage(named: "test.png")!)
         // testIV.frame = CGRect(x:0,y:0,width:100,height:100)
          self.view.addSubview(testIV)
          testIV.myPixels = [CGPoint(x:10,y:10)]

    }


}

class TestImageView:NSImageView{

    var myPixels:[CGPoint] = []

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        guard let context: CGContext = NSGraphicsContext.current()?.cgContext else {
            consolePrint("Cannot get graphics context")
            return
        }

        // Fill background to white
        context.setFillColor(.white)
        context.fill(bounds)
        context.setFillColor(NSColor.red.cgColor)


        // Draw pixels
        context.fillPixels(myPixels)


    }
}



extension CGContext {

    func fillPixels(_ pixels: [CGPoint]) {
        var size:CGSize?
        if Screen.retinaScale > 1{
            size = CGSize(width: 1.5, height: 1.5)
        }else{
            size = CGSize(width: 1.0, height: 1.0)
        }

        for pixel in pixels{
          fill(CGRect(origin: pixel, size: size!))
        }
    }

    func fill(_ pixel: CGPoint) {
        fill(CGRect(origin: pixel, size: CGSize(width: 1.0, height: 1.0)))
    }
}
class CMKViewController: NSViewController {


  override func viewDidLoad() {
        super.viewDidLoad()

         let testIV = TestImageView(image: NSImage(named: "test.png")!)
         // testIV.frame = CGRect(x:0,y:0,width:100,height:100)
          self.view.addSubview(testIV)
          testIV.myPixels = [CGPoint(x:10,y:10)]

    }


}

class TestImageView:NSImageView{

    var myPixels:[CGPoint] = []

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        guard let context: CGContext = NSGraphicsContext.current()?.cgContext else {
            consolePrint("Cannot get graphics context")
            return
        }

        // Fill background to white
        context.setFillColor(.white)
        context.fill(bounds)
        context.setFillColor(NSColor.red.cgColor)


        // Draw pixels
        context.fillPixels(myPixels)


    }
}



extension CGContext {

    func fillPixels(_ pixels: [CGPoint]) {
        var size:CGSize?
        if Screen.retinaScale > 1{
            size = CGSize(width: 1.5, height: 1.5)
        }else{
            size = CGSize(width: 1.0, height: 1.0)
        }

        for pixel in pixels{
          fill(CGRect(origin: pixel, size: size!))
        }
    }

    func fill(_ pixel: CGPoint) {
        fill(CGRect(origin: pixel, size: CGSize(width: 1.0, height: 1.0)))
    }
}
无人问我粥可暖 2024-10-12 16:15:03

NSBezierPath is the only tool available in Cocoa for drawing most primitive shapes, and for many complex shapes.
Detail description you can find here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaDrawingGuide/Paths/Paths.html%23//apple_ref/doc/uid/TP40003290-CH206-BBCHFJJG
http://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_Beginners/Graphics_-_Drawing_with_Quartz

白况 2024-10-12 16:15:02

您要求的是以下两种方法之一:

NSBitmapRep setColor:atX:y: 更改指定坐标处像素的颜色。

NSBitmapRep setPixel:atX:y: 将指定坐标处的接收器像素设置为指定的原始像素值。

请注意,这些在 iOS 上不可用。在 iOS 上,执行此操作的方法似乎是为给定颜色空间(可能是 RGB)创建像素数据的原始缓冲区,用颜色数据填充该缓冲区(编写一些 setPixel 方法来执行此操作),然后调用 CGImageCreate()像这样:

    //Create a raw buffer to hold pixel data which we will fill algorithmically
    NSInteger width = theWidthYouWant;
    NSInteger height = theHeightYouWant;
    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 image = 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

最后,您可能想要操作已经加载到 CGImage 中的图像中的像素。在标题为 QA1509 的 Apple 技术问答中,有用于执行此操作的示例代码从 CGImage 对象获取像素数据

What you are asking for is either of these two methods:

NSBitmapRep setColor:atX:y: Changes the color of the pixel at the specified coordinates.

NSBitmapRep setPixel:atX:y: Sets the receiver's pixel at the specified coordinates to the specified raw pixel values.

Note that these aren't available on iOS. On iOS, it appears that the way to do this is to create a raw buffer of pixel data for a given colorspace (likely RGB), fill that with color data (write a little setPixel method to do this) and then call CGImageCreate() like so:

    //Create a raw buffer to hold pixel data which we will fill algorithmically
    NSInteger width = theWidthYouWant;
    NSInteger height = theHeightYouWant;
    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 image = 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

Lastly, you might be wanting to manipulate pixels in an image you've already loaded into a CGImage. There is sample code for doing that in an Apple Technical Q&A titled QA1509 Getting the pixel data from a CGImage object.

毅然前行 2024-10-12 16:15:02

Cocoa 的底层绘图 API 是 Core Graphics (Quartz)。您获取绘图上下文并发出在该上下文上绘图的命令。 API 设计为独立于设备(打印时使用与在纸上绘图相同的命令在屏幕上绘图)。因此,没有用于填充单个像素的命令,因为纸上不存在像素这样的东西。即使在屏幕上,您的视图也可能已以某种方式进行转换,以便单个点不会映射到单个设备像素。

如果要绘制单个像素,则需要指定一个大小为单个像素的矩形,然后填充它。对于 (x,y) 处的像素,您需要一个原点为 (x- 0.5,y-0.5),尺寸为(1,1)。

您可以使用 NSBezierPath 来完成此操作,也可以从 [[NSGraphicsContext currentContext]graphicsPort] 获取 Core Graphics 上下文 (CGContextRef) 并使用诸如 CGContextFillRect() 之类的函数。

如果您绘制大量像素,这显然不会很快; API 的设计目的并非如此。如果这就是您需要做的,请考虑使用malloc创建一个缓冲区并将像素数据写入其中,然后使用Core Graphics将其转换为可以绘制到屏幕上的CGImageRef。

Cocoa's low-level drawing API is Core Graphics (Quartz). You obtain a drawing context and issue commands to draw onto that context. The API is designed to be device-independent (you use the same commands to draw onto the screen as you would to draw onto paper, when printing). Therefore, there are no commands for filling in individual pixels, because there's no such thing as a pixel on paper. Even on the screen, your view may have been transformed in some way so that a single point doesn't map to a single device pixel.

If you want to draw a single pixel, you need to specify a rectangle that is the size of a single pixel, then fill it in. For the pixel at (x,y), you would want a rectangle with origin of (x-0.5,y-0.5) and a size of (1,1).

You can do that with NSBezierPath, or you can get a Core Graphics context (CGContextRef) from [[NSGraphicsContext currentContext] graphicsPort] and use functions like CGContextFillRect().

This obviously won't be very fast if you are drawing a lot of pixels; that's not what the API is designed for. If that's what you need to do, consider creating a buffer with malloc and writing your pixel data to that, then using Core Graphics to convert it into a CGImageRef, which can be drawn to the screen.

十年不长 2024-10-12 16:15:02

对于您所描述的绘制像素,无需创建路径或诉诸 Quartz 2D 或 OpenGL API。

请参阅 NSRectFill() 以及相关函数,例如 NSRectFillList()NSRectFillUsingOperation()

如果您要绘制大量单独的像素,NSRectFillList() 的速度大约是您可以完成的最快速度,而无需滚动自己的图像缓冲区。

For drawing pixels as you describe, there's no need to create a path or resort to the Quartz 2D or OpenGL API.

See NSRectFill() and related functions like NSRectFillList() and NSRectFillUsingOperation().

If you're drawing a lot of individual pixels, NSRectFillList() is about as fast as you can do it without resorting to rolling your own image buffers.

暮凉 2024-10-12 16:15:02

以下是在 OS X 上绘制像素的快速方法:

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
                             initWithFocusedViewRect:dirtyRect];

    for (int x = 0; x < [rep pixelsWide]; x++) {
        for (int y = 0; y < [rep pixelsHigh]; y++) {
            NSUInteger pixel[4] = { 0, 255, 0, 255 };
            [rep setPixel:pixel atX:x y:y];
        }
    }

    [rep drawInRect:dirtyRect];
}

Here's a quick way to draw pixels on OS X:

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
                             initWithFocusedViewRect:dirtyRect];

    for (int x = 0; x < [rep pixelsWide]; x++) {
        for (int y = 0; y < [rep pixelsHigh]; y++) {
            NSUInteger pixel[4] = { 0, 255, 0, 255 };
            [rep setPixel:pixel atX:x y:y];
        }
    }

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