Clipin Log 7. 提升截屏流畅度

Posted on
iOS dev

旧版本

在之前的版本中,我在一个ClipView上对暗色背景和高亮区域绘制:

override func draw(_ dirtyRect: NSRect) {
    
    super.draw(dirtyRect)

    // Drawing code here.
    guard let image = self.image, let drawingRect = self.drawingRect else {
        return
    }
    image.draw(in: self.bounds, operation: .sourceOver, fraction: 0.5) // 绘制背景
    var rect = NSIntersectionRect(drawingRect, self.bounds)  // 取高亮区域和页面的交集区域
    rect = NSIntegralRect(rect)  // 区域取整,为了防止抖动问题

            // 取屏幕图像中高亮区域部分渲染在view上,使用.sourceOver操作
            // 由于source图像是alpha为1,所以渲染结果即是区域图像本身
    image.draw(in: rect, from: rect, operation: .sourceOver, fraction: 1.0)
}    

新版本

将背景View和高亮区域View分离,在NSWindow中分别添加两个view:

import Cocoa

class ClipWindow: NSWindow {
    
    var rectView: RectView
    var backView: BackView
    
    init(contentRect: NSRect, backView: BackView, rectView: RectView) {
        self.rectView = rectView
        self.backView = backView

        super.init(contentRect: contentRect, styleMask: .borderless, backing: .buffered, defer: false)
        self.contentView?.addSubview(backView)
        self.contentView?.addSubview(rectView)
        self.level = .statusBar
    }
    
}

绘制高亮区域

import Cocoa

class RectView: NSView {
    
    var image: NSImage?
    var drawingRect: NSRect?
    var showDots = false
    var paths: [(NSBezierPath, DotType)] = []
    
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        // Drawing code here.
        guard let image = self.image,
              let drawingRect = self.drawingRect
        else {
            return
        }
        var rect = NSIntersectionRect(drawingRect, self.bounds)
        rect = NSIntegralRect(rect)
        image.draw(in: rect, from: rect, operation: .sourceOver, fraction: 1.0)
        if self.showDots {
            self.drawDots(rect: rect)
        }
    }
    
    private func drawDots(rect: NSRect) {
        let radius: CGFloat = 1.5
        let dots = self.getDotsCoord(with: rect, radius: radius)
        self.paths = []
        for (dot, type) in dots {
            let path = NSBezierPath(ovalIn: NSRect(origin: dot, size: CGSize(width: radius*2, height: radius*2)))
            self.paths.append((path, type))
            path.lineWidth = 3
            NSColor.white.set()
            path.stroke()
        }
    }
    
    func getDotsCoord(with rect:NSRect, radius: CGFloat) -> [(NSPoint, DotType)] {
        let width = rect.width
        let height = rect.height
        let origin = rect.offsetBy(dx: -radius, dy: -radius).origin
        let dots: [(NSPoint, DotType)] = [
            (origin, .corner),
            (origin.offsetBy(dx: width/2, dy: 0), .bottom),
            (origin.offsetBy(dx: 0, dy: height/2), .left),
            (origin.offsetBy(dx: width, dy: 0), .corner),
            (origin.offsetBy(dx: 0, dy: height), .corner),
            (origin.offsetBy(dx: width, dy: height/2), .right),
            (origin.offsetBy(dx: width/2, dy: height), .top),
            (origin.offsetBy(dx: width, dy: height), .corner)
        ].map({
            (point, type) in
            return (NSPoint(x: Int(point.x), y: Int(point.y)), type)
        })
        return dots
    }
    
}

绘制背景


import Cocoa

class BackView: NSView {

    var image: NSImage?

    override func draw(_ dirtyRect: NSRect) {
        
        super.draw(dirtyRect)

        // Drawing code here.
        guard let image = self.image,
              let window = self.window
        else {
            return
        }
        
        let originFrame = NSRect(origin: .zero, size: window.frame.size)
        image.draw(in: originFrame, from: originFrame, operation: .sourceOver, fraction: 0.5)

    }
    
}