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


我希望我能找到答案。我搜索并搜索过,但无法得到正确答案。这是我的情况:

在Mac OS Cocoa应用程序中,我想在我的应用程序窗口的专用区域上绘制一个像素(实际上是几个像素)。我想,拥有一个更好 NSImageView 放在那里(我用IB做了这个并将插座连接到我的应用代表)然后用它来代替我的 NSWindow

我怎么能这样做呢? Mac OS似乎提供 NSBezierPath 作为最基本的绘图工具 - 是真的吗?这对我来说是完全令人震惊的。我来自Windows编程的悠久历史,并且通常在画布上绘制像素是最简单的事情。

我不想使用OpenGL,我不确定Quartz参与的程度如何。

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

imageObj.drawPixel(10,10,blackColor);

我很想听听你的回答,我相信这将有助于很多人从Cocoa开始。

谢谢!


5034
2017-12-04 23:33


起源



答案:


NSBezierPath是Cocoa中唯一可用于绘制大多数原始形状和许多复杂形状的工具。 您可以在此处找到详细说明: 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


-11
2017-12-04 23:59



这是错的。 - uchuugaka


答案:


NSBezierPath是Cocoa中唯一可用于绘制大多数原始形状和许多复杂形状的工具。 您可以在此处找到详细说明: 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


-11
2017-12-04 23:59



这是错的。 - uchuugaka


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

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中的图像中的像素。在Apple技术问答中有一个示例代码 QA1509从CGImage对象获取像素数据


12
2017-09-16 15:29





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

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

您可以使用NSBezierPath执行此操作,也可以从中获取Core Graphics上下文(CGContextRef) [[NSGraphicsContext currentContext] graphicsPort] 并使用像 CGContextFillRect()

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


9
2017-12-05 00:05





对于您描述的绘制像素,不需要创建路径或使用Quartz 2D或OpenGL API。

看到 NSRectFill() 和相关的功能 NSRectFillList() 和 NSRectFillUsingOperation()

如果你绘制了很多单个像素, NSRectFillList() 在不使用滚动自己的图像缓冲区的情况下,您可以尽可能快地完成它。


3
2017-12-05 10:03





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

void CGContextFillRect (CGContextRef c, CGRect rect);

1
2017-12-05 00:05



或者从drawRect:或lockFocus块中,您可以直接使用NSFillRect而无需获取CGContextRef - Jon Hess


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

1
2018-01-18 23:34





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

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

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


0
2018-03-15 19:19





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

0
2017-08-13 19:54