Load proper color spaces, add extended blend mode support, and load layer names
This commit is contained in:
parent
93dc48c1ca
commit
e457a4b3c4
1 changed files with 86 additions and 49 deletions
|
@ -18,6 +18,7 @@ struct SilicaLayerData {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SilicaLayer {
|
struct SilicaLayer {
|
||||||
|
var name: String = ""
|
||||||
var data: SilicaLayerData = SilicaLayerData()
|
var data: SilicaLayerData = SilicaLayerData()
|
||||||
var mask: SilicaLayerData?
|
var mask: SilicaLayerData?
|
||||||
var clipped: Bool = false
|
var clipped: Bool = false
|
||||||
|
@ -33,6 +34,9 @@ struct SilicaDocument {
|
||||||
var authorName: String = ""
|
var authorName: String = ""
|
||||||
var strokeCount: Int = 0
|
var strokeCount: Int = 0
|
||||||
|
|
||||||
|
var backgroundColor: CGColor = .white
|
||||||
|
var colorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
|
|
||||||
var width: Int = 0
|
var width: Int = 0
|
||||||
var height: Int = 0
|
var height: Int = 0
|
||||||
|
|
||||||
|
@ -132,6 +136,15 @@ class Document: NSDocument {
|
||||||
if getDocumentClassName(dict: dict) == LayerClassName {
|
if getDocumentClassName(dict: dict) == LayerClassName {
|
||||||
var layer = SilicaLayer()
|
var layer = SilicaLayer()
|
||||||
|
|
||||||
|
//dump(dict, indent: 5)
|
||||||
|
|
||||||
|
if let val = dict["name"] {
|
||||||
|
let NameClassID = getClassID(id: val)
|
||||||
|
let NameClass = objectsArray[NameClassID] as! NSString
|
||||||
|
|
||||||
|
layer.name = NameClass as String
|
||||||
|
}
|
||||||
|
|
||||||
let UUIDKey = dict["UUID"]
|
let UUIDKey = dict["UUID"]
|
||||||
let UUIDClassID = getClassID(id: UUIDKey)
|
let UUIDClassID = getClassID(id: UUIDKey)
|
||||||
let UUIDClass = objectsArray[UUIDClassID] as! NSString
|
let UUIDClass = objectsArray[UUIDClassID] as! NSString
|
||||||
|
@ -198,7 +211,7 @@ class Document: NSDocument {
|
||||||
let imageData = Data(bytes: uncompressedMemory, count: byteSize)
|
let imageData = Data(bytes: uncompressedMemory, count: byteSize)
|
||||||
|
|
||||||
let render: CGColorRenderingIntent = .defaultIntent
|
let render: CGColorRenderingIntent = .defaultIntent
|
||||||
var rgbColorSpace = CGColorSpaceCreateDeviceRGB()
|
var rgbColorSpace = info.colorSpace
|
||||||
|
|
||||||
if isMask {
|
if isMask {
|
||||||
rgbColorSpace = CGColorSpaceCreateDeviceGray()
|
rgbColorSpace = CGColorSpaceCreateDeviceGray()
|
||||||
|
@ -241,6 +254,30 @@ class Document: NSDocument {
|
||||||
info.flippedHorizontally = (dict[FlippedHorizontallyKey] as! NSNumber).boolValue
|
info.flippedHorizontally = (dict[FlippedHorizontallyKey] as! NSNumber).boolValue
|
||||||
info.flippedVertically = (dict[FlippedVerticallyKey] as! NSNumber).boolValue
|
info.flippedVertically = (dict[FlippedVerticallyKey] as! NSNumber).boolValue
|
||||||
|
|
||||||
|
let colorProfileClassKey = dict["colorProfile"]
|
||||||
|
let colorProfileClassID = getClassID(id: colorProfileClassKey)
|
||||||
|
let colorProfile = objectsArray[colorProfileClassID] as! NSDictionary
|
||||||
|
|
||||||
|
let colorProfileNameClassKey = colorProfile["SiColorProfileArchiveICCNameKey"]
|
||||||
|
let colorProfileNameClassID = getClassID(id: colorProfileNameClassKey)
|
||||||
|
let colorProfileName = objectsArray[colorProfileNameClassID] as! NSString
|
||||||
|
|
||||||
|
// we only support the basic "Display P3" color space... does Procreate actually store the ICC data??
|
||||||
|
if colorProfileName == "Display P3" {
|
||||||
|
info.colorSpace = CGColorSpace(name: CGColorSpace.displayP3)!
|
||||||
|
}
|
||||||
|
|
||||||
|
let backgroundClassKey = dict["backgroundColor"]
|
||||||
|
let backgroundClassID = getClassID(id: backgroundClassKey)
|
||||||
|
let background = objectsArray[backgroundClassID] as! NSData
|
||||||
|
|
||||||
|
var backgroundArray: [Float] = [0.0, 0.0, 0.0, 0.0]
|
||||||
|
|
||||||
|
background.getBytes(&backgroundArray, length: 16)
|
||||||
|
let backgroundCgArray: [CGFloat] = [CGFloat(backgroundArray[0]), CGFloat(backgroundArray[1]), CGFloat(backgroundArray[2]), CGFloat(backgroundArray[3])]
|
||||||
|
|
||||||
|
info.backgroundColor = CGColor(colorSpace: info.colorSpace, components: backgroundCgArray)!
|
||||||
|
|
||||||
let strokeClassKey = dict[StrokeCountKey]
|
let strokeClassKey = dict[StrokeCountKey]
|
||||||
let strokeClassID = getClassID(id: strokeClassKey)
|
let strokeClassID = getClassID(id: strokeClassKey)
|
||||||
let strokeCount = objectsArray[strokeClassID] as! NSNumber
|
let strokeCount = objectsArray[strokeClassID] as! NSNumber
|
||||||
|
@ -289,6 +326,8 @@ class Document: NSDocument {
|
||||||
|
|
||||||
let array = layersClass["NS.objects"] as! NSArray
|
let array = layersClass["NS.objects"] as! NSArray
|
||||||
|
|
||||||
|
//dump(dict, indent: 5)
|
||||||
|
|
||||||
for object in array {
|
for object in array {
|
||||||
let layerClassID = getClassID(id: object)
|
let layerClassID = getClassID(id: object)
|
||||||
let layerClass = objectsArray[layerClassID] as! NSDictionary
|
let layerClass = objectsArray[layerClassID] as! NSDictionary
|
||||||
|
@ -345,19 +384,22 @@ class Document: NSDocument {
|
||||||
|
|
||||||
func makeComposite() -> NSImage? {
|
func makeComposite() -> NSImage? {
|
||||||
// create the final composite output image
|
// create the final composite output image
|
||||||
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
|
|
||||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
||||||
|
|
||||||
let ccgContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue)
|
let ccgContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: info.colorSpace, bitmapInfo: bitmapInfo.rawValue)
|
||||||
|
|
||||||
ccgContext?.setFillColor(.white)
|
ccgContext?.setFillColor(info.backgroundColor)
|
||||||
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)))
|
||||||
|
|
||||||
|
var masterImage = ccgContext?.makeImage()
|
||||||
|
let context = CIContext()
|
||||||
|
let size = CGRect(x: 0, y: 0, width: info.width, height: info.height)
|
||||||
|
|
||||||
var previousImage: CGImage?
|
var previousImage: CGImage?
|
||||||
|
|
||||||
for layer in info.layers.reversed() {
|
for layer in info.layers.reversed() {
|
||||||
// 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: info.colorSpace, bitmapInfo: bitmapInfo.rawValue)
|
||||||
|
|
||||||
let grayColorSpace = CGColorSpaceCreateDeviceGray()
|
let grayColorSpace = CGColorSpaceCreateDeviceGray()
|
||||||
let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big)
|
let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big)
|
||||||
|
@ -367,93 +409,92 @@ class Document: NSDocument {
|
||||||
maskContext?.setFillColor(.white)
|
maskContext?.setFillColor(.white)
|
||||||
maskContext?.fill(CGRect(origin: .zero, size: CGSize(width: info.width, height: info.height)))
|
maskContext?.fill(CGRect(origin: .zero, size: CGSize(width: info.width, height: info.height)))
|
||||||
|
|
||||||
var op = CGBlendMode.sourceAtop
|
var kernel: CIBlendKernel? = .sourceOver
|
||||||
NSLog("----")
|
|
||||||
NSLog("blend Mode: %i", layer.data.blendMode)
|
|
||||||
NSLog("extend blend mode: %i", layer.data.extendedBlend)
|
|
||||||
if layer.data.blendMode == 1 {
|
if layer.data.blendMode == 1 {
|
||||||
op = .multiply
|
kernel = .multiply
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 10 {
|
if layer.data.blendMode == 10 {
|
||||||
op = .colorBurn
|
kernel = .colorBurn
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 19 {
|
if layer.data.blendMode == 19 {
|
||||||
op = .darken
|
kernel = .darken
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 8 {
|
if layer.data.blendMode == 8 {
|
||||||
//op = .linearBurn
|
kernel = .linearBurn
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 4 {
|
if layer.data.blendMode == 4 {
|
||||||
op = .lighten
|
kernel = .lighten
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 2 {
|
if layer.data.blendMode == 2 {
|
||||||
op = .screen
|
kernel = .screen
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 13 {
|
if layer.data.blendMode == 13 {
|
||||||
op = .hardLight
|
kernel = .hardLight
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 9 {
|
if layer.data.blendMode == 9 {
|
||||||
op = .colorDodge
|
kernel = .colorDodge
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 3 {
|
if layer.data.blendMode == 3 {
|
||||||
op = .plusLighter
|
kernel = .subtract
|
||||||
}
|
}
|
||||||
|
|
||||||
if layer.data.blendMode == 0 && layer.data.blendMode != layer.data.extendedBlend {
|
if layer.data.blendMode == 0 && layer.data.blendMode != layer.data.extendedBlend {
|
||||||
if layer.data.extendedBlend == 25 {
|
if layer.data.extendedBlend == 25 {
|
||||||
//op = .darkerColor
|
kernel = .darkerColor
|
||||||
}
|
}
|
||||||
if layer.data.extendedBlend == 24 {
|
if layer.data.extendedBlend == 24 {
|
||||||
//op = .lighterColor
|
kernel = .lighterColor
|
||||||
}
|
}
|
||||||
if layer.data.extendedBlend == 21 {
|
if layer.data.extendedBlend == 21 {
|
||||||
//op = .vividLight
|
kernel = .vividLight
|
||||||
}
|
}
|
||||||
if layer.data.extendedBlend == 22 {
|
if layer.data.extendedBlend == 22 {
|
||||||
//op = .linearLight
|
kernel = .linearLight
|
||||||
}
|
}
|
||||||
if layer.data.extendedBlend == 23 {
|
if layer.data.extendedBlend == 23 {
|
||||||
//op = .pinLight
|
kernel = .pinLight
|
||||||
}
|
}
|
||||||
if layer.data.extendedBlend == 20 {
|
if layer.data.extendedBlend == 20 {
|
||||||
//op = .hardMix
|
kernel = .hardMix
|
||||||
}
|
}
|
||||||
if layer.data.extendedBlend == 26 {
|
if layer.data.extendedBlend == 26 {
|
||||||
//op = .divide
|
kernel = .divide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if layer.data.blendMode == 11 {
|
if layer.data.blendMode == 11 {
|
||||||
op = .overlay
|
kernel = .overlay
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 17 {
|
if layer.data.blendMode == 17 {
|
||||||
op = .softLight
|
kernel = .softLight
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 12 {
|
if layer.data.blendMode == 12 {
|
||||||
op = .hardLight
|
kernel = .hardLight
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 6 {
|
if layer.data.blendMode == 6 {
|
||||||
op = .difference
|
kernel = .difference
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 5 {
|
if layer.data.blendMode == 5 {
|
||||||
op = .exclusion
|
kernel = .exclusion
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 7 {
|
if layer.data.blendMode == 7 {
|
||||||
op = .plusDarker
|
kernel = .componentAdd
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 15 {
|
if layer.data.blendMode == 15 {
|
||||||
op = .hue
|
kernel = .hue
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 16 {
|
if layer.data.blendMode == 16 {
|
||||||
op = .saturation
|
kernel = .saturation
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 13 {
|
if layer.data.blendMode == 13 {
|
||||||
op = .color
|
kernel = .color
|
||||||
}
|
}
|
||||||
if layer.data.blendMode == 14 {
|
if layer.data.blendMode == 14 {
|
||||||
op = .luminosity
|
kernel = .luminosity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Swift.print(layer.name + " is " + kernel!.name)
|
||||||
|
|
||||||
if layer.mask != nil {
|
if layer.mask != nil {
|
||||||
for chunk in layer.mask!.chunks {
|
for chunk in layer.mask!.chunks {
|
||||||
let x = chunk.x
|
let x = chunk.x
|
||||||
|
@ -489,15 +530,12 @@ class Document: NSDocument {
|
||||||
layerContext?.setBlendMode(.copy)
|
layerContext?.setBlendMode(.copy)
|
||||||
|
|
||||||
if !layer.data.hidden {
|
if !layer.data.hidden {
|
||||||
layerContext?.draw(chunk.image.cgImage(forProposedRect: nil, context: NSGraphicsContext.current, hints: nil)!, in: rect)
|
layerContext?.draw(chunk.image.cgImage(forProposedRect: nil, context: NSGraphicsContext(cgContext: layerContext!, flipped: false), hints: nil)!, in: rect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let layerImage = (layerContext?.makeImage())!
|
let layerImage = (layerContext?.makeImage())!
|
||||||
|
|
||||||
ccgContext?.setAlpha(1.0)
|
|
||||||
ccgContext?.setBlendMode(op)
|
|
||||||
|
|
||||||
if layer.clipped {
|
if layer.clipped {
|
||||||
guard let result = previousImage?.toGrayscale() else {
|
guard let result = previousImage?.toGrayscale() else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -506,24 +544,23 @@ class Document: NSDocument {
|
||||||
let newImage = layerImage.masking(result)!
|
let newImage = layerImage.masking(result)!
|
||||||
|
|
||||||
previousImage = newImage
|
previousImage = newImage
|
||||||
|
|
||||||
ccgContext?.draw(newImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height))
|
|
||||||
} else if layer.mask != nil {
|
} 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
|
previousImage = newImage
|
||||||
|
|
||||||
ccgContext?.draw(newImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height))
|
|
||||||
} else {
|
} else {
|
||||||
previousImage = layerImage
|
previousImage = layerImage
|
||||||
|
|
||||||
ccgContext?.draw(layerImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply image
|
||||||
|
let ciImage = CIImage(cgImage: (masterImage)!)
|
||||||
|
let newCiImage = kernel!.apply(foreground: CIImage(cgImage: previousImage!), background: ciImage, colorSpace: info.colorSpace)
|
||||||
|
|
||||||
|
masterImage = context.createCGImage(newCiImage!, from: size, format: .RGBA8, colorSpace: info.colorSpace)!
|
||||||
}
|
}
|
||||||
|
|
||||||
var image = NSImage(cgImage: (ccgContext?.makeImage())!, size: NSSize(width: info.width, height: info.height))
|
var image = NSImage(cgImage: masterImage!, size: NSSize(width: info.width, height: info.height))
|
||||||
|
|
||||||
if info.orientation == 3 {
|
if info.orientation == 3 {
|
||||||
image = image.imageRotatedByDegreess(degrees: 90)
|
image = image.imageRotatedByDegreess(degrees: 90)
|
||||||
|
|
Reference in a new issue