1
Fork 0

Rename to Silica Viewer

This commit is contained in:
Joshua Goins 2021-09-15 14:10:07 -04:00
parent 636145e265
commit e7b35fcfdf
43 changed files with 249 additions and 982 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -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<UInt8>.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
}
}

View file

@ -1,9 +0,0 @@
/*
Here we import Apple's functions to decode __CFKeyedArchiverUID types.
*/
#include <CoreFoundation/CFBase.h>
#include "minilzo.h"
typedef const struct __CFKeyedArchiverUID * CFKeyedArchiverUIDRef;
extern uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid);

View file

@ -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)
}
}

View file

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string> <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSExtension</key> <key>NSExtension</key>
@ -31,7 +31,7 @@
<string>com.procreate</string> <string>com.procreate</string>
</array> </array>
<key>QLSupportsSearchableItems</key> <key>QLSupportsSearchableItems</key>
<false/> <true/>
</dict> </dict>
<key>NSExtensionPointIdentifier</key> <key>NSExtensionPointIdentifier</key>
<string>com.apple.quicklook.preview</string> <string>com.apple.quicklook.preview</string>

View file

@ -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 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! 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 ### 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. 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.

View file

@ -7,15 +7,10 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; }; 030F700B2415C6B500A43F01 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 030F700A2415C6B500A43F01 /* Quartz.framework */; };
030F700E2415C6B500A43F01 /* PreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F700D2415C6B500A43F01 /* PreviewViewController.swift */; }; 030F700E2415C6B500A43F01 /* PreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030F700D2415C6B500A43F01 /* PreviewViewController.swift */; };
030F70112415C6B500A43F01 /* PreviewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 030F700F2415C6B500A43F01 /* PreviewViewController.xib */; }; 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, ); }; }; 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 */; }; 035D1A0426F0927200B332BE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D19F826F0927200B332BE /* ViewController.swift */; };
035D1A0526F0927200B332BE /* cbridge.c in Sources */ = {isa = PBXBuildFile; fileRef = 035D19FA26F0927200B332BE /* cbridge.c */; }; 035D1A0526F0927200B332BE /* cbridge.c in Sources */ = {isa = PBXBuildFile; fileRef = 035D19FA26F0927200B332BE /* cbridge.c */; };
035D1A0626F0927200B332BE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 035D19FB26F0927200B332BE /* Assets.xcassets */; }; 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 */; }; 035D1A0A26F0927200B332BE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D1A0126F0927200B332BE /* AppDelegate.swift */; };
035D1A0B26F0927200B332BE /* TimelapseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D1A0226F0927200B332BE /* TimelapseViewController.swift */; }; 035D1A0B26F0927200B332BE /* TimelapseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035D1A0226F0927200B332BE /* TimelapseViewController.swift */; };
036AFBB8241687680075400A /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 036AFBB7241687680075400A /* ZIPFoundation */; }; 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 */; }; 036AFC062417F2990075400A /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 036AFC052417F2990075400A /* ZIPFoundation */; };
036AFC0D241800350075400A /* QuickLookThumbnailing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 036AFC0C241800350075400A /* QuickLookThumbnailing.framework */; }; 036AFC0D241800350075400A /* QuickLookThumbnailing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 036AFC0C241800350075400A /* QuickLookThumbnailing.framework */; };
036AFC0E241800350075400A /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 030F700A2415C6B500A43F01 /* Quartz.framework */; }; 036AFC0E241800350075400A /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 030F700A2415C6B500A43F01 /* Quartz.framework */; };
036AFC11241800350075400A /* ThumbnailProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036AFC10241800350075400A /* ThumbnailProvider.swift */; }; 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, ); }; }; 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 */; }; 036AFC1B241800850075400A /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 036AFC1A241800850075400A /* ZIPFoundation */; };
0371996027BAC5D800EE1DFD /* Silica_ViewerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0371995F27BAC5D800EE1DFD /* Silica_ViewerTests.swift */; }; ==== BASE ====
03C39DE726F90734005555AE /* lzodefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 03C39DE226F90733005555AE /* lzodefs.h */; }; 037B4042241821D200392452 /* InfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 037B4041241821D200392452 /* InfoViewController.swift */; };
03C39DE826F90734005555AE /* Makefile in Sources */ = {isa = PBXBuildFile; fileRef = 03C39DE426F90734005555AE /* Makefile */; }; 03CB382424191F620078B3E5 /* cbridge.c in Sources */ = {isa = PBXBuildFile; fileRef = 03CB382324191F620078B3E5 /* cbridge.c */; };
03C39DE926F90734005555AE /* testmini.c in Sources */ = {isa = PBXBuildFile; fileRef = 03C39DE526F90734005555AE /* testmini.c */; }; ==== BASE ====
03C39DEA26F90734005555AE /* minilzo.h in Headers */ = {isa = PBXBuildFile; fileRef = 03C39DE626F90734005555AE /* minilzo.h */; };
03CB383B2419CA2D0078B3E5 /* libLZO.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 03CB382E2419C9DB0078B3E5 /* libLZO.a */; }; 03CB383B2419CA2D0078B3E5 /* libLZO.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 03CB382E2419C9DB0078B3E5 /* libLZO.a */; };
03CB3840241A5AED0078B3E5 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CB383F241A5AED0078B3E5 /* Shared.swift */; }; 03CB3840241A5AED0078B3E5 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CB383F241A5AED0078B3E5 /* Shared.swift */; };
03CB3841241A5AED0078B3E5 /* 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; remoteGlobalIDString = 036AFC0A241800350075400A;
remoteInfo = Thumbnail; remoteInfo = Thumbnail;
}; };
0371996127BAC5D800EE1DFD /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 030F6FE62415C5E300A43F01 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 030F6FED2415C5E300A43F01;
remoteInfo = SilicaViewer;
};
03CB383C2419CA2D0078B3E5 /* PBXContainerItemProxy */ = { 03CB383C2419CA2D0078B3E5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 030F6FE62415C5E300A43F01 /* Project object */; containerPortal = 030F6FE62415C5E300A43F01 /* Project object */;
@ -102,10 +87,6 @@
030F70102415C6B500A43F01 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PreviewViewController.xib; sourceTree = "<group>"; }; 030F70102415C6B500A43F01 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PreviewViewController.xib; sourceTree = "<group>"; };
030F70122415C6B500A43F01 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 030F70122415C6B500A43F01 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
030F70132415C6B500A43F01 /* Quicklook.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Quicklook.entitlements; sourceTree = "<group>"; }; 030F70132415C6B500A43F01 /* Quicklook.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Quicklook.entitlements; sourceTree = "<group>"; };
0357A94926F9C7370075D5BC /* README.LZO */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.LZO; sourceTree = "<group>"; };
0357A94A26F9C7370075D5BC /* minilzo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = minilzo.c; sourceTree = "<group>"; };
0357A94B26F9C7370075D5BC /* testmini */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = testmini; sourceTree = "<group>"; };
0357A94C26F9C7370075D5BC /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = "<group>"; };
035D19F826F0927200B332BE /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; }; 035D19F826F0927200B332BE /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
035D19F926F0927200B332BE /* SilicaViewer.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = SilicaViewer.entitlements; sourceTree = "<group>"; }; 035D19F926F0927200B332BE /* SilicaViewer.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = SilicaViewer.entitlements; sourceTree = "<group>"; };
035D19FA26F0927200B332BE /* cbridge.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cbridge.c; sourceTree = "<group>"; }; 035D19FA26F0927200B332BE /* cbridge.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cbridge.c; sourceTree = "<group>"; };
@ -122,14 +103,11 @@
036AFC10241800350075400A /* ThumbnailProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailProvider.swift; sourceTree = "<group>"; }; 036AFC10241800350075400A /* ThumbnailProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailProvider.swift; sourceTree = "<group>"; };
036AFC12241800350075400A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 036AFC12241800350075400A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
036AFC13241800350075400A /* Thumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Thumbnail.entitlements; sourceTree = "<group>"; }; 036AFC13241800350075400A /* Thumbnail.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Thumbnail.entitlements; sourceTree = "<group>"; };
0371995827BAC52900EE1DFD /* SilicaViewer.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = SilicaViewer.xctestplan; sourceTree = "<group>"; }; ==== BASE ====
0371995D27BAC5D800EE1DFD /* Silica ViewerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Silica ViewerTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 037B4041241821D200392452 /* InfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoViewController.swift; sourceTree = "<group>"; };
0371995F27BAC5D800EE1DFD /* Silica_ViewerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Silica_ViewerTests.swift; sourceTree = "<group>"; }; 03CB382224191F610078B3E5 /* ProcreateViewer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ProcreateViewer-Bridging-Header.h"; sourceTree = "<group>"; };
03C39DE226F90733005555AE /* lzodefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzodefs.h; sourceTree = "<group>"; }; 03CB382324191F620078B3E5 /* cbridge.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cbridge.c; sourceTree = "<group>"; };
03C39DE326F90733005555AE /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; }; ==== BASE ====
03C39DE426F90734005555AE /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
03C39DE526F90734005555AE /* testmini.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testmini.c; sourceTree = "<group>"; };
03C39DE626F90734005555AE /* minilzo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minilzo.h; sourceTree = "<group>"; };
03CB382E2419C9DB0078B3E5 /* libLZO.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLZO.a; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; 03CB383F241A5AED0078B3E5 /* Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = "<group>"; };
03CB3843241A5D600078B3E5 /* minilzo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minilzo.h; path = "Dependencies/minilzo-2.10/minilzo.h"; sourceTree = SOURCE_ROOT; }; 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; runOnlyForDeploymentPostprocessing = 0;
}; };
0371995A27BAC5D800EE1DFD /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
03CB382C2419C9DB0078B3E5 /* Frameworks */ = { 03CB382C2419C9DB0078B3E5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -187,15 +158,11 @@
030F6FE52415C5E300A43F01 = { 030F6FE52415C5E300A43F01 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0371995827BAC52900EE1DFD /* SilicaViewer.xctestplan */,
0357A94826F9C71E0075D5BC /* LZO */,
035D19F726F0927200B332BE /* SilicaViewer */, 035D19F726F0927200B332BE /* SilicaViewer */,
03CB383E241A5ACD0078B3E5 /* Shared */, 03CB383E241A5ACD0078B3E5 /* Shared */,
03CB38322419C9F80078B3E5 /* LZO */, 03CB38322419C9F80078B3E5 /* LZO */,
030F6FF02415C5E300A43F01 /* ProcreateViewer */,
030F700C2415C6B500A43F01 /* Quicklook */, 030F700C2415C6B500A43F01 /* Quicklook */,
036AFC0F241800350075400A /* Thumbnail */, 036AFC0F241800350075400A /* Thumbnail */,
0371995E27BAC5D800EE1DFD /* Silica ViewerTests */,
030F70092415C6B500A43F01 /* Frameworks */, 030F70092415C6B500A43F01 /* Frameworks */,
030F6FEF2415C5E300A43F01 /* Products */, 030F6FEF2415C5E300A43F01 /* Products */,
); );
@ -208,29 +175,10 @@
030F70082415C6B500A43F01 /* QuickLook.appex */, 030F70082415C6B500A43F01 /* QuickLook.appex */,
036AFC0B241800350075400A /* Thumbnail.appex */, 036AFC0B241800350075400A /* Thumbnail.appex */,
03CB382E2419C9DB0078B3E5 /* libLZO.a */, 03CB382E2419C9DB0078B3E5 /* libLZO.a */,
0371995D27BAC5D800EE1DFD /* Silica ViewerTests.xctest */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
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 = "<group>";
};
030F70092415C6B500A43F01 /* Frameworks */ = { 030F70092415C6B500A43F01 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -251,22 +199,6 @@
path = Quicklook; path = Quicklook;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
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 = "<group>";
};
035D19F726F0927200B332BE /* SilicaViewer */ = { 035D19F726F0927200B332BE /* SilicaViewer */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -295,12 +227,15 @@
path = Thumbnail; path = Thumbnail;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
0371995E27BAC5D800EE1DFD /* Silica ViewerTests */ = { 03CB38322419C9F80078B3E5 /* LZO */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0371995F27BAC5D800EE1DFD /* Silica_ViewerTests.swift */, 03CB3846241A5D600078B3E5 /* lzoconf.h */,
03CB3844241A5D600078B3E5 /* lzodefs.h */,
03CB3845241A5D600078B3E5 /* minilzo.c */,
03CB3843241A5D600078B3E5 /* minilzo.h */,
); );
path = "Silica ViewerTests"; path = LZO;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
03CB383E241A5ACD0078B3E5 /* Shared */ = { 03CB383E241A5ACD0078B3E5 /* Shared */ = {
@ -318,9 +253,9 @@
isa = PBXHeadersBuildPhase; isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
03C39DE726F90734005555AE /* lzodefs.h in Headers */, 03CB3848241A5D600078B3E5 /* lzodefs.h in Headers */,
0357A94E26F9C7370075D5BC /* lzoconf.h in Headers */, 03CB384A241A5D600078B3E5 /* lzoconf.h in Headers */,
03C39DEA26F90734005555AE /* minilzo.h in Headers */, 03CB3847241A5D600078B3E5 /* minilzo.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -435,7 +370,6 @@
==== BASE ==== ==== BASE ====
LastSwiftUpdateCheck = 1130; LastSwiftUpdateCheck = 1130;
LastUpgradeCheck = 1250; LastUpgradeCheck = 1250;
==== BASE ====
ORGANIZATIONNAME = Josh; ORGANIZATIONNAME = Josh;
TargetAttributes = { TargetAttributes = {
030F6FED2415C5E300A43F01 = { 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"; compatibilityVersion = "Xcode 9.3";
developmentRegion = en; developmentRegion = en;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
@ -473,7 +407,7 @@
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (
030F6FED2415C5E300A43F01 /* ProcreateViewer */, 030F6FED2415C5E300A43F01 /* SilicaViewer */,
030F70072415C6B500A43F01 /* QuickLook */, 030F70072415C6B500A43F01 /* QuickLook */,
036AFC0A241800350075400A /* Thumbnail */, 036AFC0A241800350075400A /* Thumbnail */,
03CB382D2419C9DB0078B3E5 /* LZO */, 03CB382D2419C9DB0078B3E5 /* LZO */,
@ -487,8 +421,8 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
030F6FF92415C5E400A43F01 /* Assets.xcassets in Resources */, 035D1A0626F0927200B332BE /* Assets.xcassets in Resources */,
036AFBE32417F0A00075400A /* Main.storyboard in Resources */, 035D1A0726F0927200B332BE /* Main.storyboard in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -521,13 +455,13 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( 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 */, 03CB3840241A5AED0078B3E5 /* Shared.swift in Sources */,
033F94F92648B8E200099FB7 /* TimelapseViewController.swift in Sources */, 035D1A0926F0927200B332BE /* Document.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 */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -599,10 +533,10 @@
name = PreviewViewController.xib; name = PreviewViewController.xib;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
036AFBE12417F0A00075400A /* Main.storyboard */ = { 035D19FD26F0927200B332BE /* Main.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
036AFBE22417F0A00075400A /* Base */, 035D19FE26F0927200B332BE /* Base */,
); );
name = Main.storyboard; name = Main.storyboard;
sourceTree = "<group>"; sourceTree = "<group>";
@ -636,6 +570,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -696,6 +631,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -729,18 +665,20 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = ProcreateViewer/ProcreateViewer.entitlements; CODE_SIGN_ENTITLEMENTS = SilicaViewer/SilicaViewer.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = JM5LKVKH48; DEVELOPMENT_TEAM = JM5LKVKH48;
INFOPLIST_FILE = ProcreateViewer/Info.plist; INFOPLIST_FILE = SilicaViewer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer; PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer;
PRODUCT_NAME = "Procreate Viewer"; PRODUCT_NAME = "Silica Viewer";
SWIFT_OBJC_BRIDGING_HEADER = "ProcreateViewer/ProcreateViewer-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "SilicaViewer/SilicaViewer-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
}; };
@ -752,18 +690,20 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = ProcreateViewer/ProcreateViewer.entitlements; CODE_SIGN_ENTITLEMENTS = SilicaViewer/SilicaViewer.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = JM5LKVKH48; DEVELOPMENT_TEAM = JM5LKVKH48;
INFOPLIST_FILE = ProcreateViewer/Info.plist; INFOPLIST_FILE = SilicaViewer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer; PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer;
PRODUCT_NAME = "Procreate Viewer"; PRODUCT_NAME = "Silica Viewer";
SWIFT_OBJC_BRIDGING_HEADER = "ProcreateViewer/ProcreateViewer-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "SilicaViewer/SilicaViewer-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
}; };
name = Release; name = Release;
@ -772,7 +712,9 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = Quicklook/Quicklook.entitlements; CODE_SIGN_ENTITLEMENTS = Quicklook/Quicklook.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = JM5LKVKH48; DEVELOPMENT_TEAM = JM5LKVKH48;
INFOPLIST_FILE = Quicklook/Info.plist; INFOPLIST_FILE = Quicklook/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -780,7 +722,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer.QuickLook; PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer.QuickLook;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -791,7 +733,9 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = Quicklook/Quicklook.entitlements; CODE_SIGN_ENTITLEMENTS = Quicklook/Quicklook.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = JM5LKVKH48; DEVELOPMENT_TEAM = JM5LKVKH48;
INFOPLIST_FILE = Quicklook/Info.plist; INFOPLIST_FILE = Quicklook/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -799,7 +743,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer.QuickLook; PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer.QuickLook;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -810,7 +754,9 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = Thumbnail/Thumbnail.entitlements; CODE_SIGN_ENTITLEMENTS = Thumbnail/Thumbnail.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = JM5LKVKH48; DEVELOPMENT_TEAM = JM5LKVKH48;
INFOPLIST_FILE = Thumbnail/Info.plist; INFOPLIST_FILE = Thumbnail/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -819,7 +765,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15; MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer.Thumbnail; PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer.Thumbnail;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -830,7 +776,9 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = Thumbnail/Thumbnail.entitlements; CODE_SIGN_ENTITLEMENTS = Thumbnail/Thumbnail.entitlements;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = JM5LKVKH48; DEVELOPMENT_TEAM = JM5LKVKH48;
INFOPLIST_FILE = Thumbnail/Info.plist; INFOPLIST_FILE = Thumbnail/Info.plist;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@ -839,7 +787,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15; MACOSX_DEPLOYMENT_TARGET = 10.15;
PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.ProcreateViewer.Thumbnail; PRODUCT_BUNDLE_IDENTIFIER = com.redstrate.SilicaViewer.Thumbnail;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -909,7 +857,7 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
030F6FE92415C5E300A43F01 /* Build configuration list for PBXProject "ProcreateViewer" */ = { 030F6FE92415C5E300A43F01 /* Build configuration list for PBXProject "SilicaViewer" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
030F6FFF2415C5E400A43F01 /* Debug */, 030F6FFF2415C5E400A43F01 /* Debug */,
@ -918,7 +866,7 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
030F70012415C5E400A43F01 /* Build configuration list for PBXNativeTarget "ProcreateViewer" */ = { 030F70012415C5E400A43F01 /* Build configuration list for PBXNativeTarget "SilicaViewer" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
030F70022415C5E400A43F01 /* Debug */, 030F70022415C5E400A43F01 /* Debug */,

View file

@ -3,61 +3,61 @@
{ {
"size" : "16x16", "size" : "16x16",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-16.png", "filename" : "icon-16.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "16x16", "size" : "16x16",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-32.png", "filename" : "icon-32.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "32x32", "size" : "32x32",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-32.png", "filename" : "icon-32.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "32x32", "size" : "32x32",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-64.png", "filename" : "icon-64.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "128x128", "size" : "128x128",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-128.png", "filename" : "icon-128.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "128x128", "size" : "128x128",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-256.png", "filename" : "icon-256.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "256x256", "size" : "256x256",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-256.png", "filename" : "icon-256.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "256x256", "size" : "256x256",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-512.png", "filename" : "icon-512.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"size" : "512x512", "size" : "512x512",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-512.png", "filename" : "icon-512.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"size" : "512x512", "size" : "512x512",
"idiom" : "mac", "idiom" : "mac",
"filename" : "untitled-1024.png", "filename" : "icon-1024.png",
"scale" : "2x" "scale" : "2x"
} }
], ],

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.AVKitIBPlugin" version="17156"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="18122"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17156"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
@ -13,11 +12,11 @@
<application id="hnw-xV-0zn" sceneMemberID="viewController"> <application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6"> <menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items> <items>
<menuItem title="Procreate Viewer" id="1Xt-HY-uBw" userLabel="Procreate Viewer"> <menuItem title="Silica Viewer" id="1Xt-HY-uBw" userLabel="Silica Viewer">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Procreate Viewer" systemMenu="apple" id="uQy-DD-JDr"> <menu key="submenu" title="Silica Viewer" systemMenu="apple" id="uQy-DD-JDr">
<items> <items>
<menuItem title="About Procreate Viewer" id="5kV-Vb-QxS" userLabel="About ProcreateViewer"> <menuItem title="About Silica Viewer" id="5kV-Vb-QxS" userLabel="About Silica Viewer">
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<connections> <connections>
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/> <action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
@ -31,7 +30,7 @@
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/> <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/> <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Procreate Viewer" keyEquivalent="h" id="Olw-nP-bQN"> <menuItem title="Hide Silica Viewer" keyEquivalent="h" id="Olw-nP-bQN">
<connections> <connections>
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/> <action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
</connections> </connections>
@ -49,7 +48,7 @@
</connections> </connections>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/> <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Procreate Viewer" keyEquivalent="q" id="4sb-4s-VLi"> <menuItem title="Quit Silica Viewer" keyEquivalent="q" id="4sb-4s-VLi">
<connections> <connections>
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/> <action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
</connections> </connections>
@ -79,7 +78,13 @@
</items> </items>
</menu> </menu>
</menuItem> </menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/> <menuItem isSeparatorItem="YES" id="MaD-eL-lbO"/>
<menuItem title="Save Thumbnail..." keyEquivalent="s" id="JR5-se-hvt" userLabel="Save Thumbnail..."/>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE">
<connections>
<action selector="saveDocument:" target="Ady-hI-5gd" id="fGt-aM-WYN"/>
</connections>
</menuItem>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG"> <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections> <connections>
<action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/> <action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
@ -143,7 +148,7 @@
<modifierMask key="keyEquivalentModifierMask"/> <modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ"> <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items> <items>
<menuItem title="Procreate Viewer Help" keyEquivalent="?" id="FKE-Sm-Kum"> <menuItem title="Silica Viewer Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections> <connections>
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/> <action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
</connections> </connections>
@ -157,7 +162,7 @@
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/> <outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections> </connections>
</application> </application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Procreate_Viewer" customModuleProvider="target"/> <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Silica_Viewer" customModuleProvider="target"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/> <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
<userDefaultsController representsSharedInstance="YES" id="JdS-Pg-8N9"/> <userDefaultsController representsSharedInstance="YES" id="JdS-Pg-8N9"/>
@ -167,7 +172,7 @@
<!--Info View Controller--> <!--Info View Controller-->
<scene sceneID="nJy-a4-E0d"> <scene sceneID="nJy-a4-E0d">
<objects> <objects>
<viewController showSeguePresentationStyle="single" id="wda-Mt-beD" customClass="InfoViewController" customModule="Procreate_Viewer" customModuleProvider="target" sceneMemberID="viewController"> <viewController showSeguePresentationStyle="single" id="wda-Mt-beD" customClass="InfoViewController" customModule="Silica_Viewer" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="3vu-Kd-l73"> <view key="view" id="3vu-Kd-l73">
<rect key="frame" x="0.0" y="0.0" width="480" height="109"/> <rect key="frame" x="0.0" y="0.0" width="480" height="109"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
@ -253,25 +258,18 @@ DQ
<!--Timelapse View Controller--> <!--Timelapse View Controller-->
<scene sceneID="6lP-oO-4ie"> <scene sceneID="6lP-oO-4ie">
<objects> <objects>
<viewController id="2g9-GV-hNg" customClass="TimelapseViewController" customModule="Procreate_Viewer" customModuleProvider="target" sceneMemberID="viewController"> <viewController id="2g9-GV-hNg" customClass="TimelapseViewController" customModule="Silica_Viewer" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="n7g-Sv-vdB"> <view key="view" id="n7g-Sv-vdB">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/> <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <gestureRecognizers>
<avPlayerView controlsStyle="minimal" videoGravity="resizeAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="zOe-qf-LSc"> <clickGestureRecognizer delaysPrimaryMouseButtonEvents="YES" numberOfClicksRequired="1" id="I6z-cw-DZX">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
</avPlayerView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="zOe-qf-LSc" secondAttribute="trailing" id="5e2-Uh-miL"/>
<constraint firstAttribute="bottom" secondItem="zOe-qf-LSc" secondAttribute="bottom" id="Kfx-dj-9x8"/>
<constraint firstItem="zOe-qf-LSc" firstAttribute="top" secondItem="n7g-Sv-vdB" secondAttribute="top" id="rwV-Dr-EPg"/>
<constraint firstItem="zOe-qf-LSc" firstAttribute="leading" secondItem="n7g-Sv-vdB" secondAttribute="leading" id="uY5-WX-NhA"/>
</constraints>
</view>
<connections> <connections>
<outlet property="playerView" destination="zOe-qf-LSc" id="Iy7-O4-v4P"/> <action selector="clickAction:" target="2g9-GV-hNg" id="322-pp-TuC"/>
</connections> </connections>
</clickGestureRecognizer>
</gestureRecognizers>
</view>
</viewController> </viewController>
<customObject id="LO0-g9-QLL" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="LO0-g9-QLL" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects> </objects>
@ -301,7 +299,7 @@ DQ
<!--View Controller--> <!--View Controller-->
<scene sceneID="hIz-AP-VOD"> <scene sceneID="hIz-AP-VOD">
<objects> <objects>
<viewController id="5gI-5U-AMq" customClass="ViewController" customModule="Procreate_Viewer" customModuleProvider="target" sceneMemberID="viewController"> <viewController id="5gI-5U-AMq" customClass="ViewController" customModule="Silica_Viewer" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="ERx-hH-rdd"> <view key="view" id="ERx-hH-rdd">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/> <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>

View file

@ -1,28 +1,15 @@
import Cocoa import Cocoa
import ZIPFoundation import ZIPFoundation
import CoreFoundation import CoreFoundation
import Accelerate
import CoreMedia
struct SilicaChunk { struct SilicaChunk {
var x: Int = 0 var x: Int = 0
var y: Int = 0 var y: Int = 0
var image: CGImage? var image: NSImage = NSImage()
}
struct SilicaLayerData {
var blendMode: Int = 0
var extendedBlend: Int = 0
var chunks: [SilicaChunk] = []
var opacity: Double = 1.0
var hidden: Bool = false
} }
struct SilicaLayer { struct SilicaLayer {
var name: String = "" var chunks: [SilicaChunk] = []
var data: SilicaLayerData = SilicaLayerData()
var mask: SilicaLayerData?
var clipped: Bool = false
} }
struct SilicaDocument { struct SilicaDocument {
@ -31,31 +18,11 @@ struct SilicaDocument {
var orientation: Int = 0 var orientation: Int = 0
var flippedHorizontally: Bool = false var flippedHorizontally: Bool = false
var flippedVertically: 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 width: Int = 0
var height: Int = 0 var height: Int = 0
var layers: [SilicaLayer] = [] 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 { func objectRefGetValue2(_ objectRef: CFTypeRef) -> UInt32 {
@ -87,10 +54,6 @@ class Document: NSDocument {
self.addWindowController(windowController) 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. 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. 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 width: Int = info.tileSize
var height: Int = info.tileSize var height: Int = info.tileSize
@ -149,138 +112,16 @@ class Document: NSDocument {
} }
} }
func getChunkRect(_ chunk: SilicaChunk) -> NSRect { func parseSilicaLayer(archive: Archive, dict: NSDictionary) {
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? {
let objectsArray = self.dict?["$objects"] as! NSArray let objectsArray = self.dict?["$objects"] as! NSArray
if getDocumentClassName(dict: dict) == LayerClassName { if getDocumentClassName(dict: dict) == LayerClassName {
var layer = SilicaLayer() 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 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
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] = [] var chunkPaths: [String] = []
archive.forEach { (entry: Entry) in 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 dispatchGroup = DispatchGroup()
let queue = DispatchQueue(label: "imageWork") let queue = DispatchQueue(label: "imageWork")
DispatchQueue.concurrentPerform(iterations: chunkPaths.count) { (i: Int) in 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 return
} }
@ -305,10 +148,8 @@ class Document: NSDocument {
return return
} }
let (width, height) = getTileSize(x, y) let (width, height) = getTileSize(x: x, y: y)
let byteSize = width * height * 4
let numChannels = isMask ? 1 : 4
let byteSize = width * height * numChannels
let uncompressedMemory = UnsafeMutablePointer<UInt8>.allocate(capacity: byteSize) let uncompressedMemory = UnsafeMutablePointer<UInt8>.allocate(capacity: byteSize)
@ -325,38 +166,28 @@ 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
let rgbColorSpace = isMask ? CGColorSpaceCreateDeviceGray() : info.colorSpace let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue).union(.byteOrder32Big)
let bitmapInfo = CGBitmapInfo(rawValue: (isMask ? CGImageAlphaInfo.none : CGImageAlphaInfo.premultipliedLast).rawValue).union(.byteOrder32Big)
let providerRef: CGDataProvider? = CGDataProvider(data: imageData as CFData) 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 return
} }
queue.async(group: dispatchGroup) { let image = NSImage(cgImage: cgimage, size: NSZeroSize)
layer.data.chunks[i].image = cgimage
layer.data.chunks[i].x = x queue.async(flags: .barrier) {
layer.data.chunks[i].y = y layer.chunks[i].image = image
layer.chunks[i].x = x
layer.chunks[i].y = y
dispatchGroup.leave()
} }
} }
dispatchGroup.wait() dispatchGroup.wait()
return layer info.layers.append(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
} }
} }
@ -370,70 +201,19 @@ 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 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 sizeClassKey = dict[SizeKey]
let sizeClassID = getClassID(id: sizeClassKey) let sizeClassID = getClassID(id: sizeClassKey)
let sizeString = objectsArray[sizeClassID] as! String let sizeString = objectsArray[sizeClassID] as! String
let (width, height) = parsePairString(sizeString)! let sizeComponents = sizeString.replacingOccurrences(of: "{", with: "").replacingOccurrences(of: "}", with: "").components(separatedBy: ", ")
info.width = width let width = Int(sizeComponents[0])
info.height = height let height = Int(sizeComponents[1])
info.width = width!
info.height = height!
columns = Int(ceil(Float(info.width) / Float(info.tileSize))) 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 { if info.width % info.tileSize != 0 {
remainderWidth = (columns * info.tileSize) - info.width remainderWidth = (columns * info.tileSize) - info.width
@ -449,14 +229,11 @@ 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
guard let layer = parseSilicaLayer(archive: archive, dict: layerClass, isMask: false) else { return } parseSilicaLayer(archive: archive, dict: layerClass)
info.layers.append(layer)
} }
} }
} }
@ -482,134 +259,54 @@ class Document: NSDocument {
} }
} }
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 { override func read(from data: Data, ofType typeName: String) throws {
self.data = data self.data = data
guard let archive = Archive(data: data, accessMode: Archive.AccessMode.read) else { guard let archive = Archive(data: data, accessMode: Archive.AccessMode.read) else {
throwError(.invalid)
return return
} }
guard let documentEntry = archive[DocumentArchivePath] else { guard let documentEntry = archive[DocumentArchivePath] else {
throwError(.invalid)
return return
} }
guard let documentData = readData(archive: archive, entry: documentEntry) else { guard let documentData = readData(archive: archive, entry: documentEntry) else {
throwError(.invalid)
return return
} }
var plistFormat = PropertyListSerialization.PropertyListFormat.binary var plistFormat = PropertyListSerialization.PropertyListFormat.binary
guard let propertyList = try? PropertyListSerialization.propertyList(from: documentData, options: [], format: &plistFormat) else { guard let propertyList = try? PropertyListSerialization.propertyList(from: documentData, options: [], format: &plistFormat) else {
throwError(.invalid)
return return
} }
parseDocument(archive: archive, dict: propertyList as! NSDictionary) parseDocument(archive: archive, dict: propertyList as! NSDictionary)
} }
func makeComposite() -> NSImage? { func makeComposite() -> NSImage {
// create the final composite output image var image = NSImage(size: NSSize(width: info.width, height: info.height))
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big) image.lockFocus()
let ccgContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: info.colorSpace, bitmapInfo: bitmapInfo.rawValue) let color = NSColor.white
color.drawSwatch(in: NSRect(origin: .zero, size: image.size))
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
for layer in info.layers.reversed() { for layer in info.layers.reversed() {
if !layer.data.hidden { for chunk in layer.chunks {
// start by creating a new layer composite image, needed for image masking let x = chunk.x
let layerContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: info.colorSpace, bitmapInfo: bitmapInfo.rawValue) var y = chunk.y
layerContext?.clear(info.cgRect) let (width, height) = getTileSize(x: x, y: y)
var maskContext: CGContext? if y == rows {
y = 0
}
let kernel = getBlendKernel(layer) let rect = NSRect(x: info.tileSize * x, y: info.height - (info.tileSize * y), width: width, height: height)
if layer.mask != nil { chunk.image.draw(in: rect)
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))
} }
} }
for chunk in layer.data.chunks { image.unlockFocus()
layerContext?.setAlpha(CGFloat(layer.data.opacity))
layerContext?.setBlendMode(.normal)
if !layer.data.hidden {
layerContext?.draw(chunk.image!, in: getChunkRect(chunk))
}
}
let layerImage = layerContext?.makeImage()
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)!
}
}
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)
if info.orientation == 3 { if info.orientation == 3 {
image = image.imageRotatedByDegreess(degrees: 90) image = image.imageRotatedByDegreess(degrees: 90)
@ -711,35 +408,3 @@ public extension NSImage {
return flipedImage 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()
}
}

View file

@ -14,9 +14,11 @@
<key>CFBundleTypeIconFile</key> <key>CFBundleTypeIconFile</key>
<string></string> <string></string>
<key>CFBundleTypeName</key> <key>CFBundleTypeName</key>
<string></string> <string>com.proceate</string>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Viewer</string> <string>Viewer</string>
<key>LSHandlerRank</key>
<string>Default</string>
<key>LSItemContentTypes</key> <key>LSItemContentTypes</key>
<array> <array>
<string>com.procreate</string> <string>com.procreate</string>
@ -42,13 +44,13 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string> <string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string> <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2020 Joshua Goins. All rights reserved.</string> <string>Copyright © 2021 Joshua Goins. All rights reserved.</string>
<key>NSMainStoryboardFile</key> <key>NSMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>

View file

@ -0,0 +1,8 @@
#include <stdint.h>
#include "minilzo.h"
uint32_t valueForKeyedArchiverUID(uint64_t keyedArchiverUID) {
void *uid = (void*)keyedArchiverUID;
uint32_t *valuePtr = uid+16;
return *valuePtr;
}

View file

@ -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()
}
}

View file

@ -7,7 +7,7 @@ class ViewController: NSViewController {
override func viewWillAppear() { override func viewWillAppear() {
let document = self.view.window?.windowController?.document as? Document 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?) { override func prepare(for segue: NSStoryboardSegue, sender: Any?) {

View file

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>1.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string> <string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSExtension</key> <key>NSExtension</key>