NSImage Getting Resized when I draw Text on it

前端 未结 2 386
面向向阳花
面向向阳花 2020-12-18 16:51

I have the following code to draw a text over an NSImage. But the resulting image is getting resized to smaller one when I save it to disk.

What i\'m i doing wrong?

相关标签:
2条回答
  • 2020-12-18 17:28

    Mixed pixel vs point? Depending on your screen 2x or 3x image is smaller 2 times or 3 times?

    Here is more detailed info (scroll down to "Converting between pixels and points") http://blog.fluidui.com/designing-for-mobile-101-pixels-points-and-resolutions/

    But keep in mind that:

    NSImage is resolution aware and uses a HiDPI graphics context when you lockFocus on a system with retina screen. The image dimensions you pass to your NSBitmapImageRep initializer are in points (not pixels). An 150.0 point-wide image therefore uses 300 horizontal pixels in a @2x context.

    Source: How to save PNG file from NSImage (retina issues)

    Following simple app works for me. Enjoy ;)

    import Cocoa
    
    class ViewController: NSViewController {
    
        func save(image:NSImage, imageURL:String, format:String) -> Bool
        {
            let bMImg = NSBitmapImageRep(data: (image.tiffRepresentation)!)
            switch format {
            case ".png":
                let filepath = URL(fileURLWithPath: imageURL+".png")
                let dataToSave = bMImg?.representation(using: NSBitmapImageRep.FileType.png, properties: [NSBitmapImageRep.PropertyKey.compressionFactor : 1])
                do
                {
                    try  dataToSave?.write(to: filepath)
                    return true
    
                } catch {
                    return false
                }
            default:
                return false
            }
        }
    
        func draw(text:String, image:NSImage) -> NSImage
        {
            let font = NSFont.boldSystemFont(ofSize: 18)
            let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
    
            let textRect = CGRect(x: 5, y: 5, width: image.size.width - 5, height: image.size.height - 5)
            let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
            let textFontAttributes = [
                NSAttributedStringKey.font: font,
                NSAttributedStringKey.foregroundColor: NSColor.white,
                NSAttributedStringKey.paragraphStyle: textStyle
            ]
    
            let im:NSImage = NSImage(size: image.size)
    
            let rep:NSBitmapImageRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(image.size.width), pixelsHigh: Int(image.size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSColorSpaceName.calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0)!
    
            im.addRepresentation(rep)
    
            im.lockFocus()
    
            image.draw(in: imageRect)
            text.draw(in: textRect, withAttributes: textFontAttributes)
    
            im.unlockFocus()
    
            return im
        }
    
        @IBAction func action(_ sender: NSButton) {
            let dialog = NSOpenPanel();
    
            dialog.title                   = "Choose a image...";
            dialog.showsResizeIndicator    = true;
            dialog.showsHiddenFiles        = false;
            dialog.canChooseDirectories    = true;
            dialog.canCreateDirectories    = true;
            dialog.allowsMultipleSelection = false;
            dialog.allowedFileTypes        = ["png", "jpg"];
    
            if (dialog.runModal() == NSApplication.ModalResponse.OK) {
                guard let url = dialog.url,
                    let imageCIImage = CIImage(contentsOf: url) else {
                        return
                }
                let rep: NSCIImageRep = NSCIImageRep(ciImage: imageCIImage)
                let nsImage = NSImage(size: rep.size)
                nsImage.addRepresentation(rep)
    
                let imageWithText = draw(text:"ABC", image: nsImage)
    
                if (save(image: imageWithText, imageURL: "imageWithText", format: ".png")) {
                    print("Success")
                } else {
                    print("ERROR:Failed to save image")
                }
            } else {
                // User clicked on "Cancel"
                return
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-18 17:37

    This is a different approach using a temporary NSView to draw the image and the text and cache the result in a new image (code is Swift 4). The benefit it you don't need to deal with pixels

    class ImageView : NSView  {
    
        var image : NSImage
        var text : String
    
        init(image: NSImage, text: String)
        {
            self.image = image
            self.text = text
            super.init(frame: NSRect(origin: NSZeroPoint, size: image.size))
        }
    
        required init?(coder decoder: NSCoder) { fatalError() }
    
        override func draw(_ dirtyRect: NSRect) {
            let font = NSFont.boldSystemFont(ofSize: 18)
            let textRect = CGRect(x: 5, y: 5, width: image.size.width - 5, height: image.size.height - 5)
            image.draw(in: dirtyRect)
            text.draw(in: textRect, withAttributes: [.font: font, .foregroundColor: NSColor.white])
        }
    
        var outputImage : NSImage  {
            let imageRep = bitmapImageRepForCachingDisplay(in: frame)!
            cacheDisplay(in: frame, to:imageRep)
            let tiffData = imageRep.tiffRepresentation!
            return NSImage(data : tiffData)!
        }
    }
    

    To use it, initialize a view

    let image = ... // get some image
    let view = ImageView(image: image, text: "Sample Text")
    

    and get the new image

    let imageWithText = view.outputImage
    

    Note:

    The paragraph style is not used at all, but if you want to create a mutable paragraph style just write

    let textStyle = NSMutableParagraphStyle()
    
    0 讨论(0)
提交回复
热议问题