diff --git a/SilicaViewer/Document.swift b/SilicaViewer/Document.swift index 8227cbb..1225d22 100644 --- a/SilicaViewer/Document.swift +++ b/SilicaViewer/Document.swift @@ -18,6 +18,7 @@ struct SilicaLayerData { } struct SilicaLayer { + var name: String = "" var data: SilicaLayerData = SilicaLayerData() var mask: SilicaLayerData? var clipped: Bool = false @@ -33,6 +34,9 @@ struct SilicaDocument { var authorName: String = "" var strokeCount: Int = 0 + var backgroundColor: CGColor = .white + var colorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB() + var width: Int = 0 var height: Int = 0 @@ -131,7 +135,16 @@ class Document: NSDocument { if getDocumentClassName(dict: dict) == LayerClassName { 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 UUIDClassID = getClassID(id: UUIDKey) let UUIDClass = objectsArray[UUIDClassID] as! NSString @@ -198,7 +211,7 @@ class Document: NSDocument { let imageData = Data(bytes: uncompressedMemory, count: byteSize) let render: CGColorRenderingIntent = .defaultIntent - var rgbColorSpace = CGColorSpaceCreateDeviceRGB() + var rgbColorSpace = info.colorSpace if isMask { rgbColorSpace = CGColorSpaceCreateDeviceGray() @@ -241,6 +254,30 @@ class Document: NSDocument { info.flippedHorizontally = (dict[FlippedHorizontallyKey] 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 strokeClassID = getClassID(id: strokeClassKey) let strokeCount = objectsArray[strokeClassID] as! NSNumber @@ -289,6 +326,8 @@ class Document: NSDocument { let array = layersClass["NS.objects"] as! NSArray + //dump(dict, indent: 5) + for object in array { let layerClassID = getClassID(id: object) let layerClass = objectsArray[layerClassID] as! NSDictionary @@ -345,19 +384,22 @@ class Document: NSDocument { func makeComposite() -> NSImage? { // create the final composite output image - let rgbColorSpace = CGColorSpaceCreateDeviceRGB() 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))) + + var masterImage = ccgContext?.makeImage() + let context = CIContext() + let size = CGRect(x: 0, y: 0, width: info.width, height: info.height) 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 - 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 maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big) @@ -367,93 +409,92 @@ class Document: NSDocument { maskContext?.setFillColor(.white) maskContext?.fill(CGRect(origin: .zero, size: CGSize(width: info.width, height: info.height))) - var op = CGBlendMode.sourceAtop - NSLog("----") - NSLog("blend Mode: %i", layer.data.blendMode) - NSLog("extend blend mode: %i", layer.data.extendedBlend) + var kernel: CIBlendKernel? = .sourceOver if layer.data.blendMode == 1 { - op = .multiply + kernel = .multiply } if layer.data.blendMode == 10 { - op = .colorBurn + kernel = .colorBurn } if layer.data.blendMode == 19 { - op = .darken + kernel = .darken } if layer.data.blendMode == 8 { - //op = .linearBurn + kernel = .linearBurn } if layer.data.blendMode == 4 { - op = .lighten + kernel = .lighten } if layer.data.blendMode == 2 { - op = .screen + kernel = .screen } if layer.data.blendMode == 13 { - op = .hardLight + kernel = .hardLight } if layer.data.blendMode == 9 { - op = .colorDodge + kernel = .colorDodge } if layer.data.blendMode == 3 { - op = .plusLighter + kernel = .subtract } if layer.data.blendMode == 0 && layer.data.blendMode != layer.data.extendedBlend { if layer.data.extendedBlend == 25 { - //op = .darkerColor + kernel = .darkerColor } if layer.data.extendedBlend == 24 { - //op = .lighterColor + kernel = .lighterColor } if layer.data.extendedBlend == 21 { - //op = .vividLight + kernel = .vividLight } if layer.data.extendedBlend == 22 { - //op = .linearLight + kernel = .linearLight } if layer.data.extendedBlend == 23 { - //op = .pinLight + kernel = .pinLight } if layer.data.extendedBlend == 20 { - //op = .hardMix + kernel = .hardMix } if layer.data.extendedBlend == 26 { - //op = .divide + kernel = .divide } } if layer.data.blendMode == 11 { - op = .overlay + kernel = .overlay } if layer.data.blendMode == 17 { - op = .softLight + kernel = .softLight } if layer.data.blendMode == 12 { - op = .hardLight + kernel = .hardLight } if layer.data.blendMode == 6 { - op = .difference + kernel = .difference } if layer.data.blendMode == 5 { - op = .exclusion + kernel = .exclusion } if layer.data.blendMode == 7 { - op = .plusDarker + kernel = .componentAdd } if layer.data.blendMode == 15 { - op = .hue + kernel = .hue } if layer.data.blendMode == 16 { - op = .saturation + kernel = .saturation } if layer.data.blendMode == 13 { - op = .color + kernel = .color } if layer.data.blendMode == 14 { - op = .luminosity + kernel = .luminosity } + Swift.print(layer.name + " is " + kernel!.name) + if layer.mask != nil { for chunk in layer.mask!.chunks { let x = chunk.x @@ -489,14 +530,11 @@ class Document: NSDocument { layerContext?.setBlendMode(.copy) 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())! - - ccgContext?.setAlpha(1.0) - ccgContext?.setBlendMode(op) if layer.clipped { guard let result = previousImage?.toGrayscale() else { @@ -506,24 +544,23 @@ class Document: NSDocument { 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 { + } else if layer.mask != nil { let maskImage = (maskContext?.makeImage())! let newImage = layerImage.masking(maskImage)! previousImage = newImage - - ccgContext?.draw(newImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height)) } else { 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 { image = image.imageRotatedByDegreess(degrees: 90)