diff --git a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-1024.png b/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-1024.png deleted file mode 100644 index 7c6b1c0..0000000 Binary files a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-1024.png and /dev/null differ diff --git a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-128.png b/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-128.png deleted file mode 100644 index be3a762..0000000 Binary files a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-128.png and /dev/null differ diff --git a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-16.png b/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-16.png deleted file mode 100644 index 26ced9a..0000000 Binary files a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-16.png and /dev/null differ diff --git a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-256.png b/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-256.png deleted file mode 100644 index de363be..0000000 Binary files a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-256.png and /dev/null differ diff --git a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-32.png b/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-32.png deleted file mode 100644 index 18fdc35..0000000 Binary files a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-32.png and /dev/null differ diff --git a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-512.png b/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-512.png deleted file mode 100644 index 5444743..0000000 Binary files a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-512.png and /dev/null differ diff --git a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-64.png b/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-64.png deleted file mode 100644 index 035e453..0000000 Binary files a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/untitled-64.png and /dev/null differ diff --git a/ProcreateViewer/Document.swift b/ProcreateViewer/Document.swift deleted file mode 100644 index 7f3921e..0000000 --- a/ProcreateViewer/Document.swift +++ /dev/null @@ -1,393 +0,0 @@ -import Cocoa -import ZIPFoundation -import CoreFoundation - -struct SilicaChunk { - var x: Int = 0 - var y: Int = 0 - var image: NSImage = NSImage() -} - -struct SilicaLayer { - var chunks: [SilicaChunk] = [] -} - -struct SilicaDocument { - var trackedTime: Int = 0 - var tileSize: Int = 0 - var orientation: Int = 0 - var flippedHorizontally: Bool = false - var flippedVertically: Bool = false - - var width: Int = 0 - var height: Int = 0 - - var layers: [SilicaLayer] = [] -} - -// Since this is a C-function we have to unsafe cast... -func objectRefGetValue(_ objectRef : CFTypeRef) -> UInt32 { - return _CFKeyedArchiverUIDGetValue(unsafeBitCast(objectRef, to: CFKeyedArchiverUIDRef.self)) -} - -class Document: NSDocument { - var data: Data? // oh no... - - var dict: NSDictionary? - - var info = SilicaDocument() - - var rows: Int = 0 - var columns: Int = 0 - - var remainderWidth: Int = 0 - var remainderHeight: Int = 0 - - override init() { - super.init() - } - - override func makeWindowControllers() { - let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil) - let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! NSWindowController - self.addWindowController(windowController) - } - - /* - Pass in an object from the $object array, which always contains a $class key. - */ - func getDocumentClassName(dict: NSDictionary) -> String? { - let objectsArray = self.dict?["$objects"] as! NSArray - - if let value = dict["$class"] { - let classObjectId = objectRefGetValue(value as CFTypeRef) - let classObject = objectsArray[Int(classObjectId)] as! NSDictionary - - return classObject["$classname"] as? String - } - - return nil - } - - /* - Returns the correct tile size, taking into account the remainder between tile size and image size. - */ - func getTileSize(x: Int, y: Int) -> (Int, Int) { - var width: Int = info.tileSize - var height: Int = info.tileSize - - if((x + 1) == columns) { - width = info.tileSize - remainderWidth - } - - if(y == rows) { - height = info.tileSize - remainderHeight - } - - return (width, height) - } - - /* - Converts a CFKeyedArchiveUID from a NSKeyedArchive to a Int for indexing an array or dictionary. - */ - func getClassID(id: Any?) -> Int { - return Int(objectRefGetValue(id! as CFTypeRef)) - } - - /* - Parses the chunk filename, ex. 1~1 to integer coordinates. - */ - func parseChunkFilename(filename: String) -> (Int, Int)? { - let pathURL = URL(fileURLWithPath: filename) - let pathComponents = pathURL.lastPathComponent.replacingOccurrences(of: ".chunk", with: "").components(separatedBy: "~") - - let x = Int(pathComponents[0]) - let y = Int(pathComponents[1]) - - if x != nil && y != nil { - return (x!, y! + 1) - } else { - return nil - } - } - - func parseSilicaLayer(archive: Archive, dict: NSDictionary) { - let objectsArray = self.dict?["$objects"] as! NSArray - - if getDocumentClassName(dict: dict) == LayerClassName { - var layer = SilicaLayer() - - let UUIDKey = dict["UUID"] - let UUIDClassID = getClassID(id: UUIDKey) - let UUIDClass = objectsArray[UUIDClassID] as! NSString - - var chunkPaths: [String] = [] - - archive.forEach { (entry: Entry) in - if entry.path.contains(String(UUIDClass)) { - chunkPaths.append(entry.path) - } - } - - layer.chunks = Array(repeating: SilicaChunk(), count: chunkPaths.count) - - let dispatchGroup = DispatchGroup() - let queue = DispatchQueue(label: "imageWork") - - DispatchQueue.concurrentPerform(iterations: chunkPaths.count) { (i: Int) in - dispatchGroup.enter() - - guard let threadArchive = Archive(data: self.data!, accessMode: Archive.AccessMode.read) else { - return - } - - let threadEntry = threadArchive[chunkPaths[i]] - - guard let (x, y) = parseChunkFilename(filename: threadEntry!.path) else { - return - } - - let (width, height) = getTileSize(x: x, y: y) - let byteSize = width * height * 4 - - let uncompressedMemory = UnsafeMutablePointer.allocate(capacity: byteSize) - - guard let lzoData = readData(archive: threadArchive, entry: threadEntry!) else { - return - } - - lzoData.withUnsafeBytes({ (bytes: UnsafeRawBufferPointer) -> Void in - var len = lzo_uint(byteSize) - - lzo1x_decompress_safe(bytes.baseAddress!.assumingMemoryBound(to: uint8.self), lzo_uint(lzoData.count), uncompressedMemory, &len, nil) - }) - - let imageData = Data(bytes: uncompressedMemory, count: byteSize) - - let render: CGColorRenderingIntent = .defaultIntent - let rgbColorSpace = CGColorSpaceCreateDeviceRGB() - let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue).union(.byteOrder32Big) - let providerRef: CGDataProvider? = CGDataProvider(data: imageData as CFData) - - guard let cgimage = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: width * 4, space: rgbColorSpace, bitmapInfo: bitmapInfo, provider: providerRef!, decode: nil, shouldInterpolate: false, intent: render) else { - return - } - - let image = NSImage(cgImage: cgimage, size: NSZeroSize) - - queue.async(flags: .barrier) { - layer.chunks[i].image = image - layer.chunks[i].x = x - layer.chunks[i].y = y - - dispatchGroup.leave() - } - } - - dispatchGroup.wait() - - info.layers.append(layer) - } - } - - func parseSilicaDocument(archive: Archive, dict: NSDictionary) { - let objectsArray = self.dict?["$objects"] as! NSArray - - if getDocumentClassName(dict: dict) == DocumentClassName { - info.trackedTime = (dict[TrackedTimeKey] as! NSNumber).intValue - info.tileSize = (dict[TileSizeKey] as! NSNumber).intValue - info.orientation = (dict[OrientationKey] as! NSNumber).intValue - info.flippedHorizontally = (dict[FlippedHorizontallyKey] as! NSNumber).boolValue - info.flippedVertically = (dict[FlippedVerticallyKey] as! NSNumber).boolValue - - let sizeClassKey = dict[SizeKey] - let sizeClassID = getClassID(id: sizeClassKey) - let sizeString = objectsArray[sizeClassID] as! String - - let sizeComponents = sizeString.replacingOccurrences(of: "{", with: "").replacingOccurrences(of: "}", with: "").components(separatedBy: ", ") - let width = Int(sizeComponents[0]) - let height = Int(sizeComponents[1]) - - info.width = width! - info.height = height! - - columns = Int(ceil(Float(info.width) / Float(info.tileSize))) - rows = Int(ceil(Float(info.height) / Float(info.tileSize))) - - if info.width % info.tileSize != 0 { - remainderWidth = (columns * info.tileSize) - info.width - } - - if info.height % info.tileSize != 0 { - remainderHeight = (rows * info.tileSize) - info.height - } - - let layersClassKey = dict[LayersKey] - let layersClassID = getClassID(id: layersClassKey) - let layersClass = objectsArray[layersClassID] as! NSDictionary - - let array = layersClass["NS.objects"] as! NSArray - - for object in array { - let layerClassID = getClassID(id: object) - let layerClass = objectsArray[layerClassID] as! NSDictionary - - parseSilicaLayer(archive: archive, dict: layerClass) - } - } - } - - func parseDocument(archive: Archive, dict: NSDictionary) { - // double check if this archive is really correct - if let value = dict["$version"] { - if (value as! Int) != NSKeyedArchiveVersion { - Swift.print("This is not a valid document!") - return - } - - self.dict = dict - - let objectsArray = dict["$objects"] as! NSArray - - // let's read the $top class, which is always going to be SilicaDocument type. - let topObject = dict["$top"] as! NSDictionary - let topClassID = objectRefGetValue(topObject["root"] as CFTypeRef) - let topObjectClass = objectsArray[Int(topClassID)] as! NSDictionary - - parseSilicaDocument(archive: archive, dict: topObjectClass) - } - } - - override func read(from data: Data, ofType typeName: String) throws { - self.data = data - - guard let archive = Archive(data: data, accessMode: Archive.AccessMode.read) else { - return - } - - guard let documentEntry = archive[DocumentArchivePath] else { - return - } - - guard let documentData = readData(archive: archive, entry: documentEntry) else { - return - } - - var plistFormat = PropertyListSerialization.PropertyListFormat.binary - guard let propertyList = try? PropertyListSerialization.propertyList(from: documentData, options: [], format: &plistFormat) else { - return - } - - parseDocument(archive: archive, dict: propertyList as! NSDictionary) - } - - func makeComposite() -> NSImage { - var image = NSImage(size: NSSize(width: info.width, height: info.height)) - image.lockFocus() - - let color = NSColor.white - color.drawSwatch(in: NSRect(origin: .zero, size: image.size)) - - for layer in info.layers.reversed() { - for chunk in layer.chunks { - let x = chunk.x - var y = chunk.y - - let (width, height) = getTileSize(x: x, y: y) - - if y == rows { - y = 0 - } - - let rect = NSRect(x: info.tileSize * x, y: info.height - (info.tileSize * y), width: width, height: height) - - chunk.image.draw(in: rect) - } - } - - image.unlockFocus() - - if info.orientation == 3 { - image = image.imageRotatedByDegreess(degrees: 90) - } else if info.orientation == 4 { - image = image.imageRotatedByDegreess(degrees: -90) - } - - if info.flippedHorizontally && (info.orientation == 1 || info.orientation == 2) { - image = image.flipHorizontally() - } else if info.flippedHorizontally && (info.orientation == 3 || info.orientation == 4) { - image = image.flipVertically() - } else if info.flippedVertically && (info.orientation == 1 || info.orientation == 2) { - image = image.flipVertically() - } else if !info.flippedVertically && (info.orientation == 3 || info.orientation == 4) { - image = image.flipHorizontally() - } - - return image - } -} - -public extension NSImage { - func imageRotatedByDegreess(degrees:CGFloat) -> NSImage { - var imageBounds = NSMakeRect(0.0, 0.0, size.width, size.height) - - let pathBounds = NSBezierPath(rect: imageBounds) - var transform = NSAffineTransform() - transform.rotate(byDegrees: degrees) - pathBounds.transform(using: transform as AffineTransform) - - let rotatedBounds:NSRect = NSMakeRect(NSZeroPoint.x, NSZeroPoint.y, pathBounds.bounds.size.width, pathBounds.bounds.size.height ) - let rotatedImage = NSImage(size: rotatedBounds.size) - - imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth(imageBounds) / 2) - imageBounds.origin.y = NSMidY(rotatedBounds) - (NSHeight(imageBounds) / 2) - - transform = NSAffineTransform() - transform.translateX(by: +(NSWidth(rotatedBounds) / 2 ), yBy: +(NSHeight(rotatedBounds) / 2)) - transform.rotate(byDegrees: degrees) - transform.translateX(by: -(NSWidth(rotatedBounds) / 2 ), yBy: -(NSHeight(rotatedBounds) / 2)) - - rotatedImage.lockFocus() - transform.concat() - - self.draw(in: imageBounds, from: .zero, operation: .copy, fraction: 1.0) - - rotatedImage.unlockFocus() - - return rotatedImage - } - - func flipHorizontally() -> NSImage { - let flipedImage = NSImage(size: size) - flipedImage.lockFocus() - - let transform = NSAffineTransform() - transform.translateX(by: size.width, yBy: 0.0) - transform.scaleX(by: -1.0, yBy: 1.0) - transform.concat() - - let rect = NSMakeRect(0, 0, size.width, size.height) - self.draw(at: .zero, from: rect, operation: .sourceOver, fraction: 1.0) - - flipedImage.unlockFocus() - - return flipedImage - } - - func flipVertically() -> NSImage { - let flipedImage = NSImage(size: size) - flipedImage.lockFocus() - - let transform = NSAffineTransform() - transform.translateX(by: 0.0, yBy: size.height) - transform.scaleX(by: 1.0, yBy: -1.0) - transform.concat() - - let rect = NSMakeRect(0, 0, size.width, size.height) - self.draw(at: .zero, from: rect, operation: .sourceOver, fraction: 1.0) - - flipedImage.unlockFocus() - - return flipedImage - } -} diff --git a/ProcreateViewer/ProcreateViewer-Bridging-Header.h b/ProcreateViewer/ProcreateViewer-Bridging-Header.h deleted file mode 100644 index 1a593af..0000000 --- a/ProcreateViewer/ProcreateViewer-Bridging-Header.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - Here we import Apple's functions to decode __CFKeyedArchiverUID types. - */ -#include -#include "minilzo.h" - -typedef const struct __CFKeyedArchiverUID * CFKeyedArchiverUIDRef; - -extern uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid); diff --git a/ProcreateViewer/TimelapseViewController.swift b/ProcreateViewer/TimelapseViewController.swift deleted file mode 100644 index 489a7e0..0000000 --- a/ProcreateViewer/TimelapseViewController.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation -import Cocoa -import AVKit -import AVFoundation -import ZIPFoundation - -class TimelapseViewController: NSViewController { - var document: Document? - - @IBOutlet weak var playerView: AVPlayerView! - - override func viewWillAppear() { - super.viewDidAppear() - - guard let archive = Archive(data: (document?.data)!, accessMode: Archive.AccessMode.read) else { - return - } - - let directory = NSTemporaryDirectory() - - var entries: [AVPlayerItem] = [] - for entry in archive.makeIterator() { - if entry.path.contains(VideoPath) { - let fileName = NSUUID().uuidString + ".mp4" - - // This returns a URL? even though it is an NSURL class method - let fullURL = NSURL.fileURL(withPathComponents: [directory, fileName])! - - try? archive.extract(entry, to: fullURL) - - entries.append(AVPlayerItem(url: fullURL)) - } - } - - playerView?.player = AVQueuePlayer(items: entries) - } -} diff --git a/Quicklook/Info.plist b/Quicklook/Info.plist index f7e3046..9bd301e 100644 --- a/Quicklook/Info.plist +++ b/Quicklook/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSExtension @@ -31,7 +31,7 @@ com.procreate QLSupportsSearchableItems - + NSExtensionPointIdentifier com.apple.quicklook.preview diff --git a/README.md b/README.md index 14e68d2..d8ac055 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Procreate Viewer +# Silica Viewer This is a macOS app to view [Procreate](https://procreate.art) documents, and it also contains [QuickLook](https://support.apple.com/guide/mac-help/preview-files-with-quick-look-mh14119/mac) and thumbnail extensions to allow you to quickly preview your files as well! @@ -41,5 +41,3 @@ Procreate, just like with thumbnails and image data - continue to use standard f ### Document Data Layer names, time spent and other data is located in `Document.archive`. This is the only hard-to-read file in Procreate documents, but it is a [NSKeyedArchive](https://developer.apple.com/documentation/foundation/nskeyedarchiver). Here, we just use the [PropertyListSerialization](https://developer.apple.com/documentation/foundation/propertylistserialization) object to decode this in Swift. - -If you want more information please read [#2](https://github.com/redstrate/procreate-viewer/issues/2) where I break down the format of this file in more detail. diff --git a/ProcreateViewer.xcodeproj/project.pbxproj b/SilicaViewer.xcodeproj/project.pbxproj similarity index 82% rename from ProcreateViewer.xcodeproj/project.pbxproj rename to SilicaViewer.xcodeproj/project.pbxproj index 521763b..afbe471 100644 --- a/ProcreateViewer.xcodeproj/project.pbxproj +++ b/SilicaViewer.xcodeproj/project.pbxproj @@ -7,15 +7,10 @@ objects = { /* Begin PBXBuildFile section */ - 030F6FF22415C5E300A43F01 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F6FF12415C5E300A43F01 /* AppDelegate.swift */; }; - 030F6FF42415C5E300A43F01 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F6FF32415C5E300A43F01 /* Document.swift */; }; - 030F6FF92415C5E400A43F01 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 030F6FF82415C5E400A43F01 /* Assets.xcassets */; }; 030F700B2415C6B500A43F01 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 030F700A2415C6B500A43F01 /* Quartz.framework */; }; 030F700E2415C6B500A43F01 /* PreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F700D2415C6B500A43F01 /* PreviewViewController.swift */; }; 030F70112415C6B500A43F01 /* PreviewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 030F700F2415C6B500A43F01 /* PreviewViewController.xib */; }; 030F70162415C6B500A43F01 /* QuickLook.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 030F70082415C6B500A43F01 /* QuickLook.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - 0357A94D26F9C7370075D5BC /* minilzo.c in Sources */ = {isa = PBXBuildFile; fileRef = 0357A94A26F9C7370075D5BC /* minilzo.c */; }; - 0357A94E26F9C7370075D5BC /* lzoconf.h in Headers */ = {isa = PBXBuildFile; fileRef = 0357A94C26F9C7370075D5BC /* lzoconf.h */; }; 035D1A0426F0927200B332BE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D19F826F0927200B332BE /* ViewController.swift */; }; 035D1A0526F0927200B332BE /* cbridge.c in Sources */ = {isa = PBXBuildFile; fileRef = 035D19FA26F0927200B332BE /* cbridge.c */; }; 035D1A0626F0927200B332BE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 035D19FB26F0927200B332BE /* Assets.xcassets */; }; @@ -25,19 +20,16 @@ 035D1A0A26F0927200B332BE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D1A0126F0927200B332BE /* AppDelegate.swift */; }; 035D1A0B26F0927200B332BE /* TimelapseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D1A0226F0927200B332BE /* TimelapseViewController.swift */; }; 036AFBB8241687680075400A /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 036AFBB7241687680075400A /* ZIPFoundation */; }; - 036AFBBA24168C030075400A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036AFBB924168C030075400A /* ViewController.swift */; }; - 036AFBE32417F0A00075400A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 036AFBE12417F0A00075400A /* Main.storyboard */; }; 036AFC062417F2990075400A /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 036AFC052417F2990075400A /* ZIPFoundation */; }; 036AFC0D241800350075400A /* QuickLookThumbnailing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 036AFC0C241800350075400A /* QuickLookThumbnailing.framework */; }; 036AFC0E241800350075400A /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 030F700A2415C6B500A43F01 /* Quartz.framework */; }; 036AFC11241800350075400A /* ThumbnailProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036AFC10241800350075400A /* ThumbnailProvider.swift */; }; 036AFC16241800350075400A /* Thumbnail.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 036AFC0B241800350075400A /* Thumbnail.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 036AFC1B241800850075400A /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 036AFC1A241800850075400A /* ZIPFoundation */; }; - 0371996027BAC5D800EE1DFD /* Silica_ViewerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0371995F27BAC5D800EE1DFD /* Silica_ViewerTests.swift */; }; - 03C39DE726F90734005555AE /* lzodefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 03C39DE226F90733005555AE /* lzodefs.h */; }; - 03C39DE826F90734005555AE /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = 03C39DE426F90734005555AE /* Makefile */; }; - 03C39DE926F90734005555AE /* testmini.c in Sources */ = {isa = PBXBuildFile; fileRef = 03C39DE526F90734005555AE /* testmini.c */; }; - 03C39DEA26F90734005555AE /* minilzo.h in Headers */ = {isa = PBXBuildFile; fileRef = 03C39DE626F90734005555AE /* minilzo.h */; }; +==== BASE ==== + 037B4042241821D200392452 /* InfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037B4041241821D200392452 /* InfoViewController.swift */; }; + 03CB382424191F620078B3E5 /* cbridge.c in Sources */ = {isa = PBXBuildFile; fileRef = 03CB382324191F620078B3E5 /* cbridge.c */; }; +==== BASE ==== 03CB383B2419CA2D0078B3E5 /* libLZO.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 03CB382E2419C9DB0078B3E5 /* libLZO.a */; }; 03CB3840241A5AED0078B3E5 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CB383F241A5AED0078B3E5 /* Shared.swift */; }; 03CB3841241A5AED0078B3E5 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CB383F241A5AED0078B3E5 /* Shared.swift */; }; @@ -63,13 +55,6 @@ remoteGlobalIDString = 036AFC0A241800350075400A; remoteInfo = Thumbnail; }; - 0371996127BAC5D800EE1DFD /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 030F6FE62415C5E300A43F01 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 030F6FED2415C5E300A43F01; - remoteInfo = SilicaViewer; - }; 03CB383C2419CA2D0078B3E5 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 030F6FE62415C5E300A43F01 /* Project object */; @@ -102,10 +87,6 @@ 030F70102415C6B500A43F01 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PreviewViewController.xib; sourceTree = ""; }; 030F70122415C6B500A43F01 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 030F70132415C6B500A43F01 /* Quicklook.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Quicklook.entitlements; sourceTree = ""; }; - 0357A94926F9C7370075D5BC /* README.LZO */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.LZO; sourceTree = ""; }; - 0357A94A26F9C7370075D5BC /* minilzo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = minilzo.c; sourceTree = ""; }; - 0357A94B26F9C7370075D5BC /* testmini */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = testmini; sourceTree = ""; }; - 0357A94C26F9C7370075D5BC /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = ""; }; 035D19F826F0927200B332BE /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 035D19F926F0927200B332BE /* SilicaViewer.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = SilicaViewer.entitlements; sourceTree = ""; }; 035D19FA26F0927200B332BE /* cbridge.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cbridge.c; sourceTree = ""; }; @@ -122,14 +103,11 @@ 036AFC10241800350075400A /* ThumbnailProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailProvider.swift; sourceTree = ""; }; 036AFC12241800350075400A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 036AFC13241800350075400A /* Thumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Thumbnail.entitlements; sourceTree = ""; }; - 0371995827BAC52900EE1DFD /* SilicaViewer.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = SilicaViewer.xctestplan; sourceTree = ""; }; - 0371995D27BAC5D800EE1DFD /* Silica ViewerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Silica ViewerTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 0371995F27BAC5D800EE1DFD /* Silica_ViewerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Silica_ViewerTests.swift; sourceTree = ""; }; - 03C39DE226F90733005555AE /* lzodefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzodefs.h; sourceTree = ""; }; - 03C39DE326F90733005555AE /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYING; sourceTree = ""; }; - 03C39DE426F90734005555AE /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; - 03C39DE526F90734005555AE /* testmini.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testmini.c; sourceTree = ""; }; - 03C39DE626F90734005555AE /* minilzo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minilzo.h; sourceTree = ""; }; +==== BASE ==== + 037B4041241821D200392452 /* InfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoViewController.swift; sourceTree = ""; }; + 03CB382224191F610078B3E5 /* ProcreateViewer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ProcreateViewer-Bridging-Header.h"; sourceTree = ""; }; + 03CB382324191F620078B3E5 /* cbridge.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cbridge.c; sourceTree = ""; }; +==== BASE ==== 03CB382E2419C9DB0078B3E5 /* libLZO.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLZO.a; sourceTree = BUILT_PRODUCTS_DIR; }; 03CB383F241A5AED0078B3E5 /* Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = ""; }; 03CB3843241A5D600078B3E5 /* minilzo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minilzo.h; path = "Dependencies/minilzo-2.10/minilzo.h"; sourceTree = SOURCE_ROOT; }; @@ -167,13 +145,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 0371995A27BAC5D800EE1DFD /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 03CB382C2419C9DB0078B3E5 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -187,15 +158,11 @@ 030F6FE52415C5E300A43F01 = { isa = PBXGroup; children = ( - 0371995827BAC52900EE1DFD /* SilicaViewer.xctestplan */, - 0357A94826F9C71E0075D5BC /* LZO */, 035D19F726F0927200B332BE /* SilicaViewer */, 03CB383E241A5ACD0078B3E5 /* Shared */, 03CB38322419C9F80078B3E5 /* LZO */, - 030F6FF02415C5E300A43F01 /* ProcreateViewer */, 030F700C2415C6B500A43F01 /* Quicklook */, 036AFC0F241800350075400A /* Thumbnail */, - 0371995E27BAC5D800EE1DFD /* Silica ViewerTests */, 030F70092415C6B500A43F01 /* Frameworks */, 030F6FEF2415C5E300A43F01 /* Products */, ); @@ -208,29 +175,10 @@ 030F70082415C6B500A43F01 /* QuickLook.appex */, 036AFC0B241800350075400A /* Thumbnail.appex */, 03CB382E2419C9DB0078B3E5 /* libLZO.a */, - 0371995D27BAC5D800EE1DFD /* Silica ViewerTests.xctest */, ); name = Products; sourceTree = ""; }; - 030F6FF02415C5E300A43F01 /* ProcreateViewer */ = { - isa = PBXGroup; - children = ( - 036AFBE12417F0A00075400A /* Main.storyboard */, - 030F6FF12415C5E300A43F01 /* AppDelegate.swift */, - 030F6FF32415C5E300A43F01 /* Document.swift */, - 030F6FF82415C5E400A43F01 /* Assets.xcassets */, - 030F6FFD2415C5E400A43F01 /* Info.plist */, - 030F6FFE2415C5E400A43F01 /* ProcreateViewer.entitlements */, - 036AFBB924168C030075400A /* ViewController.swift */, - 037B4041241821D200392452 /* InfoViewController.swift */, - 03CB382324191F620078B3E5 /* cbridge.c */, - 03CB382224191F610078B3E5 /* ProcreateViewer-Bridging-Header.h */, - 033F94F82648B8E200099FB7 /* TimelapseViewController.swift */, - ); - path = ProcreateViewer; - sourceTree = ""; - }; 030F70092415C6B500A43F01 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -251,22 +199,6 @@ path = Quicklook; sourceTree = ""; }; - 0357A94826F9C71E0075D5BC /* LZO */ = { - isa = PBXGroup; - children = ( - 0357A94C26F9C7370075D5BC /* lzoconf.h */, - 0357A94A26F9C7370075D5BC /* minilzo.c */, - 0357A94926F9C7370075D5BC /* README.LZO */, - 0357A94B26F9C7370075D5BC /* testmini */, - 03C39DE326F90733005555AE /* COPYING */, - 03C39DE226F90733005555AE /* lzodefs.h */, - 03C39DE426F90734005555AE /* Makefile */, - 03C39DE626F90734005555AE /* minilzo.h */, - 03C39DE526F90734005555AE /* testmini.c */, - ); - name = LZO; - sourceTree = ""; - }; 035D19F726F0927200B332BE /* SilicaViewer */ = { isa = PBXGroup; children = ( @@ -295,12 +227,15 @@ path = Thumbnail; sourceTree = ""; }; - 0371995E27BAC5D800EE1DFD /* Silica ViewerTests */ = { + 03CB38322419C9F80078B3E5 /* LZO */ = { isa = PBXGroup; children = ( - 0371995F27BAC5D800EE1DFD /* Silica_ViewerTests.swift */, + 03CB3846241A5D600078B3E5 /* lzoconf.h */, + 03CB3844241A5D600078B3E5 /* lzodefs.h */, + 03CB3845241A5D600078B3E5 /* minilzo.c */, + 03CB3843241A5D600078B3E5 /* minilzo.h */, ); - path = "Silica ViewerTests"; + path = LZO; sourceTree = ""; }; 03CB383E241A5ACD0078B3E5 /* Shared */ = { @@ -318,9 +253,9 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 03C39DE726F90734005555AE /* lzodefs.h in Headers */, - 0357A94E26F9C7370075D5BC /* lzoconf.h in Headers */, - 03C39DEA26F90734005555AE /* minilzo.h in Headers */, + 03CB3848241A5D600078B3E5 /* lzodefs.h in Headers */, + 03CB384A241A5D600078B3E5 /* lzoconf.h in Headers */, + 03CB3847241A5D600078B3E5 /* minilzo.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -435,7 +370,6 @@ ==== BASE ==== LastSwiftUpdateCheck = 1130; LastUpgradeCheck = 1250; -==== BASE ==== ORGANIZATIONNAME = Josh; TargetAttributes = { 030F6FED2415C5E300A43F01 = { @@ -457,7 +391,7 @@ }; }; }; - buildConfigurationList = 030F6FE92415C5E300A43F01 /* Build configuration list for PBXProject "ProcreateViewer" */; + buildConfigurationList = 030F6FE92415C5E300A43F01 /* Build configuration list for PBXProject "SilicaViewer" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; @@ -473,7 +407,7 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 030F6FED2415C5E300A43F01 /* ProcreateViewer */, + 030F6FED2415C5E300A43F01 /* SilicaViewer */, 030F70072415C6B500A43F01 /* QuickLook */, 036AFC0A241800350075400A /* Thumbnail */, 03CB382D2419C9DB0078B3E5 /* LZO */, @@ -487,8 +421,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 030F6FF92415C5E400A43F01 /* Assets.xcassets in Resources */, - 036AFBE32417F0A00075400A /* Main.storyboard in Resources */, + 035D1A0626F0927200B332BE /* Assets.xcassets in Resources */, + 035D1A0726F0927200B332BE /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -521,13 +455,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 035D1A0A26F0927200B332BE /* AppDelegate.swift in Sources */, + 035D1A0B26F0927200B332BE /* TimelapseViewController.swift in Sources */, + 035D1A0426F0927200B332BE /* ViewController.swift in Sources */, + 035D1A0526F0927200B332BE /* cbridge.c in Sources */, + 035D1A0826F0927200B332BE /* InfoViewController.swift in Sources */, 03CB3840241A5AED0078B3E5 /* Shared.swift in Sources */, - 033F94F92648B8E200099FB7 /* TimelapseViewController.swift in Sources */, - 036AFBBA24168C030075400A /* ViewController.swift in Sources */, - 03CB382424191F620078B3E5 /* cbridge.c in Sources */, - 030F6FF42415C5E300A43F01 /* Document.swift in Sources */, - 037B4042241821D200392452 /* InfoViewController.swift in Sources */, - 030F6FF22415C5E300A43F01 /* AppDelegate.swift in Sources */, + 035D1A0926F0927200B332BE /* Document.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -599,10 +533,10 @@ name = PreviewViewController.xib; sourceTree = ""; }; - 036AFBE12417F0A00075400A /* Main.storyboard */ = { + 035D19FD26F0927200B332BE /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( - 036AFBE22417F0A00075400A /* Base */, + 035D19FE26F0927200B332BE /* Base */, ); name = Main.storyboard; sourceTree = ""; @@ -636,6 +570,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -696,6 +631,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -729,18 +665,20 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = ProcreateViewer/ProcreateViewer.entitlements; + CODE_SIGN_ENTITLEMENTS = SilicaViewer/SilicaViewer.entitlements; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = JM5LKVKH48; - INFOPLIST_FILE = ProcreateViewer/Info.plist; + INFOPLIST_FILE = SilicaViewer/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer; - PRODUCT_NAME = "Procreate Viewer"; - SWIFT_OBJC_BRIDGING_HEADER = "ProcreateViewer/ProcreateViewer-Bridging-Header.h"; + PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer; + PRODUCT_NAME = "Silica Viewer"; + SWIFT_OBJC_BRIDGING_HEADER = "SilicaViewer/SilicaViewer-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; }; @@ -752,18 +690,20 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = ProcreateViewer/ProcreateViewer.entitlements; + CODE_SIGN_ENTITLEMENTS = SilicaViewer/SilicaViewer.entitlements; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = JM5LKVKH48; - INFOPLIST_FILE = ProcreateViewer/Info.plist; + INFOPLIST_FILE = SilicaViewer/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer; - PRODUCT_NAME = "Procreate Viewer"; - SWIFT_OBJC_BRIDGING_HEADER = "ProcreateViewer/ProcreateViewer-Bridging-Header.h"; + PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer; + PRODUCT_NAME = "Silica Viewer"; + SWIFT_OBJC_BRIDGING_HEADER = "SilicaViewer/SilicaViewer-Bridging-Header.h"; SWIFT_VERSION = 5.0; }; name = Release; @@ -772,7 +712,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = Quicklook/Quicklook.entitlements; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = JM5LKVKH48; INFOPLIST_FILE = Quicklook/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -780,7 +722,7 @@ "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer.QuickLook; + PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer.QuickLook; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; @@ -791,7 +733,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = Quicklook/Quicklook.entitlements; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = JM5LKVKH48; INFOPLIST_FILE = Quicklook/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -799,7 +743,7 @@ "@executable_path/../Frameworks", "@executable_path/../../../../Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer.QuickLook; + PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer.QuickLook; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; @@ -810,7 +754,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = Thumbnail/Thumbnail.entitlements; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = JM5LKVKH48; INFOPLIST_FILE = Thumbnail/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -819,7 +765,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer.Thumbnail; + PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer.Thumbnail; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; @@ -830,7 +776,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = Thumbnail/Thumbnail.entitlements; + CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 4; DEVELOPMENT_TEAM = JM5LKVKH48; INFOPLIST_FILE = Thumbnail/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -839,7 +787,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer.Thumbnail; + PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer.Thumbnail; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; @@ -909,7 +857,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 030F6FE92415C5E300A43F01 /* Build configuration list for PBXProject "ProcreateViewer" */ = { + 030F6FE92415C5E300A43F01 /* Build configuration list for PBXProject "SilicaViewer" */ = { isa = XCConfigurationList; buildConfigurations = ( 030F6FFF2415C5E400A43F01 /* Debug */, @@ -918,7 +866,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 030F70012415C5E400A43F01 /* Build configuration list for PBXNativeTarget "ProcreateViewer" */ = { + 030F70012415C5E400A43F01 /* Build configuration list for PBXNativeTarget "SilicaViewer" */ = { isa = XCConfigurationList; buildConfigurations = ( 030F70022415C5E400A43F01 /* Debug */, diff --git a/ProcreateViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SilicaViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from ProcreateViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to SilicaViewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/ProcreateViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SilicaViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from ProcreateViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to SilicaViewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/ProcreateViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SilicaViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 100% rename from ProcreateViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to SilicaViewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/ProcreateViewer/AppDelegate.swift b/SilicaViewer/AppDelegate.swift similarity index 100% rename from ProcreateViewer/AppDelegate.swift rename to SilicaViewer/AppDelegate.swift diff --git a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/Contents.json b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 70% rename from ProcreateViewer/Assets.xcassets/AppIcon.appiconset/Contents.json rename to SilicaViewer/Assets.xcassets/AppIcon.appiconset/Contents.json index 4d83053..f46fb60 100644 --- a/ProcreateViewer/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -3,61 +3,61 @@ { "size" : "16x16", "idiom" : "mac", - "filename" : "untitled-16.png", + "filename" : "icon-16.png", "scale" : "1x" }, { "size" : "16x16", "idiom" : "mac", - "filename" : "untitled-32.png", + "filename" : "icon-32.png", "scale" : "2x" }, { "size" : "32x32", "idiom" : "mac", - "filename" : "untitled-32.png", + "filename" : "icon-32.png", "scale" : "1x" }, { "size" : "32x32", "idiom" : "mac", - "filename" : "untitled-64.png", + "filename" : "icon-64.png", "scale" : "2x" }, { "size" : "128x128", "idiom" : "mac", - "filename" : "untitled-128.png", + "filename" : "icon-128.png", "scale" : "1x" }, { "size" : "128x128", "idiom" : "mac", - "filename" : "untitled-256.png", + "filename" : "icon-256.png", "scale" : "2x" }, { "size" : "256x256", "idiom" : "mac", - "filename" : "untitled-256.png", + "filename" : "icon-256.png", "scale" : "1x" }, { "size" : "256x256", "idiom" : "mac", - "filename" : "untitled-512.png", + "filename" : "icon-512.png", "scale" : "2x" }, { "size" : "512x512", "idiom" : "mac", - "filename" : "untitled-512.png", + "filename" : "icon-512.png", "scale" : "1x" }, { "size" : "512x512", "idiom" : "mac", - "filename" : "untitled-1024.png", + "filename" : "icon-1024.png", "scale" : "2x" } ], diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-1024.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-1024.png new file mode 100644 index 0000000..de64c1f Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-1024.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-128.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-128.png new file mode 100644 index 0000000..6f1d7eb Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-128.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-16.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-16.png new file mode 100644 index 0000000..fb12b02 Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-16.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-256.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-256.png new file mode 100644 index 0000000..87c240e Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-256.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-32.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-32.png new file mode 100644 index 0000000..450f9ca Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-32.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-512.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-512.png new file mode 100644 index 0000000..b981e84 Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-512.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-64.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-64.png new file mode 100644 index 0000000..1244c44 Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/Untitled-64.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-1024.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-1024.png new file mode 100644 index 0000000..5411042 Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-1024.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-128.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-128.png new file mode 100644 index 0000000..54fc4b1 Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-128.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-16.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-16.png new file mode 100644 index 0000000..9805acf Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-16.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-256.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-256.png new file mode 100644 index 0000000..08b0ba7 Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-256.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-32.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-32.png new file mode 100644 index 0000000..9ce4966 Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-32.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-512.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-512.png new file mode 100644 index 0000000..1f507c6 Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-512.png differ diff --git a/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-64.png b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-64.png new file mode 100644 index 0000000..5b2782e Binary files /dev/null and b/SilicaViewer/Assets.xcassets/AppIcon.appiconset/icon-64.png differ diff --git a/ProcreateViewer/Assets.xcassets/Contents.json b/SilicaViewer/Assets.xcassets/Contents.json similarity index 100% rename from ProcreateViewer/Assets.xcassets/Contents.json rename to SilicaViewer/Assets.xcassets/Contents.json diff --git a/ProcreateViewer/Base.lproj/Main.storyboard b/SilicaViewer/Base.lproj/Main.storyboard similarity index 90% rename from ProcreateViewer/Base.lproj/Main.storyboard rename to SilicaViewer/Base.lproj/Main.storyboard index 38263a0..181a2c7 100644 --- a/ProcreateViewer/Base.lproj/Main.storyboard +++ b/SilicaViewer/Base.lproj/Main.storyboard @@ -1,9 +1,8 @@ - + - - + @@ -13,11 +12,11 @@ - + - + - + @@ -31,7 +30,7 @@ - + @@ -49,7 +48,7 @@ - + @@ -79,7 +78,13 @@ - + + + + + + + @@ -143,7 +148,7 @@ - + @@ -157,7 +162,7 @@ - + @@ -167,7 +172,7 @@ - + @@ -253,25 +258,18 @@ DQ - + - - - - - - - - - - - + + + + + + + - - - @@ -301,7 +299,7 @@ DQ - + diff --git a/SilicaViewer/Document.swift b/SilicaViewer/Document.swift index 7d845d1..ca7b97b 100644 --- a/SilicaViewer/Document.swift +++ b/SilicaViewer/Document.swift @@ -1,28 +1,15 @@ import Cocoa import ZIPFoundation import CoreFoundation -import Accelerate -import CoreMedia struct SilicaChunk { var x: Int = 0 var y: Int = 0 - var image: CGImage? -} - -struct SilicaLayerData { - var blendMode: Int = 0 - var extendedBlend: Int = 0 - var chunks: [SilicaChunk] = [] - var opacity: Double = 1.0 - var hidden: Bool = false + var image: NSImage = NSImage() } struct SilicaLayer { - var name: String = "" - var data: SilicaLayerData = SilicaLayerData() - var mask: SilicaLayerData? - var clipped: Bool = false + var chunks: [SilicaChunk] = [] } struct SilicaDocument { @@ -31,31 +18,11 @@ struct SilicaDocument { var orientation: Int = 0 var flippedHorizontally: Bool = false var flippedVertically: Bool = false - var name: String = "" - var authorName: String = "" - var strokeCount: Int = 0 - - var backgroundColor: CGColor = .white - var colorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB() var width: Int = 0 var height: Int = 0 var layers: [SilicaLayer] = [] - - var videoFrame: (Int, Int) = (0, 0) - - lazy var nsSize = { - return NSSize(width: width, height: height) - }() - - lazy var cgSize = { - return CGSize(width: width, height: height) - }() - - lazy var cgRect = { - return CGRect(origin: .zero, size: cgSize) - }() } func objectRefGetValue2(_ objectRef: CFTypeRef) -> UInt32 { @@ -87,10 +54,6 @@ class Document: NSDocument { self.addWindowController(windowController) } - override class func canConcurrentlyReadDocuments(ofType: String) -> Bool { - return ofType == "com.procreate" - } - /* Pass in an object from the $object array, which always contains a $class key. */ @@ -110,7 +73,7 @@ class Document: NSDocument { /* Returns the correct tile size, taking into account the remainder between tile size and image size. */ - func getTileSize(_ x: Int, _ y: Int) -> (Int, Int) { + func getTileSize(x: Int, y: Int) -> (Int, Int) { var width: Int = info.tileSize var height: Int = info.tileSize @@ -149,138 +112,16 @@ class Document: NSDocument { } } - func getChunkRect(_ chunk: SilicaChunk) -> NSRect { - let x = chunk.x - var y = chunk.y - - let (width, height) = getTileSize(x, y) - - if y == rows { - y = 0 - } - - return NSRect(x: info.tileSize * x, y: info.height - (info.tileSize * y), width: width, height: height) - } - - // TODO: convert to switch/case - func getBlendKernel(_ layer: SilicaLayer) -> CIBlendKernel? { - if layer.data.blendMode == 1 { - return .multiply - } - if layer.data.blendMode == 10 { - return .colorBurn - } - if layer.data.blendMode == 19 { - return .darken - } - if layer.data.blendMode == 8 { - return .linearBurn - } - if layer.data.blendMode == 4 { - return .lighten - } - if layer.data.blendMode == 2 { - return .screen - } - if layer.data.blendMode == 13 { - return .hardLight - } - if layer.data.blendMode == 9 { - return .colorDodge - } - if layer.data.blendMode == 3 { - return .subtract - } - - if layer.data.blendMode == 0 && layer.data.blendMode != layer.data.extendedBlend { - if layer.data.extendedBlend == 25 { - return .darkerColor - } - if layer.data.extendedBlend == 24 { - return .lighterColor - } - if layer.data.extendedBlend == 21 { - return .vividLight - } - if layer.data.extendedBlend == 22 { - return .linearLight - } - if layer.data.extendedBlend == 23 { - return .pinLight - } - if layer.data.extendedBlend == 20 { - return .hardMix - } - if layer.data.extendedBlend == 26 { - return .divide - } - } - - if layer.data.blendMode == 11 { - return .overlay - } - if layer.data.blendMode == 17 { - return .softLight - } - if layer.data.blendMode == 12 { - return .hardLight - } - if layer.data.blendMode == 6 { - return .difference - } - if layer.data.blendMode == 5 { - return .exclusion - } - if layer.data.blendMode == 7 { - return .componentAdd - } - if layer.data.blendMode == 15 { - return .hue - } - if layer.data.blendMode == 16 { - return .saturation - } - if layer.data.blendMode == 13 { - return .color - } - if layer.data.blendMode == 14 { - return .luminosity - } - - return .sourceOver - } - - func parseSilicaLayer(archive: Archive, dict: NSDictionary, isMask: Bool) -> SilicaLayer? { + func parseSilicaLayer(archive: Archive, dict: NSDictionary) { let objectsArray = self.dict?["$objects"] as! NSArray if getDocumentClassName(dict: dict) == LayerClassName { var layer = SilicaLayer() - - 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 - let maskKey = dict["mask"] - let maskClassID = getClassID(id: maskKey) - let maskClass = objectsArray[maskClassID] - - layer.data.blendMode = (dict["blend"] as? NSNumber)!.intValue - layer.data.extendedBlend = (dict["extendedBlend"] as? NSNumber)!.intValue - layer.data.opacity = (dict["opacity"] as? NSNumber)!.doubleValue - layer.data.hidden = (dict["hidden"] as? Bool)! - layer.clipped = (dict["clipped"] as? Bool)! - - if maskClassID != 0 { - layer.mask = parseSilicaLayer(archive: archive, dict: maskClass as! NSDictionary, isMask: true)?.data - } - var chunkPaths: [String] = [] archive.forEach { (entry: Entry) in @@ -289,13 +130,15 @@ class Document: NSDocument { } } - layer.data.chunks = Array(repeating: SilicaChunk(), count: chunkPaths.count) + layer.chunks = Array(repeating: SilicaChunk(), count: chunkPaths.count) let dispatchGroup = DispatchGroup() let queue = DispatchQueue(label: "imageWork") DispatchQueue.concurrentPerform(iterations: chunkPaths.count) { (i: Int) in - guard let threadArchive = Archive(data: self.data!, accessMode: .read) else { + dispatchGroup.enter() + + guard let threadArchive = Archive(data: self.data!, accessMode: Archive.AccessMode.read) else { return } @@ -305,10 +148,8 @@ class Document: NSDocument { return } - let (width, height) = getTileSize(x, y) - - let numChannels = isMask ? 1 : 4 - let byteSize = width * height * numChannels + let (width, height) = getTileSize(x: x, y: y) + let byteSize = width * height * 4 let uncompressedMemory = UnsafeMutablePointer.allocate(capacity: byteSize) @@ -325,38 +166,28 @@ class Document: NSDocument { let imageData = Data(bytes: uncompressedMemory, count: byteSize) let render: CGColorRenderingIntent = .defaultIntent - let rgbColorSpace = isMask ? CGColorSpaceCreateDeviceGray() : info.colorSpace - - let bitmapInfo = CGBitmapInfo(rawValue: (isMask ? CGImageAlphaInfo.none : CGImageAlphaInfo.premultipliedLast).rawValue).union(.byteOrder32Big) + let rgbColorSpace = CGColorSpaceCreateDeviceRGB() + let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue).union(.byteOrder32Big) let providerRef: CGDataProvider? = CGDataProvider(data: imageData as CFData) - guard let cgimage = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 8 * numChannels, bytesPerRow: width * numChannels, space: rgbColorSpace, bitmapInfo: bitmapInfo, provider: providerRef!, decode: nil, shouldInterpolate: false, intent: render) else { + guard let cgimage = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: width * 4, space: rgbColorSpace, bitmapInfo: bitmapInfo, provider: providerRef!, decode: nil, shouldInterpolate: false, intent: render) else { return } - - queue.async(group: dispatchGroup) { - layer.data.chunks[i].image = cgimage - layer.data.chunks[i].x = x - layer.data.chunks[i].y = y + + let image = NSImage(cgImage: cgimage, size: NSZeroSize) + + queue.async(flags: .barrier) { + layer.chunks[i].image = image + layer.chunks[i].x = x + layer.chunks[i].y = y + + dispatchGroup.leave() } } dispatchGroup.wait() - return layer - } - - return nil - } - - // this parses a string of form "{255, 255}" - func parsePairString(_ str: String) -> (Int, Int)? { - let sizeComponents = str.replacingOccurrences(of: "{", with: "").replacingOccurrences(of: "}", with: "").components(separatedBy: ", ") - - if sizeComponents.count == 2, let width = Int(sizeComponents[0]), let height = Int(sizeComponents[1]) { - return (width, height) - } else { - return nil + info.layers.append(layer) } } @@ -370,70 +201,19 @@ class Document: NSDocument { info.flippedHorizontally = (dict[FlippedHorizontallyKey] as! NSNumber).boolValue info.flippedVertically = (dict[FlippedVerticallyKey] as! NSNumber).boolValue - let videoResolutionClassKey = dict["SilicaDocumentVideoSegmentInfoKey"] - let videoResolutionClassID = getClassID(id: videoResolutionClassKey) - let videoResolution = objectsArray[videoResolutionClassID] as! NSDictionary - - let frameSizeClassKey = videoResolution["frameSize"] - let frameSizeClassID = getClassID(id: frameSizeClassKey) - let frameSize = objectsArray[frameSizeClassID] as! String - - info.videoFrame = parsePairString(frameSize)! - - 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 - - info.strokeCount = Int(truncating: strokeCount) - - let nameClassKey = dict[NameKey] - let nameClassID = getClassID(id: nameClassKey) - let nameString = objectsArray[nameClassID] as! String - - info.name = nameString - - let authorClassKey = dict[AuthorNameKey] - let authorClassID = getClassID(id: authorClassKey) - let authorString = objectsArray[authorClassID] as! String - - if authorString != "$null" { - info.authorName = authorString - } - let sizeClassKey = dict[SizeKey] let sizeClassID = getClassID(id: sizeClassKey) let sizeString = objectsArray[sizeClassID] as! String - let (width, height) = parsePairString(sizeString)! - info.width = width - info.height = height + let sizeComponents = sizeString.replacingOccurrences(of: "{", with: "").replacingOccurrences(of: "}", with: "").components(separatedBy: ", ") + let width = Int(sizeComponents[0]) + let height = Int(sizeComponents[1]) + + info.width = width! + info.height = height! columns = Int(ceil(Float(info.width) / Float(info.tileSize))) - rows = Int(ceil(Float(info.height) / Float(info.tileSize))) + 1 // TODO: lol why + rows = Int(ceil(Float(info.height) / Float(info.tileSize))) if info.width % info.tileSize != 0 { remainderWidth = (columns * info.tileSize) - info.width @@ -449,14 +229,11 @@ 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 - guard let layer = parseSilicaLayer(archive: archive, dict: layerClass, isMask: false) else { return } - info.layers.append(layer) + parseSilicaLayer(archive: archive, dict: layerClass) } } } @@ -481,135 +258,55 @@ class Document: NSDocument { parseSilicaDocument(archive: archive, dict: topObjectClass) } } - - struct SilicaParsingError: Error, LocalizedError { - enum Kind { - case invalid - } - - let kind: Kind - let filename: URL? - - public var errorDescription: String? { - switch self.kind { - case .invalid: - return filename!.lastPathComponent + " is an invalid Silica Document." - } - } - } - - func throwError(_ error: SilicaParsingError.Kind) { - DispatchQueue.main.sync { - let _ = presentError(SilicaParsingError(kind: error, filename: fileURL)) - } - } override func read(from data: Data, ofType typeName: String) throws { self.data = data - guard let archive = Archive(data: data, accessMode: Archive.AccessMode.read) else { - throwError(.invalid) + guard let archive = Archive(data: data, accessMode: Archive.AccessMode.read) else { return } guard let documentEntry = archive[DocumentArchivePath] else { - throwError(.invalid) return } guard let documentData = readData(archive: archive, entry: documentEntry) else { - throwError(.invalid) return } var plistFormat = PropertyListSerialization.PropertyListFormat.binary guard let propertyList = try? PropertyListSerialization.propertyList(from: documentData, options: [], format: &plistFormat) else { - throwError(.invalid) return } parseDocument(archive: archive, dict: propertyList as! NSDictionary) } - func makeComposite() -> NSImage? { - // create the final composite output image - 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: info.colorSpace, bitmapInfo: bitmapInfo.rawValue) + func makeComposite() -> NSImage { + var image = NSImage(size: NSSize(width: info.width, height: info.height)) + image.lockFocus() - ccgContext?.setFillColor(info.backgroundColor) - ccgContext?.fill(info.cgRect) - - let context = CIContext() - - guard let cgImage = ccgContext?.makeImage() else { - return nil - } - - var masterImage = CIImage(cgImage: cgImage) - - var previousImage: CGImage? = nil + let color = NSColor.white + color.drawSwatch(in: NSRect(origin: .zero, size: image.size)) for layer in info.layers.reversed() { - if !layer.data.hidden { - // 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: info.colorSpace, bitmapInfo: bitmapInfo.rawValue) + for chunk in layer.chunks { + let x = chunk.x + var y = chunk.y - layerContext?.clear(info.cgRect) - - var maskContext: CGContext? - - let kernel = getBlendKernel(layer) - - if layer.mask != nil { - let grayColorSpace = CGColorSpaceCreateDeviceGray() - let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big) - - maskContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 16, bytesPerRow: 0, space: grayColorSpace, bitmapInfo: maskBitmapInfo.rawValue) - - maskContext?.setFillColor(.white) - maskContext?.fill(info.cgRect) - - for chunk in layer.mask!.chunks { - maskContext?.draw(chunk.image!, in: getChunkRect(chunk)) - } - } + let (width, height) = getTileSize(x: x, y: y) - for chunk in layer.data.chunks { - layerContext?.setAlpha(CGFloat(layer.data.opacity)) - layerContext?.setBlendMode(.normal) - - if !layer.data.hidden { - layerContext?.draw(chunk.image!, in: getChunkRect(chunk)) - } + if y == rows { + y = 0 } - - let layerImage = layerContext?.makeImage() + + let rect = NSRect(x: info.tileSize * x, y: info.height - (info.tileSize * y), width: width, height: height) - if layer.clipped && previousImage != nil { - let result = previousImage!.toGrayscale() - let newImage = layerImage!.masking(result!) - - previousImage = newImage - } else if layer.mask != nil && maskContext != nil { - let maskImage = (maskContext?.makeImage())! - let newImage = layerImage!.masking(maskImage)! - - previousImage = newImage - } else { - previousImage = layerImage - } - - // apply image - masterImage = kernel!.apply(foreground: CIImage(cgImage: previousImage!), background: masterImage, colorSpace: info.colorSpace)! + chunk.image.draw(in: rect) } } - guard let finalCgImage = context.createCGImage(masterImage, from: info.cgRect, format: .RGBA8, colorSpace: info.colorSpace) else { - return nil - } - - var image = NSImage(cgImage: finalCgImage, size: info.nsSize) + image.unlockFocus() if info.orientation == 3 { image = image.imageRotatedByDegreess(degrees: 90) @@ -711,35 +408,3 @@ public extension NSImage { return flipedImage } } - -public extension CGImage { - func toGrayscale() -> CGImage? { - let ciImage = CIImage(cgImage: self) - - let filter = CIFilter(name: "CIColorControls") - filter?.setValue(ciImage, forKey: kCIInputImageKey) - filter?.setValue(5.0, forKey: kCIInputBrightnessKey) - filter?.setValue(0.0, forKey: kCIInputSaturationKey) - filter?.setValue(1.1, forKey: kCIInputContrastKey) - - guard let intermediateImage = filter?.outputImage else { - return nil - } - - guard let image = CIContext().createCGImage(intermediateImage, from: CGRect(origin: .zero, size: CGSize(width: self.width, height: self.height))) else { - return nil - } - - let grayColorSpace = CGColorSpaceCreateDeviceGray() - let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big) - - let maskContext = CGContext(data: nil, width: self.width, height: self.height, bitsPerComponent: 16, bytesPerRow: 0, space: grayColorSpace, bitmapInfo: maskBitmapInfo.rawValue) - - maskContext?.setFillColor(.black) - maskContext?.fill(CGRect(origin: .zero, size: CGSize(width: self.width, height: self.height))) - - maskContext?.draw(image, in: CGRect(origin: .zero, size: CGSize(width: self.width, height: self.height))) - - return maskContext?.makeImage() - } -} diff --git a/ProcreateViewer/Info.plist b/SilicaViewer/Info.plist similarity index 91% rename from ProcreateViewer/Info.plist rename to SilicaViewer/Info.plist index 58d0d8b..a6b11f2 100644 --- a/ProcreateViewer/Info.plist +++ b/SilicaViewer/Info.plist @@ -14,9 +14,11 @@ CFBundleTypeIconFile CFBundleTypeName - + com.proceate CFBundleTypeRole Viewer + LSHandlerRank + Default LSItemContentTypes com.procreate @@ -42,13 +44,13 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright - Copyright © 2020 Joshua Goins. All rights reserved. + Copyright © 2021 Joshua Goins. All rights reserved. NSMainStoryboardFile Main NSPrincipalClass diff --git a/ProcreateViewer/InfoViewController.swift b/SilicaViewer/InfoViewController.swift similarity index 100% rename from ProcreateViewer/InfoViewController.swift rename to SilicaViewer/InfoViewController.swift diff --git a/SilicaViewer/SilicaViewer-Bridging-Header.h b/SilicaViewer/SilicaViewer-Bridging-Header.h new file mode 100644 index 0000000..6616edf --- /dev/null +++ b/SilicaViewer/SilicaViewer-Bridging-Header.h @@ -0,0 +1,8 @@ +#include +#include "minilzo.h" + +uint32_t valueForKeyedArchiverUID(uint64_t keyedArchiverUID) { + void *uid = (void*)keyedArchiverUID; + uint32_t *valuePtr = uid+16; + return *valuePtr; +} diff --git a/ProcreateViewer/ProcreateViewer.entitlements b/SilicaViewer/SilicaViewer.entitlements similarity index 100% rename from ProcreateViewer/ProcreateViewer.entitlements rename to SilicaViewer/SilicaViewer.entitlements diff --git a/SilicaViewer/TimelapseViewController.swift b/SilicaViewer/TimelapseViewController.swift new file mode 100644 index 0000000..bfd3cbd --- /dev/null +++ b/SilicaViewer/TimelapseViewController.swift @@ -0,0 +1,87 @@ +import Foundation +import Cocoa +import AVKit +import AVFoundation +import ZIPFoundation + +func radians(_ x: CGFloat) -> CGFloat { + return .pi * x / 180.0 +} + +class TimelapseViewController: NSViewController { + var document: Document? + + private let rootLayer = CALayer() + let player: AVQueuePlayer = + AVQueuePlayer() + var isPlaying = true + + @IBAction func clickAction(_ sender: Any) { + if isPlaying { + player.pause() + isPlaying = false + } else { + player.play() + isPlaying = true + } + } + + func addVideo(entryPath: String, directory: String) { + guard let archive = Archive(data: (document?.data)!, accessMode: Archive.AccessMode.read) else { + return + } + + let fileName = NSUUID().uuidString + ".mp4" + + // This returns a URL? even though it is an NSURL class method + let fullURL = NSURL.fileURL(withPathComponents: [directory, fileName])! + + try? archive.extract(archive[entryPath]!, to: fullURL) + + OperationQueue.main.addOperation { + self.player.insert(AVPlayerItem(url: fullURL), after: nil) + } + } + + override func viewWillAppear() { + super.viewDidAppear() + + guard let archive = Archive(data: (document?.data)!, accessMode: Archive.AccessMode.read) else { + return + } + + + let directory = NSTemporaryDirectory() + + let queue = OperationQueue() + for entry in archive.makeIterator() { + if entry.path.contains(VideoPath) { + queue.addOperation() { + self.addVideo(entryPath: entry.path, directory: directory) + } + } + } + + let playerLayer = AVPlayerLayer.init(player: self.player) + playerLayer.videoGravity = .resizeAspect + playerLayer.autoresizingMask = [.layerHeightSizable, .layerWidthSizable] + playerLayer.frame = view.bounds + + var rotationDegrees: CGFloat = 0 + if document?.info.orientation == 3 { + rotationDegrees = 90 + } else if document?.info.orientation == 4 { + rotationDegrees = -90 + } + + let affineTransform = CGAffineTransform(rotationAngle: radians(rotationDegrees)) + playerLayer.setAffineTransform(affineTransform) + + view.wantsLayer = true + view.layer = rootLayer + + rootLayer.addSublayer(playerLayer) + + player.play() + } +} diff --git a/ProcreateViewer/ViewController.swift b/SilicaViewer/ViewController.swift similarity index 94% rename from ProcreateViewer/ViewController.swift rename to SilicaViewer/ViewController.swift index e0f3f07..6ad6019 100644 --- a/ProcreateViewer/ViewController.swift +++ b/SilicaViewer/ViewController.swift @@ -7,7 +7,7 @@ class ViewController: NSViewController { override func viewWillAppear() { let document = self.view.window?.windowController?.document as? Document - imageView.image = document?.makeComposite() + imageView.image = document?.makeThumbnail() } override func prepare(for segue: NSStoryboardSegue, sender: Any?) { diff --git a/ProcreateViewer/cbridge.c b/SilicaViewer/cbridge.c similarity index 100% rename from ProcreateViewer/cbridge.c rename to SilicaViewer/cbridge.c diff --git a/Thumbnail/Info.plist b/Thumbnail/Info.plist index 2f1489a..167e597 100644 --- a/Thumbnail/Info.plist +++ b/Thumbnail/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSExtension