Mac OS Cocoa: нарисуйте простой пиксель на холсте - PullRequest
11 голосов
/ 05 декабря 2010

Хотелось бы найти ответ на этот вопрос.Я искал и искал и не мог найти правильный ответ.Вот моя ситуация:

В приложении Mac OS Cocoa я хочу нарисовать пиксель (фактически несколько пикселей) в выделенной области окна моего приложения.Я подумал, что было бы лучше, если бы там поместили NSImageView (я сделал это с IB и подключил розетку к моему делегату приложения) и использовал это вместо моего NSWindow.

Как вмир я могу сделать это?Mac OS, кажется, предлагает NSBezierPath как самый основной инструмент для рисования - это правда?Это совершенно шокирует меня.Я пришел из долгой истории программирования для Windows, и рисование пикселя на холсте - это самая простая вещь, обычно.

Я не хочу использовать OpenGL, и я не уверен, насколько Quartz вовлечен в это..

Все, что мне нужно, это некоторая помощь о том, как я могу осуществить этот псевдокод в реальном Objective-C / Cocoa:

imageObj.drawPixel(10,10,blackColor);

Я хотел бы услышать ваши ответы на этот вопрос, и яуверен, что это поможет многим людям, начинающим с какао.

Спасибо!

Ответы [ 8 ]

12 голосов
/ 16 сентября 2011

То, что вы просите, является одним из этих двух методов:

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 .

9 голосов
/ 05 декабря 2010

Низкоуровневым API рисования Cocoa является 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, который можно отобразить на экране.

3 голосов
/ 05 декабря 2010

Для рисования пикселей, как вы описываете, нет необходимости создавать путь или прибегать к Quartz 2D или OpenGL API.

См. NSRectFill() и связанные с ними функции, такие как NSRectFillList() и NSRectFillUsingOperation().

Если вы рисуете много отдельных пикселей, NSRectFillList() примерно так же быстро, как вы можете сделать это, не прибегая к использованию собственных буферов изображений.

1 голос
/ 19 января 2015

Вот быстрый способ рисовать пиксели на 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 голос
/ 05 декабря 2010

Возможно, я неправильно понимаю вопрос, но у Кварца есть способность заполнять прямоугольники:

void CGContextFillRect (CGContextRef c, CGRect rect);
0 голосов
/ 13 августа 2018
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 голосов
/ 15 марта 2011

Я нашел ваш вопрос здесь немного поздно, потому что у меня та же проблема.Возможно, документация разработчика Apple может помочь вам здесь.Я не проверял это сам, но взгляните на этот документ:

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

Примерно в центре документа вы найдете раздел «Создание растрового изображения».Он рассказывает вам различные способы создания данных пикселей.

0 голосов
/ 05 декабря 2010

NSBezierPath - единственный инструмент, доступный в Какао для рисования самых примитивных фигур и для многих сложных форм. Подробное описание вы можете найти здесь: 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

...