【问题标题】:Mac OS Cocoa: Draw a simple pixel on a canvasMac OS Cocoa:在画布上绘制一个简单的像素
【发布时间】:2011-05-20 09:04:17
【问题描述】:

我希望我能找到答案。我已经搜索和搜索并找不到正确的答案。这是我的情况:

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

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

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

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

imageObj.drawPixel(10,10,blackColor);

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

谢谢!

【问题讨论】:

    标签: objective-c cocoa macos pixel nsimageview


    【解决方案1】:

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

    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 Getting the pixel data from a CGImage object 的 Apple 技术问答中提供了执行此操作的示例代码。

    【讨论】:

      【解决方案2】:

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

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

      您可以使用 NSBezierPath 来做到这一点,或者您可以从 [[NSGraphicsContext currentContext] graphicsPort] 获取核心图形上下文 (CGContextRef) 并使用像 CGContextFillRect() 这样的函数。

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

      【讨论】:

        【解决方案3】:

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

        参见NSRectFill() 和相关函数,如NSRectFillList()NSRectFillUsingOperation()

        如果您要绘制大量单独的像素,NSRectFillList() 的速度与您可以做到的速度差不多,而无需求助于滚动您自己的图像缓冲区。

        【讨论】:

          【解决方案4】:

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

          【讨论】:

            【解决方案5】:

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

            void CGContextFillRect (CGContextRef c, CGRect rect);
            

            【讨论】:

            • 或者从 drawRect: 或 lockFocus 块中,您可以直接使用 NSFillRect 而无需获取 CGContextRef
            【解决方案6】:

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

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

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

            【讨论】:

              【解决方案7】:
              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)))
                  }
              }
              

              【讨论】:

                【解决方案8】:

                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

                【讨论】:

                  猜你喜欢
                  • 2011-12-10
                  • 2015-12-31
                  • 2012-03-22
                  • 2012-05-04
                  • 1970-01-01
                  • 2016-07-08
                  • 1970-01-01
                  • 2016-10-22
                  • 1970-01-01
                  相关资源
                  最近更新 更多