-1

How do I mirror a jpeg file, swift, macOS?

Using something like CGImageSourceCreateWithURL would be good. I would like to change a property and then write the file out again. Not sure if this is possible.

Another way appears to be drawing the image and then write this out to a file. The image needs to be scaled with a -1 scale and transformed so that it still appears in the destination correctly. I've not been able to find a swift example of doing this. This is an example that is not in swift. I cannot reproduce it in swift. Also using an NSImage requires decoding and then encoding the JPEG file. A solution that does not do this would be better. Flip NSImage on both axes

Edit: A vImage may be a middle ground. This will need to be populated by reading the JPEG but the data could be flipped without "drawing" it at a scale of -1. I think an NSImage could be made using initWithContentsOfURL or initByReferencingURL and a CGImage made from the NSImage using CGImageForProposedRect. The vImage made from the CGImage?

  • @Rob Do you know how I get a vImage from a JPEG file on MacOS? iOS examples use a UIImage, but I don't think that is available on macOS. I think it can probably be done. Its just knowing the chain from a URL or path to a vImage. (and then I have to write it out again to a JPEG file.) – William J Bagshaw Mar 31 '19 at 23:31

1 Answers1

1

To mirror a NSImage using vImage mirroring routines, you can do something like:

extension NSImage {
    func mirrored() -> NSImage? {
        guard
            let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil),
            let colorSpace = cgImage.colorSpace else {
                return nil
        }

        var format = vImage_CGImageFormat(bitsPerComponent: UInt32(cgImage.bitsPerComponent),
                                          bitsPerPixel: UInt32(cgImage.bitsPerPixel),
                                          colorSpace: Unmanaged.passRetained(colorSpace),
                                          bitmapInfo: cgImage.bitmapInfo,
                                          version: 0,
                                          decode: nil,
                                          renderingIntent: cgImage.renderingIntent)

        var source = vImage_Buffer()
        var result = vImageBuffer_InitWithCGImage(
            &source,
            &format,
            nil,
            cgImage,
            vImage_Flags(kvImageNoFlags))

        guard result == kvImageNoError else { return nil }

        defer { free(source.data) }

        var destination = vImage_Buffer()
        result = vImageBuffer_Init(
            &destination,
            vImagePixelCount(cgImage.height),
            vImagePixelCount(cgImage.width),
            UInt32(cgImage.bitsPerPixel),
            vImage_Flags(kvImageNoFlags))

        guard result == kvImageNoError else { return nil }

        result = vImageHorizontalReflect_ARGB8888(&source, &destination, vImage_Flags(kvImageNoFlags))
        guard result == kvImageNoError else { return nil }

        defer { free(destination.data) }

        return vImageCreateCGImageFromBuffer(&destination, &format, nil, nil, vImage_Flags(kvImageNoFlags), nil).map {
            NSImage(cgImage: $0.takeRetainedValue(), size: size)
        }
    }

}

Thus, taking a screen snapshot of the above in my editor, I then mirrored the image, resulting in:

enter image description here

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • This looks great. I had hoped to manipulate the JPEG representation directly. This approach is manipulating the decompressed data but does so directly. This feels better than using the NSAffineTransform approach. – William J Bagshaw Apr 01 '19 at 00:06
  • 1
    Yeah, I don’t know of any JPEG direct method. And while I haven’t benchmarked the transform based approaches, my experience is that vImage manipulation generally tends to be much faster. – Rob Apr 01 '19 at 00:19
  • This basically works. I am, however, get some images that are resampled and look like thumb nails. I think one of two things may be happening. I could be getting the thumbnail from the image file, or the resolution is high and it is being produced at lower resolution. – William J Bagshaw Apr 01 '19 at 12:35
  • Added: self.representations[0].size.width = CGFloat(self.representations[0].pixelsWide) self.representations[0].size.height = CGFloat(self.representations[0].pixelsHigh) to the top. Works great! – William J Bagshaw Apr 01 '19 at 13:32