1
Fork 0

Load proper color spaces, add extended blend mode support, and load layer names

This commit is contained in:
Joshua Goins 2021-09-27 11:31:19 -04:00
parent 93dc48c1ca
commit e457a4b3c4

View file

@ -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
@ -131,7 +135,16 @@ 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,14 +530,11 @@ 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 {
@ -506,24 +544,23 @@ class Document: NSDocument {
let newImage = layerImage.masking(result)! let newImage = layerImage.masking(result)!
previousImage = newImage previousImage = newImage
} else if layer.mask != nil {
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 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)