admin管理员组

文章数量:1220535

I am trying to create an NSImage in macOS using NSBitmapImageRep while having full control over its pixel data. Specifically, I want to:

Manually allocate and fill pixel data with a solid color.

Support both RGB (when hasAlpha = false) and RGBA (when hasAlpha = true).

Ensure no automatic padding is added when hasAlpha = false (i.e., the image should be stored in memory as tightly packed RGB data without extra unused bytes).

Avoid using NSGraphicsContext or CGContext, relying solely on NSBitmapImageRep to construct the image, because, based on what I learned from other questions and issues, NSGraphicsContext does not support RGB-only images.

Here is my function, but I still see unwanted padding added to my image. Here is the output of this code:

Expected Bytes Per Row: 3072 bytes/row
Actual Bytes Per Row: 4096 bytes/row
Image has padding! Extra 1024 bytes per row.

use case:

let size: NSSize = NSSize(width: 1024, height: 1024)

let myImage = createImage(hasAlpha: false, pixelSize: size, color: .red)

if let validImage: NSImage = myImage {
    if let tiffData: Data = validImage.tiffRepresentation, let nsBitmapImageRep: NSBitmapImageRep = NSBitmapImageRep(data: tiffData) {

        let expectedBytesPerRow = nsBitmapImageRep.samplesPerPixel*nsBitmapImageRep.pixelsWide
        let actualBytesPerRow = nsBitmapImageRep.bytesPerRow
        
        print("Expected Bytes Per Row: \(expectedBytesPerRow) bytes/row")
        print("Actual Bytes Per Row: \(actualBytesPerRow) bytes/row")
        
        if actualBytesPerRow > expectedBytesPerRow {
            print("Image has padding! Extra \(actualBytesPerRow - expectedBytesPerRow) bytes per row.")
        } else {
            print("No padding detected.")
        }
        
    }
}

func createImage(hasAlpha: Bool, pixelSize: NSSize, color: NSColor) -> NSImage? {
    
    let width = Int(pixelSize.width)
    let height = Int(pixelSize.height)
    let components = hasAlpha ? 4 : 3  // RGBA or RGB
    let bitsPerSample = 8
    let bytesPerPixel = components
    let bytesPerRow = width * bytesPerPixel  // Ensure exact memory layout, no padding

    // Allocate memory for pixel buffer
    guard let pixelData = malloc(height * bytesPerRow)?.assumingMemoryBound(to: UInt8.self) else {
        print("Failed to allocate memory.")
        return nil
    }

    defer { free(pixelData) }  // Free memory on function exit

    // Convert color to RGB space
    let colorSpace = NSColorSpace.deviceRGB
    guard let colorInRGB = color.usingColorSpace(colorSpace) else {
        print("Failed to convert color to RGB space.")
        return nil
    }

    let red = UInt8(colorInRGB.redComponent * 255)
    let green = UInt8(colorInRGB.greenComponent * 255)
    let blue = UInt8(colorInRGB.blueComponent * 255)
    let alpha = hasAlpha ? UInt8(colorInRGB.alphaComponent * 255) : 255

    // Fill pixel buffer manually
    for y in 0..<height {
        for x in 0..<width {
            let pixelIndex = (y * bytesPerRow) + (x * bytesPerPixel)
            pixelData[pixelIndex] = red
            pixelData[pixelIndex + 1] = green
            pixelData[pixelIndex + 2] = blue
            if hasAlpha {
                pixelData[pixelIndex + 3] = alpha
            }
        }
    }

    // Create a pointer to the pixel buffer
    var pixelDataPlane: UnsafeMutablePointer<UInt8>? = pixelData

    // Create NSBitmapImageRep without extra padding
    guard let bitmapRep = NSBitmapImageRep(
        bitmapDataPlanes: &pixelDataPlane,  // Pass pointer to pointer
        pixelsWide: width,
        pixelsHigh: height,
        bitsPerSample: bitsPerSample,
        samplesPerPixel: components,
        hasAlpha: hasAlpha,
        isPlanar: false,
        colorSpaceName: .deviceRGB,
        bytesPerRow: bytesPerRow,  // Prevent unwanted padding
        bitsPerPixel: bitsPerSample * bytesPerPixel
    ) else {
        print("Failed to create bitmap representation.")
        return nil
    }

    // Create final NSImage
    let image = NSImage(size: pixelSize)
    image.addRepresentation(bitmapRep)

    return image
}

本文标签: swiftUnwanted Padding in Image Created with NSBitmapImageRep Despite Manual Pixel AllocationStack Overflow