Add clipping mask support
This commit is contained in:
parent
dd63ca0eba
commit
00b655f519
1 changed files with 111 additions and 4 deletions
|
@ -1,6 +1,7 @@
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import ZIPFoundation
|
import ZIPFoundation
|
||||||
import CoreFoundation
|
import CoreFoundation
|
||||||
|
import Accelerate
|
||||||
|
|
||||||
struct SilicaChunk {
|
struct SilicaChunk {
|
||||||
var x: Int = 0
|
var x: Int = 0
|
||||||
|
@ -18,6 +19,7 @@ struct SilicaLayerData {
|
||||||
struct SilicaLayer {
|
struct SilicaLayer {
|
||||||
var data: SilicaLayerData = SilicaLayerData()
|
var data: SilicaLayerData = SilicaLayerData()
|
||||||
var mask: SilicaLayerData?
|
var mask: SilicaLayerData?
|
||||||
|
var clipped: Bool = false
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SilicaDocument {
|
struct SilicaDocument {
|
||||||
|
@ -140,6 +142,9 @@ class Document: NSDocument {
|
||||||
layer.data.blendMode = (dict["blend"] as? NSNumber)!.intValue
|
layer.data.blendMode = (dict["blend"] as? NSNumber)!.intValue
|
||||||
layer.data.opacity = (dict["opacity"] as? NSNumber)!.doubleValue
|
layer.data.opacity = (dict["opacity"] as? NSNumber)!.doubleValue
|
||||||
layer.data.hidden = (dict["hidden"] as? Bool)!
|
layer.data.hidden = (dict["hidden"] as? Bool)!
|
||||||
|
layer.clipped = (dict["clipped"] as? Bool)!
|
||||||
|
|
||||||
|
dump(dict, indent: 2)
|
||||||
|
|
||||||
if maskClassID != 0 {
|
if maskClassID != 0 {
|
||||||
layer.mask = parseSilicaLayer(archive: archive, dict: maskClass as! NSDictionary, isMask: true)?.data
|
layer.mask = parseSilicaLayer(archive: archive, dict: maskClass as! NSDictionary, isMask: true)?.data
|
||||||
|
@ -240,7 +245,7 @@ class Document: NSDocument {
|
||||||
let strokeClassID = getClassID(id: strokeClassKey)
|
let strokeClassID = getClassID(id: strokeClassKey)
|
||||||
let strokeCount = objectsArray[strokeClassID] as! NSNumber
|
let strokeCount = objectsArray[strokeClassID] as! NSNumber
|
||||||
|
|
||||||
info.strokeCount = Int(strokeCount)
|
info.strokeCount = Int(truncating: strokeCount)
|
||||||
|
|
||||||
let nameClassKey = dict[NameKey]
|
let nameClassKey = dict[NameKey]
|
||||||
let nameClassID = getClassID(id: nameClassKey)
|
let nameClassID = getClassID(id: nameClassKey)
|
||||||
|
@ -348,7 +353,11 @@ class Document: NSDocument {
|
||||||
ccgContext?.setFillColor(.white)
|
ccgContext?.setFillColor(.white)
|
||||||
ccgContext?.fill(CGRect(origin: .zero, size: CGSize(width: info.width, height: info.height)))
|
ccgContext?.fill(CGRect(origin: .zero, size: CGSize(width: info.width, height: info.height)))
|
||||||
|
|
||||||
for layer in info.layers.reversed() {
|
var previousImage: CGImage?
|
||||||
|
|
||||||
|
for (index, layer) in info.layers.reversed().enumerated() {
|
||||||
|
dump(layer, indent: 5)
|
||||||
|
|
||||||
// start by creating a new layer composite image, needed for image masking
|
// start by creating a new layer composite image, needed for image masking
|
||||||
let layerContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue)
|
let layerContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue)
|
||||||
|
|
||||||
|
@ -409,14 +418,112 @@ class Document: NSDocument {
|
||||||
ccgContext?.setAlpha(1.0)
|
ccgContext?.setAlpha(1.0)
|
||||||
ccgContext?.setBlendMode(.sourceAtop)
|
ccgContext?.setBlendMode(.sourceAtop)
|
||||||
|
|
||||||
if layer.mask != nil {
|
if layer.clipped {
|
||||||
|
let previousLayer = info.layers.reversed()[index - 1]
|
||||||
|
|
||||||
|
var format: vImage_CGImageFormat = {
|
||||||
|
guard
|
||||||
|
let format = vImage_CGImageFormat(cgImage: previousImage!) else {
|
||||||
|
fatalError("Unable to create format.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return format
|
||||||
|
}()
|
||||||
|
|
||||||
|
var sourceBuffer: vImage_Buffer = {
|
||||||
|
guard
|
||||||
|
var sourceImageBuffer = try? vImage_Buffer(cgImage: previousImage!,
|
||||||
|
format: format),
|
||||||
|
|
||||||
|
var scaledBuffer = try? vImage_Buffer(width: Int(sourceImageBuffer.height / 3),
|
||||||
|
height: Int(sourceImageBuffer.width / 3),
|
||||||
|
bitsPerPixel: format.bitsPerPixel) else {
|
||||||
|
fatalError("Unable to create source buffers.")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer {
|
||||||
|
sourceImageBuffer.free()
|
||||||
|
}
|
||||||
|
|
||||||
|
vImageScale_ARGB8888(&sourceImageBuffer,
|
||||||
|
&scaledBuffer,
|
||||||
|
nil,
|
||||||
|
vImage_Flags(kvImageNoFlags))
|
||||||
|
|
||||||
|
return scaledBuffer
|
||||||
|
}()
|
||||||
|
|
||||||
|
/*
|
||||||
|
The 1-channel, 8-bit vImage buffer used as the operation destination.
|
||||||
|
*/
|
||||||
|
var destinationBuffer: vImage_Buffer = {
|
||||||
|
guard var destinationBuffer = try? vImage_Buffer(width: Int(sourceBuffer.width),
|
||||||
|
height: Int(sourceBuffer.height),
|
||||||
|
bitsPerPixel: 8) else {
|
||||||
|
fatalError("Unable to create destination buffers.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return destinationBuffer
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
let redCoefficient: Float = 0.2126
|
||||||
|
let greenCoefficient: Float = 0.7152
|
||||||
|
let blueCoefficient: Float = 0.0722
|
||||||
|
|
||||||
|
let divisor: Int32 = 0x1000
|
||||||
|
let fDivisor = Float(divisor)
|
||||||
|
|
||||||
|
var coefficientsMatrix = [
|
||||||
|
Int16(redCoefficient * fDivisor),
|
||||||
|
Int16(greenCoefficient * fDivisor),
|
||||||
|
Int16(blueCoefficient * fDivisor)
|
||||||
|
]
|
||||||
|
|
||||||
|
// Use the matrix of coefficients to compute the scalar luminance by
|
||||||
|
// returning the dot product of each RGB pixel and the coefficients
|
||||||
|
// matrix.
|
||||||
|
let preBias: [Int16] = [0, 0, 0, 0]
|
||||||
|
let postBias: Int32 = 0
|
||||||
|
|
||||||
|
vImageMatrixMultiply_ARGB8888ToPlanar8(&sourceBuffer,
|
||||||
|
&destinationBuffer,
|
||||||
|
&coefficientsMatrix,
|
||||||
|
divisor,
|
||||||
|
preBias,
|
||||||
|
postBias,
|
||||||
|
vImage_Flags(kvImageNoFlags))
|
||||||
|
|
||||||
|
// Create a 1-channel, 8-bit grayscale format that's used to
|
||||||
|
// generate a displayable image.
|
||||||
|
let monoFormat = vImage_CGImageFormat(
|
||||||
|
bitsPerComponent: 8,
|
||||||
|
bitsPerPixel: 8,
|
||||||
|
colorSpace: CGColorSpaceCreateDeviceGray(),
|
||||||
|
bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue),
|
||||||
|
renderingIntent: .defaultIntent)!
|
||||||
|
|
||||||
|
// Create a Core Graphics image from the grayscale destination buffer.
|
||||||
|
let result = (try? destinationBuffer.createCGImage(format: monoFormat))!
|
||||||
|
|
||||||
|
let newImage = layerImage.masking(result)!
|
||||||
|
|
||||||
|
previousImage = newImage
|
||||||
|
|
||||||
|
ccgContext?.draw(newImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height))
|
||||||
|
} else if layer.mask != nil {
|
||||||
let maskImage = (maskContext?.makeImage())!
|
let maskImage = (maskContext?.makeImage())!
|
||||||
let newImage = layerImage.masking(maskImage)!
|
let newImage = layerImage.masking(maskImage)!
|
||||||
|
|
||||||
|
previousImage = newImage
|
||||||
|
|
||||||
ccgContext?.draw(newImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height))
|
ccgContext?.draw(newImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height))
|
||||||
} else {
|
} else {
|
||||||
|
previousImage = layerImage
|
||||||
|
|
||||||
ccgContext?.draw(layerImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height))
|
ccgContext?.draw(layerImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var image = NSImage(cgImage: (ccgContext?.makeImage())!, size: NSSize(width: info.width, height: info.height))
|
var image = NSImage(cgImage: (ccgContext?.makeImage())!, size: NSSize(width: info.width, height: info.height))
|
||||||
|
|
Reference in a new issue