根据绘制的框架,裁剪工作不完美

问题描述

我正在尝试裁剪 NSImage 的选定部分,该部分按照 ProportionallyUpOrDown(AspectFill) 模式进行拟合。 我正在使用鼠标拖动事件绘制一个框架,如下所示:

class CropImageView: NSImageView {

    var startPoint: NSPoint!
    var shapeLayer: CAShapeLayer!
    var flagCheck = false
    var finalPoint: NSPoint!

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
    }
    
    override var image: NSImage? {
        set {
            self.layer = CALayer()
            self.layer?.contentsGravity = kCAGravityResizeAspectFill
            self.layer?.contents = newValue
            self.wantsLayer = true
            super.image = newValue
        }
        get {
            return super.image
        }
    }

    override func mouseDown(with event: NSEvent) {

        self.startPoint = self.convert(event.locationInWindow,from: nil)
        if self.shapeLayer != nil {
            self.shapeLayer.removeFromSuperlayer()
            self.shapeLayer = nil
        }
        self.flagCheck = true
        var pixelColor: NSColor = NSReadPixel(startPoint) ?? NSColor()
        shapeLayer = CAShapeLayer()
        shapeLayer.linewidth = 1.0
        shapeLayer.fillColor = NSColor.clear.cgColor
        if pixelColor == NSColor.black {
            pixelColor = NSColor.color_white
        } else {
            pixelColor = NSColor.black
        }
        shapeLayer.strokeColor = pixelColor.cgColor
        shapeLayer.lineDashPattern = [1]
        self.layer?.addSublayer(shapeLayer)

        var dashAnimation = CABasicAnimation()
        dashAnimation = CABasicAnimation(keyPath: "lineDashPhase")
        dashAnimation.duration = 0.75
        dashAnimation.fromValue = 0.0
        dashAnimation.tovalue = 15.0
        dashAnimation.repeatCount = 0.0
        shapeLayer.add(dashAnimation,forKey: "linePhase")
    }

    override func mouseDragged(with event: NSEvent) {
        let point: NSPoint = self.convert(event.locationInWindow,from: nil)

        var newPoint: CGPoint = self.startPoint
        
        let xDiff = point.x - self.startPoint.x
        let yDiff = point.y - self.startPoint.y
        let dist = min(abs(xDiff),abs(yDiff))
        newPoint.x += xDiff > 0 ? dist : -dist
        newPoint.y += yDiff > 0 ? dist : -dist
        
        let path = CGMutablePath()
        path.move(to: self.startPoint)
        path.addLine(to: NSPoint(x: self.startPoint.x,y: newPoint.y))
        path.addLine(to: newPoint)
        path.addLine(to: NSPoint(x: newPoint.x,y: self.startPoint.y))
        path.closeSubpath()
        self.shapeLayer.path = path
    }

    override func mouseUp(with event: NSEvent) {
        self.finalPoint = self.convert(event.locationInWindow,from: nil)
    }
}

并使用黑色虚线选择如图所示的区域:

enter image description here

我的裁剪代码逻辑是这样的:

// resize Image Methods
extension CropProfileView {

    func resizeImage(image: NSImage) -> Data {

        var scalingFactor: CGFloat = 0.0
        if image.size.width >= image.size.height {
            scalingFactor = image.size.width/cropImgView.size.width
        } else {
            scalingFactor = image.size.height/cropImgView.size.height
        }
        let width = (self.cropImgView.finalPoint.x - self.cropImgView.startPoint.x) * scalingFactor
        let height = (self.cropImgView.startPoint.y - self.cropImgView.finalPoint.y) * scalingFactor
        let xPos = ((image.size.width/2) - (cropImgView.bounds.midX - self.cropImgView.startPoint.x) * scalingFactor)
        let yPos = ((image.size.height/2) - (cropImgView.bounds.midY - (cropImgView.size.height - self.cropImgView.startPoint.y)) * scalingFactor)
        
        var croppedRect: NSRect = NSRect(x: xPos,y: yPos,width: width,height: height)
        let imageRef = image.cgImage(forProposedRect: &croppedRect,context: nil,hints: nil)
        guard let croppedImage = imageRef?.cropping(to: croppedRect) else {return Data()}
        let imageWithNewSize = NSImage(cgImage: croppedImage,size: NSSize(width: width,height: height))

        guard let data = imageWithNewSize.tiffRepresentation,let rep = NSBitmapImageRep(data: data),let imgData = rep.representation(using: .png,properties: [.compressionFactor: NSNumber(floatLiteral: 0.25)]) else {
            return imageWithNewSize.tiffRepresentation ?? Data()
        }
        return imgData
    }
}

通过这种裁剪逻辑,我得到了这个输出

enter image description here

我认为图像是 AspectFill 这就是为什么它没有按照选定的帧被裁剪成完美的尺寸。在这里,如果您查看输出:xpositon & width & heights 并不完美。或者可能我没有正确计算这些坐标。让我知道可能是我计算错误错误

解决方法

注意:问题中的 List<com.bb.loan.Liability> liabilities = new ArrayListList<com.bb.loan.Liability>(); result.liabilities = liabilities; 类是 CropImageView 的子类,但视图是层托管的,图像是由层绘制的,而不是由 NSImageView 绘制的。未使用 NSImageView

在决定使用哪个缩放因子时,您必须考虑图像视图的大小。如果图像大小为 imageScaling 且图像视图大小为 width:120,height:100,则 width:120,height 80image.size.width >= image.size.heighttrueimage.size.width/cropImgView.size.width 但图像已缩放因为 1image.size.height/cropImgView.size.height。计算水平和垂直比例因子并使用最大的。

How to crop a UIImageView to a new UIImage in 'aspect fill' mode?

以下是 1.25 的计算,假设 croppedRect 返回 cropImgView.size

self.layer!.bounds.size

修正:var scalingWidthFactor: CGFloat = image.size.width/cropImgView.size.width var scalingHeightFactor: CGFloat = image.size.height/cropImgView.size.height var xOffset: CGFloat = 0 var yOffset: CGFloat = 0 switch cropImgView.layer?.contentsGravity { case CALayerContentsGravity.resize: break case CALayerContentsGravity.resizeAspect: if scalingWidthFactor > scalingHeightFactor { scalingHeightFactor = scalingWidthFactor yOffset = (cropImgView.size.height - (image.size.height / scalingHeightFactor)) / 2 } else { scalingWidthFactor = scalingHeightFactor xOffset = (cropImgView.size.width - (image.size.width / scalingWidthFactor)) / 2 } case CALayerContentsGravity.resizeAspectFill: if scalingWidthFactor < scalingHeightFactor { scalingHeightFactor = scalingWidthFactor yOffset = (cropImgView.size.height - (image.size.height / scalingHeightFactor)) / 2 } else { scalingWidthFactor = scalingHeightFactor xOffset = (cropImgView.size.width - (image.size.width / scalingWidthFactor)) / 2 } default: print("contentsGravity \(String(describing: cropImgView.layer?.contentsGravity)) is not supported") return nil } let width = (self.cropImgView.finalPoint.x - self.cropImgView.startPoint.x) * scalingWidthFactor let height = (self.cropImgView.startPoint.y - self.cropImgView.finalPoint.y) * scalingHeightFactor let xPos = (self.cropImgView.startPoint.x - xOffset) * scalingWidthFactor let yPos = (cropImgView.size.height - self.cropImgView.startPoint.y - yOffset) * scalingHeightFactor var croppedRect: NSRect = NSRect(x: xPos,y: yPos,width: width,height: height) 应该是选择的角落,而不是 cropImgView.finalPoint 的位置。在 mouseUp 中,在 CropImageView 中设置 self.finalPoint = newPoint 而不是 mouseDragged