NSImage Getting Resized when I draw Text on it

2019-01-26 02:42发布

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? Please advice

func drawText(image :NSImage) ->NSImage
{
    let text = "Sample Text"
    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 = [
        NSFontAttributeName: font,
        NSForegroundColorAttributeName: NSColor.white,
        NSParagraphStyleAttributeName: 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: NSCalibratedRGBColorSpace, bytesPerRow: 0, bitsPerPixel: 0)!

    im.addRepresentation(rep)

    im.lockFocus()

    image.draw(in: imageRect)
    text.draw(in: textRect, withAttributes: textFontAttributes)

    im.unlockFocus()

    return im
}

2条回答
Animai°情兽
2楼-- · 2019-01-26 02:46

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
        }
    }
}
查看更多
狗以群分
3楼-- · 2019-01-26 02:47

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()
查看更多
登录 后发表回答