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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4356441/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 07:24:00  来源:igfitidea点击:

Mac OS Cocoa: Draw a simple pixel on a canvas

objective-ccocoamacospixelnsimageview

提问by Roman

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 NSImageViewplaced there (I did so with IB and connected the outlet to my app delegate) and draw on that instead of my NSWindow.

在 Mac OS Cocoa 应用程序中,我想在应用程序窗口的专用区域上绘制一个像素(实际上是几个像素)。我想,NSImageView在那里放置一个会更好(我用 IB 这样做并将插座连接到我的应用程序委托)并在它而不是我的NSWindow.

How in the world can I do that? Mac OS seems to offer NSBezierPathas 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.

我怎么能做到这一点?Mac OS 似乎提供NSBezierPath了最基本的绘图工具——这是真的吗?这对我来说完全令人震惊。我有着悠久的 Windows 编程历史,在画布上绘制像素是最简单的事情,通常。

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

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

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

我想要的只是关于如何在真正的 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.

我很想听听您对此的回答,我相信这会帮助很多开始使用 Cocoa 的人。

Thanks!

谢谢!

采纳答案by Sergio

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-BBCHFJJGhttp://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_Beginners/Graphics_-_Drawing_with_Quartz

NSBezierPath 是 Cocoa 中唯一可用于绘制最原始形状和许多复杂形状的工具。您可以在此处找到详细说明:http: //developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaDrawingGuide/Paths/Paths.html%23//apple_ref/doc/uid/TP40003290-CH206-BBCHFJJGhttp://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_Beginners/Graphics_-_Drawing_with_Quartz

回答by Logachu

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 setColor:atX:y:更改指定坐标处像素的颜色。

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

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

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:

请注意,这些在 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

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.

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

回答by benzado

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.

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

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).

如果要绘制单个像素,则需要指定一个与单个像素大小相同的矩形,然后将其填充。对于 (x,y) 处的像素,您需要一个原点为 (x- 0.5,y-0.5) 和 (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().

您可以使用 NSBezierPath 来做到这一点,或者您可以从中获取核心图形上下文 (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]并使用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 mallocand writing your pixel data to that, then using Core Graphics to convert it into a CGImageRef, which can be drawn to the screen.

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

回答by NSResponder

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

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

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

请参阅NSRectFill()和相关功能,如NSRectFillList()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.

如果您要绘制大量单个像素,NSRectFillList()则无需借助滚动自己的图像缓冲区即可尽可能快地完成。

回答by Mike Hays

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

这是在 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];
}

回答by Pete Rossi

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

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

void CGContextFillRect (CGContextRef c, CGRect rect);

回答by johndpope

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)))
    }
}

回答by Malte Dreschert

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:

我在这里发现你的问题有点晚,因为我有同样的问题。也许 Apples 开发人员文档可以帮助您。我自己没有测试过,但是看看这个文档:

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

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.

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