├── .gitignore ├── APNGb ├── APNGb.xcodeproj │ └── project.pbxproj ├── APNGb │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── 1024.png │ │ │ ├── 128.png │ │ │ ├── 16.png │ │ │ ├── 256.png │ │ │ ├── 32.png │ │ │ ├── 512.png │ │ │ ├── 64.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── SideBarItemIcons │ │ │ ├── Contents.json │ │ │ ├── assembly-default.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── assembly-default.png │ │ │ │ ├── assembly-default@2x.png │ │ │ │ └── assembly-default@3x.png │ │ │ ├── assembly-selected.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── assembly-selected.png │ │ │ │ ├── assembly-selected@2x.png │ │ │ │ └── assembly-selected@3x.png │ │ │ ├── disassembly-default.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── disassembly-default.png │ │ │ │ ├── disassembly-default@2x.png │ │ │ │ └── disassembly-default@3x.png │ │ │ └── disassembly-selected.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── disassembly-selected.png │ │ │ │ ├── disassembly-selected@2x.png │ │ │ │ └── disassembly-selected@3x.png │ │ ├── StartStop │ │ │ ├── Contents.json │ │ │ ├── start.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── play.png │ │ │ │ ├── play@2x.png │ │ │ │ └── play@3x.png │ │ │ └── stop.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── stop.png │ │ │ │ ├── stop@2x.png │ │ │ │ └── stop@3x.png │ │ └── reorder-cell.imageset │ │ │ ├── Contents.json │ │ │ ├── reorder-cell.png │ │ │ ├── reorder-cell@2x.png │ │ │ └── reorder-cell@3x.png │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Cells │ │ ├── AssemblyFrameCellView.swift │ │ └── AssemblyFrameRowView.swift │ ├── CustomViews │ │ ├── ActionToolbar.swift │ │ ├── DragAndDropValidator.swift │ │ ├── DragAndDropView.swift │ │ └── DragAndDropWebView.swift │ ├── DataSource │ │ └── StripPopUpDataSource.swift │ ├── Executables │ │ ├── apngasm │ │ └── apngdis │ ├── Extensions │ │ ├── BoolExtension.swift │ │ ├── FileManagerExtension.swift │ │ ├── NSTableViewExtension.swift │ │ ├── NSViewControllerExtension.swift │ │ ├── NSViewExtension.swift │ │ └── StringExtension.swift │ ├── Info.plist │ ├── Layout │ │ ├── ChildContainerViewLayoutCareTaker.swift │ │ ├── ChildViewLayoutCareTaker.swift │ │ ├── MainContainerViewLayoutCareTaker.swift │ │ └── PreferencesViewLayoutCareTaker.swift │ ├── Library │ │ ├── Argument.swift │ │ ├── AssemblyArguments.swift │ │ ├── AssemblyProcess.swift │ │ ├── Command.swift │ │ ├── Compression.swift │ │ ├── DisassemblyArguments.swift │ │ ├── DisassemblyProcess.swift │ │ ├── ExecutableProcess.swift │ │ ├── FrameDelay.swift │ │ ├── Optimization.swift │ │ ├── Playback.swift │ │ └── Strip.swift │ ├── Models │ │ └── AnimationFrame.swift │ ├── Resources │ │ └── Credits.rtf │ ├── Utilities │ │ ├── Closure.swift │ │ ├── DirectoryManager.swift │ │ ├── Resource.swift │ │ ├── Theme.swift │ │ ├── ViewControllerId.swift │ │ └── ViewSize.swift │ └── ViewControllers │ │ ├── AssemblyPreferencesViewController.swift │ │ ├── AssemblyViewController.swift │ │ ├── ChildContainerViewController.swift │ │ ├── DisassemblyPreferencesViewController.swift │ │ ├── DisassemblyViewController.swift │ │ ├── DropHintViewController.swift │ │ ├── MainContainerViewController.swift │ │ ├── MainWindowController.swift │ │ ├── PreferencesContainerViewController.swift │ │ ├── SideBarItemGroup.swift │ │ └── SideBarViewController.swift └── APNGbTests │ └── Info.plist ├── Additional-Resources ├── apng-examples │ ├── DancingPeaks.gif │ ├── bouncing_beach_ball.png │ ├── elephant.png │ ├── firefox.png │ └── mechanism.png └── design │ └── icon │ ├── app-icon.sketch │ └── app-logo.sketch ├── LICENSE ├── README.md └── credits.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /APNGb/APNGb.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AB04ACFC1E040E62002D2B40 /* MainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB04ACFB1E040E62002D2B40 /* MainWindowController.swift */; }; 11 | AB04AD041E046A58002D2B40 /* AssemblyFrameRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB04AD031E046A58002D2B40 /* AssemblyFrameRowView.swift */; }; 12 | AB0FD07E1E04B8F70023E528 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB0FD07D1E04B8F70023E528 /* Theme.swift */; }; 13 | AB0FD0811E04B93D0023E528 /* ViewControllerId.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB0FD07F1E04B93D0023E528 /* ViewControllerId.swift */; }; 14 | AB0FD0821E04B93D0023E528 /* ViewSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB0FD0801E04B93D0023E528 /* ViewSize.swift */; }; 15 | AB2C94E11DAEAD7D008DB042 /* apngasm in Resources */ = {isa = PBXBuildFile; fileRef = AB2C94DF1DAEAD7D008DB042 /* apngasm */; }; 16 | AB2C94E51DAEAD89008DB042 /* apngdis in Resources */ = {isa = PBXBuildFile; fileRef = AB2C94E31DAEAD89008DB042 /* apngdis */; }; 17 | AB2D0AB91DC929C600F3B425 /* DragAndDropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB2D0AB81DC929C600F3B425 /* DragAndDropView.swift */; }; 18 | AB2D0ABB1DC95A7500F3B425 /* DragAndDropValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB2D0ABA1DC95A7500F3B425 /* DragAndDropValidator.swift */; }; 19 | AB36BB411DB17AA3001F4DDD /* AssemblyArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB36BB401DB17AA3001F4DDD /* AssemblyArguments.swift */; }; 20 | AB36BB441DB17BFF001F4DDD /* Playback.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB36BB431DB17BFF001F4DDD /* Playback.swift */; }; 21 | AB36BB461DB17D9E001F4DDD /* Optimization.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB36BB451DB17D9E001F4DDD /* Optimization.swift */; }; 22 | AB36BB481DB17E24001F4DDD /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB36BB471DB17E24001F4DDD /* Compression.swift */; }; 23 | AB36BB4A1DB183E3001F4DDD /* FrameDelay.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB36BB491DB183E3001F4DDD /* FrameDelay.swift */; }; 24 | AB3DF2301D8DDE660048BE0A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB3DF22F1D8DDE660048BE0A /* AppDelegate.swift */; }; 25 | AB3DF2341D8DDE660048BE0A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AB3DF2331D8DDE660048BE0A /* Assets.xcassets */; }; 26 | AB3DF2371D8DDE660048BE0A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AB3DF2351D8DDE660048BE0A /* Main.storyboard */; }; 27 | AB702A5C1E0F1FA2003E8FD5 /* Argument.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB702A5B1E0F1FA2003E8FD5 /* Argument.swift */; }; 28 | AB702A5F1E0F283F003E8FD5 /* Strip.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB702A5E1E0F283F003E8FD5 /* Strip.swift */; }; 29 | AB702A6D1E112246003E8FD5 /* StripPopUpDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB702A6C1E112246003E8FD5 /* StripPopUpDataSource.swift */; }; 30 | AB702A6F1E112272003E8FD5 /* ActionToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB702A6E1E112272003E8FD5 /* ActionToolbar.swift */; }; 31 | AB7B53C61DF81023005C8539 /* SideBarItemGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7B53C51DF81023005C8539 /* SideBarItemGroup.swift */; }; 32 | AB7B53C81DF831D4005C8539 /* NSViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7B53C71DF831D4005C8539 /* NSViewControllerExtension.swift */; }; 33 | AB7B53CC1DF865E6005C8539 /* Closure.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7B53CB1DF865E6005C8539 /* Closure.swift */; }; 34 | AB7B53D21DF88A2E005C8539 /* PreferencesContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7B53D11DF88A2E005C8539 /* PreferencesContainerViewController.swift */; }; 35 | AB7B53D51DF983D2005C8539 /* DropHintViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7B53D41DF983D2005C8539 /* DropHintViewController.swift */; }; 36 | AB8CFEBE1DFB092700691ECC /* MainContainerViewLayoutCareTaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB8CFEBD1DFB092700691ECC /* MainContainerViewLayoutCareTaker.swift */; }; 37 | AB8CFEC01DFB327E00691ECC /* ChildContainerViewLayoutCareTaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB8CFEBF1DFB327E00691ECC /* ChildContainerViewLayoutCareTaker.swift */; }; 38 | AB8CFEC41DFB537D00691ECC /* DisassemblyPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB8CFEC31DFB537D00691ECC /* DisassemblyPreferencesViewController.swift */; }; 39 | AB97CAC91DA1600300A6A272 /* AssemblyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB97CAC51DA1600300A6A272 /* AssemblyViewController.swift */; }; 40 | AB97CACA1DA1600300A6A272 /* AssemblyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB97CAC51DA1600300A6A272 /* AssemblyViewController.swift */; }; 41 | AB97CACB1DA1600300A6A272 /* DisassemblyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB97CAC61DA1600300A6A272 /* DisassemblyViewController.swift */; }; 42 | AB97CACC1DA1600300A6A272 /* DisassemblyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB97CAC61DA1600300A6A272 /* DisassemblyViewController.swift */; }; 43 | AB9830331DB79C57009E0254 /* DisassemblyArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB9830321DB79C57009E0254 /* DisassemblyArguments.swift */; }; 44 | AB98303A1DB7B065009E0254 /* FileManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB9830391DB7B065009E0254 /* FileManagerExtension.swift */; }; 45 | ABABA26B1DF77A0600A90962 /* MainContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABABA26A1DF77A0600A90962 /* MainContainerViewController.swift */; }; 46 | ABABA26D1DF77A3600A90962 /* SideBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABABA26C1DF77A3600A90962 /* SideBarViewController.swift */; }; 47 | ABABA26F1DF77A8300A90962 /* ChildContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABABA26E1DF77A8300A90962 /* ChildContainerViewController.swift */; }; 48 | ABABA2711DF77BC300A90962 /* NSViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABABA2701DF77BC300A90962 /* NSViewExtension.swift */; }; 49 | ABB54AAC1E0988DF00779F50 /* Resource.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABB54AAB1E0988DF00779F50 /* Resource.swift */; }; 50 | ABB54AB01E09951A00779F50 /* AssemblyProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABB54AAF1E09951A00779F50 /* AssemblyProcess.swift */; }; 51 | ABB54AB21E09954000779F50 /* DisassemblyProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABB54AB11E09954000779F50 /* DisassemblyProcess.swift */; }; 52 | ABB54AB41E0AB43A00779F50 /* DirectoryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABB54AB31E0AB43A00779F50 /* DirectoryManager.swift */; }; 53 | ABB75E601E114B13006CEC4B /* AssemblyPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABB75E5F1E114B13006CEC4B /* AssemblyPreferencesViewController.swift */; }; 54 | ABB75E621E11F250006CEC4B /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = ABB75E611E11F250006CEC4B /* Credits.rtf */; }; 55 | ABB75E681E142400006CEC4B /* NSTableViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABB75E671E142400006CEC4B /* NSTableViewExtension.swift */; }; 56 | ABD680771DB8EBB200A4735E /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD680761DB8EBB200A4735E /* StringExtension.swift */; }; 57 | ABD8C3711DB52D3E00C74183 /* BoolExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD8C3701DB52D3E00C74183 /* BoolExtension.swift */; }; 58 | ABD8C3741DB58A2400C74183 /* AnimationFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABD8C3731DB58A2400C74183 /* AnimationFrame.swift */; }; 59 | ABF0D9B61E0578F0008AF40B /* DragAndDropWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF0D9B51E0578F0008AF40B /* DragAndDropWebView.swift */; }; 60 | ABF362AD1DFC32F200F8903A /* PreferencesViewLayoutCareTaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF362AC1DFC32F200F8903A /* PreferencesViewLayoutCareTaker.swift */; }; 61 | ABF4090F1DA9BB5800B0D373 /* ExecutableProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF4090E1DA9BB5800B0D373 /* ExecutableProcess.swift */; }; 62 | ABF409171DAAF35000B0D373 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF409161DAAF35000B0D373 /* Command.swift */; }; 63 | ABF443FD1E02C0D800751DB6 /* ChildViewLayoutCareTaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF443FC1E02C0D800751DB6 /* ChildViewLayoutCareTaker.swift */; }; 64 | ABF444011E032FAD00751DB6 /* AssemblyFrameCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABF444001E032FAD00751DB6 /* AssemblyFrameCellView.swift */; }; 65 | /* End PBXBuildFile section */ 66 | 67 | /* Begin PBXContainerItemProxy section */ 68 | AB3DF23E1D8DDE660048BE0A /* PBXContainerItemProxy */ = { 69 | isa = PBXContainerItemProxy; 70 | containerPortal = AB3DF2241D8DDE660048BE0A /* Project object */; 71 | proxyType = 1; 72 | remoteGlobalIDString = AB3DF22B1D8DDE660048BE0A; 73 | remoteInfo = APNGb; 74 | }; 75 | /* End PBXContainerItemProxy section */ 76 | 77 | /* Begin PBXFileReference section */ 78 | AB04ACFB1E040E62002D2B40 /* MainWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MainWindowController.swift; path = ViewControllers/MainWindowController.swift; sourceTree = ""; }; 79 | AB04AD031E046A58002D2B40 /* AssemblyFrameRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AssemblyFrameRowView.swift; path = Cells/AssemblyFrameRowView.swift; sourceTree = ""; }; 80 | AB0FD07D1E04B8F70023E528 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = Utilities/Theme.swift; sourceTree = ""; }; 81 | AB0FD07F1E04B93D0023E528 /* ViewControllerId.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewControllerId.swift; path = Utilities/ViewControllerId.swift; sourceTree = ""; }; 82 | AB0FD0801E04B93D0023E528 /* ViewSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewSize.swift; path = Utilities/ViewSize.swift; sourceTree = ""; }; 83 | AB2C94DF1DAEAD7D008DB042 /* apngasm */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = apngasm; path = APNGb/Executables/apngasm; sourceTree = SOURCE_ROOT; }; 84 | AB2C94E31DAEAD89008DB042 /* apngdis */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = apngdis; path = APNGb/Executables/apngdis; sourceTree = SOURCE_ROOT; }; 85 | AB2D0AB81DC929C600F3B425 /* DragAndDropView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DragAndDropView.swift; path = CustomViews/DragAndDropView.swift; sourceTree = ""; }; 86 | AB2D0ABA1DC95A7500F3B425 /* DragAndDropValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DragAndDropValidator.swift; path = CustomViews/DragAndDropValidator.swift; sourceTree = ""; }; 87 | AB36BB401DB17AA3001F4DDD /* AssemblyArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AssemblyArguments.swift; path = Library/AssemblyArguments.swift; sourceTree = ""; }; 88 | AB36BB431DB17BFF001F4DDD /* Playback.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Playback.swift; path = Library/Playback.swift; sourceTree = ""; }; 89 | AB36BB451DB17D9E001F4DDD /* Optimization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Optimization.swift; path = Library/Optimization.swift; sourceTree = ""; }; 90 | AB36BB471DB17E24001F4DDD /* Compression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Compression.swift; path = Library/Compression.swift; sourceTree = ""; }; 91 | AB36BB491DB183E3001F4DDD /* FrameDelay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FrameDelay.swift; path = Library/FrameDelay.swift; sourceTree = ""; }; 92 | AB3DF22C1D8DDE660048BE0A /* APNGb.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = APNGb.app; sourceTree = BUILT_PRODUCTS_DIR; }; 93 | AB3DF22F1D8DDE660048BE0A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 94 | AB3DF2331D8DDE660048BE0A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 95 | AB3DF2361D8DDE660048BE0A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 96 | AB3DF2381D8DDE660048BE0A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 97 | AB3DF23D1D8DDE660048BE0A /* APNGbTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = APNGbTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 98 | AB3DF2431D8DDE660048BE0A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 99 | AB702A5B1E0F1FA2003E8FD5 /* Argument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Argument.swift; path = Library/Argument.swift; sourceTree = ""; }; 100 | AB702A5E1E0F283F003E8FD5 /* Strip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Strip.swift; path = Library/Strip.swift; sourceTree = ""; }; 101 | AB702A6C1E112246003E8FD5 /* StripPopUpDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StripPopUpDataSource.swift; path = DataSource/StripPopUpDataSource.swift; sourceTree = ""; }; 102 | AB702A6E1E112272003E8FD5 /* ActionToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ActionToolbar.swift; path = CustomViews/ActionToolbar.swift; sourceTree = ""; }; 103 | AB7B53C51DF81023005C8539 /* SideBarItemGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideBarItemGroup.swift; path = ViewControllers/SideBarItemGroup.swift; sourceTree = ""; }; 104 | AB7B53C71DF831D4005C8539 /* NSViewControllerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSViewControllerExtension.swift; path = Extensions/NSViewControllerExtension.swift; sourceTree = ""; }; 105 | AB7B53CB1DF865E6005C8539 /* Closure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Closure.swift; path = Utilities/Closure.swift; sourceTree = ""; }; 106 | AB7B53D11DF88A2E005C8539 /* PreferencesContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferencesContainerViewController.swift; path = ViewControllers/PreferencesContainerViewController.swift; sourceTree = ""; }; 107 | AB7B53D41DF983D2005C8539 /* DropHintViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DropHintViewController.swift; path = ViewControllers/DropHintViewController.swift; sourceTree = ""; }; 108 | AB8CFEBD1DFB092700691ECC /* MainContainerViewLayoutCareTaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MainContainerViewLayoutCareTaker.swift; path = Layout/MainContainerViewLayoutCareTaker.swift; sourceTree = ""; }; 109 | AB8CFEBF1DFB327E00691ECC /* ChildContainerViewLayoutCareTaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChildContainerViewLayoutCareTaker.swift; path = Layout/ChildContainerViewLayoutCareTaker.swift; sourceTree = ""; }; 110 | AB8CFEC31DFB537D00691ECC /* DisassemblyPreferencesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisassemblyPreferencesViewController.swift; path = ViewControllers/DisassemblyPreferencesViewController.swift; sourceTree = ""; }; 111 | AB97CAC51DA1600300A6A272 /* AssemblyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AssemblyViewController.swift; path = ViewControllers/AssemblyViewController.swift; sourceTree = ""; }; 112 | AB97CAC61DA1600300A6A272 /* DisassemblyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisassemblyViewController.swift; path = ViewControllers/DisassemblyViewController.swift; sourceTree = ""; }; 113 | AB9830321DB79C57009E0254 /* DisassemblyArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisassemblyArguments.swift; path = Library/DisassemblyArguments.swift; sourceTree = ""; }; 114 | AB9830391DB7B065009E0254 /* FileManagerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FileManagerExtension.swift; path = Extensions/FileManagerExtension.swift; sourceTree = ""; }; 115 | ABABA26A1DF77A0600A90962 /* MainContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MainContainerViewController.swift; path = ViewControllers/MainContainerViewController.swift; sourceTree = ""; }; 116 | ABABA26C1DF77A3600A90962 /* SideBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SideBarViewController.swift; path = ViewControllers/SideBarViewController.swift; sourceTree = ""; }; 117 | ABABA26E1DF77A8300A90962 /* ChildContainerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChildContainerViewController.swift; path = ViewControllers/ChildContainerViewController.swift; sourceTree = ""; }; 118 | ABABA2701DF77BC300A90962 /* NSViewExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSViewExtension.swift; path = Extensions/NSViewExtension.swift; sourceTree = ""; }; 119 | ABB54AAB1E0988DF00779F50 /* Resource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Resource.swift; path = Utilities/Resource.swift; sourceTree = ""; }; 120 | ABB54AAF1E09951A00779F50 /* AssemblyProcess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AssemblyProcess.swift; path = Library/AssemblyProcess.swift; sourceTree = ""; }; 121 | ABB54AB11E09954000779F50 /* DisassemblyProcess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DisassemblyProcess.swift; path = Library/DisassemblyProcess.swift; sourceTree = ""; }; 122 | ABB54AB31E0AB43A00779F50 /* DirectoryManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DirectoryManager.swift; path = Utilities/DirectoryManager.swift; sourceTree = ""; }; 123 | ABB75E5F1E114B13006CEC4B /* AssemblyPreferencesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AssemblyPreferencesViewController.swift; path = ViewControllers/AssemblyPreferencesViewController.swift; sourceTree = ""; }; 124 | ABB75E611E11F250006CEC4B /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; name = Credits.rtf; path = Resources/Credits.rtf; sourceTree = ""; }; 125 | ABB75E671E142400006CEC4B /* NSTableViewExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSTableViewExtension.swift; path = Extensions/NSTableViewExtension.swift; sourceTree = ""; }; 126 | ABD680761DB8EBB200A4735E /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StringExtension.swift; path = Extensions/StringExtension.swift; sourceTree = ""; }; 127 | ABD8C3701DB52D3E00C74183 /* BoolExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BoolExtension.swift; path = Extensions/BoolExtension.swift; sourceTree = ""; }; 128 | ABD8C3731DB58A2400C74183 /* AnimationFrame.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnimationFrame.swift; path = Models/AnimationFrame.swift; sourceTree = ""; }; 129 | ABF0D9B51E0578F0008AF40B /* DragAndDropWebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DragAndDropWebView.swift; path = CustomViews/DragAndDropWebView.swift; sourceTree = ""; }; 130 | ABF362AC1DFC32F200F8903A /* PreferencesViewLayoutCareTaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreferencesViewLayoutCareTaker.swift; path = Layout/PreferencesViewLayoutCareTaker.swift; sourceTree = ""; }; 131 | ABF4090E1DA9BB5800B0D373 /* ExecutableProcess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ExecutableProcess.swift; path = Library/ExecutableProcess.swift; sourceTree = ""; }; 132 | ABF409161DAAF35000B0D373 /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Command.swift; path = Library/Command.swift; sourceTree = ""; }; 133 | ABF443FC1E02C0D800751DB6 /* ChildViewLayoutCareTaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChildViewLayoutCareTaker.swift; path = Layout/ChildViewLayoutCareTaker.swift; sourceTree = ""; }; 134 | ABF444001E032FAD00751DB6 /* AssemblyFrameCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AssemblyFrameCellView.swift; path = Cells/AssemblyFrameCellView.swift; sourceTree = ""; }; 135 | /* End PBXFileReference section */ 136 | 137 | /* Begin PBXFrameworksBuildPhase section */ 138 | AB3DF2291D8DDE660048BE0A /* Frameworks */ = { 139 | isa = PBXFrameworksBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | AB3DF23A1D8DDE660048BE0A /* Frameworks */ = { 146 | isa = PBXFrameworksBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXFrameworksBuildPhase section */ 153 | 154 | /* Begin PBXGroup section */ 155 | AB04ACFD1E044932002D2B40 /* TableViewCell */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | ABF444001E032FAD00751DB6 /* AssemblyFrameCellView.swift */, 159 | AB04AD031E046A58002D2B40 /* AssemblyFrameRowView.swift */, 160 | ); 161 | name = TableViewCell; 162 | sourceTree = ""; 163 | }; 164 | AB04ACFE1E04493D002D2B40 /* Layout */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | ABF443FC1E02C0D800751DB6 /* ChildViewLayoutCareTaker.swift */, 168 | ); 169 | name = Layout; 170 | sourceTree = ""; 171 | }; 172 | AB04ACFF1E0450BC002D2B40 /* Layout */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | AB8CFEBF1DFB327E00691ECC /* ChildContainerViewLayoutCareTaker.swift */, 176 | ); 177 | name = Layout; 178 | sourceTree = ""; 179 | }; 180 | AB04AD001E0450C7002D2B40 /* Layout */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | ABF362AC1DFC32F200F8903A /* PreferencesViewLayoutCareTaker.swift */, 184 | ); 185 | name = Layout; 186 | sourceTree = ""; 187 | }; 188 | AB04AD011E0450D0002D2B40 /* Childs */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | ABB75E5F1E114B13006CEC4B /* AssemblyPreferencesViewController.swift */, 192 | AB8CFEC31DFB537D00691ECC /* DisassemblyPreferencesViewController.swift */, 193 | AB702A6B1E112205003E8FD5 /* SubviewsDataSource */, 194 | ); 195 | name = Childs; 196 | sourceTree = ""; 197 | }; 198 | AB04AD021E0450DF002D2B40 /* Layout */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | AB8CFEBD1DFB092700691ECC /* MainContainerViewLayoutCareTaker.swift */, 202 | ); 203 | name = Layout; 204 | sourceTree = ""; 205 | }; 206 | AB0FD0831E04B96A0023E528 /* WindowControllers */ = { 207 | isa = PBXGroup; 208 | children = ( 209 | AB04ACFB1E040E62002D2B40 /* MainWindowController.swift */, 210 | ); 211 | name = WindowControllers; 212 | sourceTree = ""; 213 | }; 214 | AB0FD0841E04B9A70023E528 /* ViewController */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | AB0FD0801E04B93D0023E528 /* ViewSize.swift */, 218 | AB0FD07F1E04B93D0023E528 /* ViewControllerId.swift */, 219 | ); 220 | name = ViewController; 221 | sourceTree = ""; 222 | }; 223 | AB2B42FA1DAFEA61001E054A /* Storyboards */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | AB3DF2351D8DDE660048BE0A /* Main.storyboard */, 227 | ); 228 | name = Storyboards; 229 | sourceTree = ""; 230 | }; 231 | AB2C94E91DAEBD25008DB042 /* CustomViews */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | AB702A6E1E112272003E8FD5 /* ActionToolbar.swift */, 235 | AB2D0AB81DC929C600F3B425 /* DragAndDropView.swift */, 236 | ABF0D9B51E0578F0008AF40B /* DragAndDropWebView.swift */, 237 | AB2D0ABA1DC95A7500F3B425 /* DragAndDropValidator.swift */, 238 | ); 239 | name = CustomViews; 240 | sourceTree = ""; 241 | }; 242 | AB36BB421DB17BC2001F4DDD /* AssemblyArguments */ = { 243 | isa = PBXGroup; 244 | children = ( 245 | AB36BB401DB17AA3001F4DDD /* AssemblyArguments.swift */, 246 | AB36BB431DB17BFF001F4DDD /* Playback.swift */, 247 | AB36BB451DB17D9E001F4DDD /* Optimization.swift */, 248 | AB36BB471DB17E24001F4DDD /* Compression.swift */, 249 | AB36BB491DB183E3001F4DDD /* FrameDelay.swift */, 250 | AB702A5E1E0F283F003E8FD5 /* Strip.swift */, 251 | ); 252 | name = AssemblyArguments; 253 | sourceTree = ""; 254 | }; 255 | AB3DF2231D8DDE660048BE0A = { 256 | isa = PBXGroup; 257 | children = ( 258 | AB3DF22E1D8DDE660048BE0A /* APNGb */, 259 | AB3DF2401D8DDE660048BE0A /* APNGbTests */, 260 | AB3DF22D1D8DDE660048BE0A /* Products */, 261 | ); 262 | sourceTree = ""; 263 | }; 264 | AB3DF22D1D8DDE660048BE0A /* Products */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | AB3DF22C1D8DDE660048BE0A /* APNGb.app */, 268 | AB3DF23D1D8DDE660048BE0A /* APNGbTests.xctest */, 269 | ); 270 | name = Products; 271 | sourceTree = ""; 272 | }; 273 | AB3DF22E1D8DDE660048BE0A /* APNGb */ = { 274 | isa = PBXGroup; 275 | children = ( 276 | AB3DF22F1D8DDE660048BE0A /* AppDelegate.swift */, 277 | AB0FD0831E04B96A0023E528 /* WindowControllers */, 278 | AB3DF24C1D8E0D400048BE0A /* ViewControllers */, 279 | AB98302B1DB78048009E0254 /* Utilities */, 280 | ABF4090D1DA9B92700B0D373 /* Library */, 281 | AB9830261DB69069009E0254 /* Models */, 282 | AB2C94E91DAEBD25008DB042 /* CustomViews */, 283 | ABD8C36F1DB52CE000C74183 /* Extensions */, 284 | AB97CB561DA3093D00A6A272 /* Executables */, 285 | AB3DF24E1D8E0D590048BE0A /* Resources */, 286 | AB3DF24D1D8E0D4C0048BE0A /* Others */, 287 | ); 288 | path = APNGb; 289 | sourceTree = ""; 290 | }; 291 | AB3DF2401D8DDE660048BE0A /* APNGbTests */ = { 292 | isa = PBXGroup; 293 | children = ( 294 | AB3DF2431D8DDE660048BE0A /* Info.plist */, 295 | ); 296 | path = APNGbTests; 297 | sourceTree = ""; 298 | }; 299 | AB3DF24C1D8E0D400048BE0A /* ViewControllers */ = { 300 | isa = PBXGroup; 301 | children = ( 302 | ABF362AE1DFC4A9300F8903A /* MainContainer */, 303 | AB7B53CD1DF86717005C8539 /* SideBar */, 304 | AB7B53CE1DF86728005C8539 /* ChildContainer */, 305 | AB7B53D31DF8D91E005C8539 /* PreferencesPane */, 306 | AB7B53D01DF867B7005C8539 /* Loose */, 307 | ); 308 | name = ViewControllers; 309 | sourceTree = ""; 310 | }; 311 | AB3DF24D1D8E0D4C0048BE0A /* Others */ = { 312 | isa = PBXGroup; 313 | children = ( 314 | AB3DF2381D8DDE660048BE0A /* Info.plist */, 315 | ); 316 | name = Others; 317 | sourceTree = ""; 318 | }; 319 | AB3DF24E1D8E0D590048BE0A /* Resources */ = { 320 | isa = PBXGroup; 321 | children = ( 322 | ABB75E611E11F250006CEC4B /* Credits.rtf */, 323 | AB3DF2331D8DDE660048BE0A /* Assets.xcassets */, 324 | AB2B42FA1DAFEA61001E054A /* Storyboards */, 325 | ); 326 | name = Resources; 327 | sourceTree = ""; 328 | }; 329 | AB702A5D1E0F1FAA003E8FD5 /* Process */ = { 330 | isa = PBXGroup; 331 | children = ( 332 | ABF4090E1DA9BB5800B0D373 /* ExecutableProcess.swift */, 333 | ABB54AAF1E09951A00779F50 /* AssemblyProcess.swift */, 334 | ABB54AB11E09954000779F50 /* DisassemblyProcess.swift */, 335 | ); 336 | name = Process; 337 | sourceTree = ""; 338 | }; 339 | AB702A6B1E112205003E8FD5 /* SubviewsDataSource */ = { 340 | isa = PBXGroup; 341 | children = ( 342 | AB702A6C1E112246003E8FD5 /* StripPopUpDataSource.swift */, 343 | ); 344 | name = SubviewsDataSource; 345 | sourceTree = ""; 346 | }; 347 | AB7B53CD1DF86717005C8539 /* SideBar */ = { 348 | isa = PBXGroup; 349 | children = ( 350 | AB7B53C51DF81023005C8539 /* SideBarItemGroup.swift */, 351 | ABABA26C1DF77A3600A90962 /* SideBarViewController.swift */, 352 | ); 353 | name = SideBar; 354 | sourceTree = ""; 355 | }; 356 | AB7B53CE1DF86728005C8539 /* ChildContainer */ = { 357 | isa = PBXGroup; 358 | children = ( 359 | ABABA26E1DF77A8300A90962 /* ChildContainerViewController.swift */, 360 | AB04ACFF1E0450BC002D2B40 /* Layout */, 361 | AB7B53CF1DF86732005C8539 /* ChildViewControllers */, 362 | ); 363 | name = ChildContainer; 364 | sourceTree = ""; 365 | }; 366 | AB7B53CF1DF86732005C8539 /* ChildViewControllers */ = { 367 | isa = PBXGroup; 368 | children = ( 369 | AB97CAC51DA1600300A6A272 /* AssemblyViewController.swift */, 370 | AB97CAC61DA1600300A6A272 /* DisassemblyViewController.swift */, 371 | AB04ACFE1E04493D002D2B40 /* Layout */, 372 | AB04ACFD1E044932002D2B40 /* TableViewCell */, 373 | ); 374 | name = ChildViewControllers; 375 | sourceTree = ""; 376 | }; 377 | AB7B53D01DF867B7005C8539 /* Loose */ = { 378 | isa = PBXGroup; 379 | children = ( 380 | AB7B53D41DF983D2005C8539 /* DropHintViewController.swift */, 381 | ); 382 | name = Loose; 383 | sourceTree = ""; 384 | }; 385 | AB7B53D31DF8D91E005C8539 /* PreferencesPane */ = { 386 | isa = PBXGroup; 387 | children = ( 388 | AB7B53D11DF88A2E005C8539 /* PreferencesContainerViewController.swift */, 389 | AB04AD011E0450D0002D2B40 /* Childs */, 390 | AB04AD001E0450C7002D2B40 /* Layout */, 391 | ); 392 | name = PreferencesPane; 393 | sourceTree = ""; 394 | }; 395 | AB8F4A0A1DADA0CA0002C73D /* Assembler */ = { 396 | isa = PBXGroup; 397 | children = ( 398 | AB2C94DF1DAEAD7D008DB042 /* apngasm */, 399 | ); 400 | name = Assembler; 401 | sourceTree = ""; 402 | }; 403 | AB8F4A0B1DADA0DA0002C73D /* Disassembler */ = { 404 | isa = PBXGroup; 405 | children = ( 406 | AB2C94E31DAEAD89008DB042 /* apngdis */, 407 | ); 408 | name = Disassembler; 409 | sourceTree = ""; 410 | }; 411 | AB97CB561DA3093D00A6A272 /* Executables */ = { 412 | isa = PBXGroup; 413 | children = ( 414 | AB8F4A0A1DADA0CA0002C73D /* Assembler */, 415 | AB8F4A0B1DADA0DA0002C73D /* Disassembler */, 416 | ); 417 | name = Executables; 418 | path = Scripts; 419 | sourceTree = ""; 420 | }; 421 | AB9830261DB69069009E0254 /* Models */ = { 422 | isa = PBXGroup; 423 | children = ( 424 | ABD8C3731DB58A2400C74183 /* AnimationFrame.swift */, 425 | ); 426 | name = Models; 427 | sourceTree = ""; 428 | }; 429 | AB98302B1DB78048009E0254 /* Utilities */ = { 430 | isa = PBXGroup; 431 | children = ( 432 | AB0FD07D1E04B8F70023E528 /* Theme.swift */, 433 | AB7B53CB1DF865E6005C8539 /* Closure.swift */, 434 | ABB54AAB1E0988DF00779F50 /* Resource.swift */, 435 | ABB54AB31E0AB43A00779F50 /* DirectoryManager.swift */, 436 | AB0FD0841E04B9A70023E528 /* ViewController */, 437 | ); 438 | name = Utilities; 439 | sourceTree = ""; 440 | }; 441 | AB9830311DB79C14009E0254 /* DisassemblyArguments */ = { 442 | isa = PBXGroup; 443 | children = ( 444 | AB9830321DB79C57009E0254 /* DisassemblyArguments.swift */, 445 | ); 446 | name = DisassemblyArguments; 447 | sourceTree = ""; 448 | }; 449 | ABD8C36F1DB52CE000C74183 /* Extensions */ = { 450 | isa = PBXGroup; 451 | children = ( 452 | ABD8C3701DB52D3E00C74183 /* BoolExtension.swift */, 453 | AB9830391DB7B065009E0254 /* FileManagerExtension.swift */, 454 | ABD680761DB8EBB200A4735E /* StringExtension.swift */, 455 | ABABA2701DF77BC300A90962 /* NSViewExtension.swift */, 456 | AB7B53C71DF831D4005C8539 /* NSViewControllerExtension.swift */, 457 | ABB75E671E142400006CEC4B /* NSTableViewExtension.swift */, 458 | ); 459 | name = Extensions; 460 | sourceTree = ""; 461 | }; 462 | ABF362AE1DFC4A9300F8903A /* MainContainer */ = { 463 | isa = PBXGroup; 464 | children = ( 465 | ABABA26A1DF77A0600A90962 /* MainContainerViewController.swift */, 466 | AB04AD021E0450DF002D2B40 /* Layout */, 467 | ); 468 | name = MainContainer; 469 | sourceTree = ""; 470 | }; 471 | ABF4090D1DA9B92700B0D373 /* Library */ = { 472 | isa = PBXGroup; 473 | children = ( 474 | ABF409161DAAF35000B0D373 /* Command.swift */, 475 | AB702A5B1E0F1FA2003E8FD5 /* Argument.swift */, 476 | AB702A5D1E0F1FAA003E8FD5 /* Process */, 477 | AB36BB421DB17BC2001F4DDD /* AssemblyArguments */, 478 | AB9830311DB79C14009E0254 /* DisassemblyArguments */, 479 | ); 480 | name = Library; 481 | sourceTree = ""; 482 | }; 483 | /* End PBXGroup section */ 484 | 485 | /* Begin PBXNativeTarget section */ 486 | AB3DF22B1D8DDE660048BE0A /* APNGb */ = { 487 | isa = PBXNativeTarget; 488 | buildConfigurationList = AB3DF2461D8DDE660048BE0A /* Build configuration list for PBXNativeTarget "APNGb" */; 489 | buildPhases = ( 490 | AB3DF2281D8DDE660048BE0A /* Sources */, 491 | AB3DF2291D8DDE660048BE0A /* Frameworks */, 492 | AB3DF22A1D8DDE660048BE0A /* Resources */, 493 | ); 494 | buildRules = ( 495 | ); 496 | dependencies = ( 497 | ); 498 | name = APNGb; 499 | productName = APNGb; 500 | productReference = AB3DF22C1D8DDE660048BE0A /* APNGb.app */; 501 | productType = "com.apple.product-type.application"; 502 | }; 503 | AB3DF23C1D8DDE660048BE0A /* APNGbTests */ = { 504 | isa = PBXNativeTarget; 505 | buildConfigurationList = AB3DF2491D8DDE660048BE0A /* Build configuration list for PBXNativeTarget "APNGbTests" */; 506 | buildPhases = ( 507 | AB3DF2391D8DDE660048BE0A /* Sources */, 508 | AB3DF23A1D8DDE660048BE0A /* Frameworks */, 509 | AB3DF23B1D8DDE660048BE0A /* Resources */, 510 | ); 511 | buildRules = ( 512 | ); 513 | dependencies = ( 514 | AB3DF23F1D8DDE660048BE0A /* PBXTargetDependency */, 515 | ); 516 | name = APNGbTests; 517 | productName = APNGbTests; 518 | productReference = AB3DF23D1D8DDE660048BE0A /* APNGbTests.xctest */; 519 | productType = "com.apple.product-type.bundle.unit-test"; 520 | }; 521 | /* End PBXNativeTarget section */ 522 | 523 | /* Begin PBXProject section */ 524 | AB3DF2241D8DDE660048BE0A /* Project object */ = { 525 | isa = PBXProject; 526 | attributes = { 527 | LastSwiftUpdateCheck = 0800; 528 | LastUpgradeCheck = 0910; 529 | ORGANIZATIONNAME = "Godoroja Stefan"; 530 | TargetAttributes = { 531 | AB3DF22B1D8DDE660048BE0A = { 532 | CreatedOnToolsVersion = 8.0; 533 | DevelopmentTeam = 7YX3D8UMCU; 534 | ProvisioningStyle = Automatic; 535 | }; 536 | AB3DF23C1D8DDE660048BE0A = { 537 | CreatedOnToolsVersion = 8.0; 538 | DevelopmentTeam = Q453Q9XLFV; 539 | ProvisioningStyle = Automatic; 540 | TestTargetID = AB3DF22B1D8DDE660048BE0A; 541 | }; 542 | }; 543 | }; 544 | buildConfigurationList = AB3DF2271D8DDE660048BE0A /* Build configuration list for PBXProject "APNGb" */; 545 | compatibilityVersion = "Xcode 3.2"; 546 | developmentRegion = English; 547 | hasScannedForEncodings = 0; 548 | knownRegions = ( 549 | en, 550 | Base, 551 | ); 552 | mainGroup = AB3DF2231D8DDE660048BE0A; 553 | productRefGroup = AB3DF22D1D8DDE660048BE0A /* Products */; 554 | projectDirPath = ""; 555 | projectRoot = ""; 556 | targets = ( 557 | AB3DF22B1D8DDE660048BE0A /* APNGb */, 558 | AB3DF23C1D8DDE660048BE0A /* APNGbTests */, 559 | ); 560 | }; 561 | /* End PBXProject section */ 562 | 563 | /* Begin PBXResourcesBuildPhase section */ 564 | AB3DF22A1D8DDE660048BE0A /* Resources */ = { 565 | isa = PBXResourcesBuildPhase; 566 | buildActionMask = 2147483647; 567 | files = ( 568 | ABB75E621E11F250006CEC4B /* Credits.rtf in Resources */, 569 | AB2C94E11DAEAD7D008DB042 /* apngasm in Resources */, 570 | AB2C94E51DAEAD89008DB042 /* apngdis in Resources */, 571 | AB3DF2341D8DDE660048BE0A /* Assets.xcassets in Resources */, 572 | AB3DF2371D8DDE660048BE0A /* Main.storyboard in Resources */, 573 | ); 574 | runOnlyForDeploymentPostprocessing = 0; 575 | }; 576 | AB3DF23B1D8DDE660048BE0A /* Resources */ = { 577 | isa = PBXResourcesBuildPhase; 578 | buildActionMask = 2147483647; 579 | files = ( 580 | ); 581 | runOnlyForDeploymentPostprocessing = 0; 582 | }; 583 | /* End PBXResourcesBuildPhase section */ 584 | 585 | /* Begin PBXSourcesBuildPhase section */ 586 | AB3DF2281D8DDE660048BE0A /* Sources */ = { 587 | isa = PBXSourcesBuildPhase; 588 | buildActionMask = 2147483647; 589 | files = ( 590 | AB36BB461DB17D9E001F4DDD /* Optimization.swift in Sources */, 591 | ABABA26B1DF77A0600A90962 /* MainContainerViewController.swift in Sources */, 592 | AB98303A1DB7B065009E0254 /* FileManagerExtension.swift in Sources */, 593 | ABABA2711DF77BC300A90962 /* NSViewExtension.swift in Sources */, 594 | ABF0D9B61E0578F0008AF40B /* DragAndDropWebView.swift in Sources */, 595 | AB0FD0811E04B93D0023E528 /* ViewControllerId.swift in Sources */, 596 | ABB75E681E142400006CEC4B /* NSTableViewExtension.swift in Sources */, 597 | AB04ACFC1E040E62002D2B40 /* MainWindowController.swift in Sources */, 598 | AB7B53C61DF81023005C8539 /* SideBarItemGroup.swift in Sources */, 599 | AB36BB481DB17E24001F4DDD /* Compression.swift in Sources */, 600 | ABB54AB21E09954000779F50 /* DisassemblyProcess.swift in Sources */, 601 | AB2D0AB91DC929C600F3B425 /* DragAndDropView.swift in Sources */, 602 | AB04AD041E046A58002D2B40 /* AssemblyFrameRowView.swift in Sources */, 603 | AB97CAC91DA1600300A6A272 /* AssemblyViewController.swift in Sources */, 604 | AB0FD07E1E04B8F70023E528 /* Theme.swift in Sources */, 605 | AB36BB4A1DB183E3001F4DDD /* FrameDelay.swift in Sources */, 606 | AB97CACB1DA1600300A6A272 /* DisassemblyViewController.swift in Sources */, 607 | ABD680771DB8EBB200A4735E /* StringExtension.swift in Sources */, 608 | ABF4090F1DA9BB5800B0D373 /* ExecutableProcess.swift in Sources */, 609 | ABF444011E032FAD00751DB6 /* AssemblyFrameCellView.swift in Sources */, 610 | AB7B53D51DF983D2005C8539 /* DropHintViewController.swift in Sources */, 611 | ABB75E601E114B13006CEC4B /* AssemblyPreferencesViewController.swift in Sources */, 612 | AB2D0ABB1DC95A7500F3B425 /* DragAndDropValidator.swift in Sources */, 613 | AB36BB411DB17AA3001F4DDD /* AssemblyArguments.swift in Sources */, 614 | ABABA26D1DF77A3600A90962 /* SideBarViewController.swift in Sources */, 615 | AB8CFEC01DFB327E00691ECC /* ChildContainerViewLayoutCareTaker.swift in Sources */, 616 | ABD8C3711DB52D3E00C74183 /* BoolExtension.swift in Sources */, 617 | AB7B53D21DF88A2E005C8539 /* PreferencesContainerViewController.swift in Sources */, 618 | ABF362AD1DFC32F200F8903A /* PreferencesViewLayoutCareTaker.swift in Sources */, 619 | AB702A6D1E112246003E8FD5 /* StripPopUpDataSource.swift in Sources */, 620 | ABB54AB41E0AB43A00779F50 /* DirectoryManager.swift in Sources */, 621 | AB3DF2301D8DDE660048BE0A /* AppDelegate.swift in Sources */, 622 | AB9830331DB79C57009E0254 /* DisassemblyArguments.swift in Sources */, 623 | ABD8C3741DB58A2400C74183 /* AnimationFrame.swift in Sources */, 624 | AB36BB441DB17BFF001F4DDD /* Playback.swift in Sources */, 625 | ABB54AAC1E0988DF00779F50 /* Resource.swift in Sources */, 626 | AB0FD0821E04B93D0023E528 /* ViewSize.swift in Sources */, 627 | AB702A5F1E0F283F003E8FD5 /* Strip.swift in Sources */, 628 | ABB54AB01E09951A00779F50 /* AssemblyProcess.swift in Sources */, 629 | AB8CFEC41DFB537D00691ECC /* DisassemblyPreferencesViewController.swift in Sources */, 630 | AB7B53CC1DF865E6005C8539 /* Closure.swift in Sources */, 631 | ABF409171DAAF35000B0D373 /* Command.swift in Sources */, 632 | AB7B53C81DF831D4005C8539 /* NSViewControllerExtension.swift in Sources */, 633 | ABF443FD1E02C0D800751DB6 /* ChildViewLayoutCareTaker.swift in Sources */, 634 | ABABA26F1DF77A8300A90962 /* ChildContainerViewController.swift in Sources */, 635 | AB702A5C1E0F1FA2003E8FD5 /* Argument.swift in Sources */, 636 | AB8CFEBE1DFB092700691ECC /* MainContainerViewLayoutCareTaker.swift in Sources */, 637 | AB702A6F1E112272003E8FD5 /* ActionToolbar.swift in Sources */, 638 | ); 639 | runOnlyForDeploymentPostprocessing = 0; 640 | }; 641 | AB3DF2391D8DDE660048BE0A /* Sources */ = { 642 | isa = PBXSourcesBuildPhase; 643 | buildActionMask = 2147483647; 644 | files = ( 645 | AB97CACA1DA1600300A6A272 /* AssemblyViewController.swift in Sources */, 646 | AB97CACC1DA1600300A6A272 /* DisassemblyViewController.swift in Sources */, 647 | ); 648 | runOnlyForDeploymentPostprocessing = 0; 649 | }; 650 | /* End PBXSourcesBuildPhase section */ 651 | 652 | /* Begin PBXTargetDependency section */ 653 | AB3DF23F1D8DDE660048BE0A /* PBXTargetDependency */ = { 654 | isa = PBXTargetDependency; 655 | target = AB3DF22B1D8DDE660048BE0A /* APNGb */; 656 | targetProxy = AB3DF23E1D8DDE660048BE0A /* PBXContainerItemProxy */; 657 | }; 658 | /* End PBXTargetDependency section */ 659 | 660 | /* Begin PBXVariantGroup section */ 661 | AB3DF2351D8DDE660048BE0A /* Main.storyboard */ = { 662 | isa = PBXVariantGroup; 663 | children = ( 664 | AB3DF2361D8DDE660048BE0A /* Base */, 665 | ); 666 | name = Main.storyboard; 667 | sourceTree = ""; 668 | }; 669 | /* End PBXVariantGroup section */ 670 | 671 | /* Begin XCBuildConfiguration section */ 672 | AB3DF2441D8DDE660048BE0A /* Debug */ = { 673 | isa = XCBuildConfiguration; 674 | buildSettings = { 675 | ALWAYS_SEARCH_USER_PATHS = NO; 676 | CLANG_ANALYZER_NONNULL = YES; 677 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 678 | CLANG_CXX_LIBRARY = "libc++"; 679 | CLANG_ENABLE_MODULES = YES; 680 | CLANG_ENABLE_OBJC_ARC = YES; 681 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 682 | CLANG_WARN_BOOL_CONVERSION = YES; 683 | CLANG_WARN_COMMA = YES; 684 | CLANG_WARN_CONSTANT_CONVERSION = YES; 685 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 686 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 687 | CLANG_WARN_EMPTY_BODY = YES; 688 | CLANG_WARN_ENUM_CONVERSION = YES; 689 | CLANG_WARN_INFINITE_RECURSION = YES; 690 | CLANG_WARN_INT_CONVERSION = YES; 691 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 692 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 693 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 694 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 695 | CLANG_WARN_STRICT_PROTOTYPES = YES; 696 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 697 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 698 | CLANG_WARN_UNREACHABLE_CODE = YES; 699 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 700 | CODE_SIGN_IDENTITY = "-"; 701 | COPY_PHASE_STRIP = NO; 702 | DEBUG_INFORMATION_FORMAT = dwarf; 703 | ENABLE_STRICT_OBJC_MSGSEND = YES; 704 | ENABLE_TESTABILITY = YES; 705 | GCC_C_LANGUAGE_STANDARD = gnu99; 706 | GCC_DYNAMIC_NO_PIC = NO; 707 | GCC_NO_COMMON_BLOCKS = YES; 708 | GCC_OPTIMIZATION_LEVEL = 0; 709 | GCC_PREPROCESSOR_DEFINITIONS = ( 710 | "DEBUG=1", 711 | "$(inherited)", 712 | ); 713 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 714 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 715 | GCC_WARN_UNDECLARED_SELECTOR = YES; 716 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 717 | GCC_WARN_UNUSED_FUNCTION = YES; 718 | GCC_WARN_UNUSED_VARIABLE = YES; 719 | MACOSX_DEPLOYMENT_TARGET = 10.11; 720 | MTL_ENABLE_DEBUG_INFO = YES; 721 | ONLY_ACTIVE_ARCH = YES; 722 | SDKROOT = macosx; 723 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 724 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 725 | }; 726 | name = Debug; 727 | }; 728 | AB3DF2451D8DDE660048BE0A /* Release */ = { 729 | isa = XCBuildConfiguration; 730 | buildSettings = { 731 | ALWAYS_SEARCH_USER_PATHS = NO; 732 | CLANG_ANALYZER_NONNULL = YES; 733 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 734 | CLANG_CXX_LIBRARY = "libc++"; 735 | CLANG_ENABLE_MODULES = YES; 736 | CLANG_ENABLE_OBJC_ARC = YES; 737 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 738 | CLANG_WARN_BOOL_CONVERSION = YES; 739 | CLANG_WARN_COMMA = YES; 740 | CLANG_WARN_CONSTANT_CONVERSION = YES; 741 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 742 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 743 | CLANG_WARN_EMPTY_BODY = YES; 744 | CLANG_WARN_ENUM_CONVERSION = YES; 745 | CLANG_WARN_INFINITE_RECURSION = YES; 746 | CLANG_WARN_INT_CONVERSION = YES; 747 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 748 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 749 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 750 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 751 | CLANG_WARN_STRICT_PROTOTYPES = YES; 752 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 753 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 754 | CLANG_WARN_UNREACHABLE_CODE = YES; 755 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 756 | CODE_SIGN_IDENTITY = "-"; 757 | COPY_PHASE_STRIP = NO; 758 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 759 | ENABLE_NS_ASSERTIONS = NO; 760 | ENABLE_STRICT_OBJC_MSGSEND = YES; 761 | GCC_C_LANGUAGE_STANDARD = gnu99; 762 | GCC_NO_COMMON_BLOCKS = YES; 763 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 764 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 765 | GCC_WARN_UNDECLARED_SELECTOR = YES; 766 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 767 | GCC_WARN_UNUSED_FUNCTION = YES; 768 | GCC_WARN_UNUSED_VARIABLE = YES; 769 | MACOSX_DEPLOYMENT_TARGET = 10.11; 770 | MTL_ENABLE_DEBUG_INFO = NO; 771 | SDKROOT = macosx; 772 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 773 | }; 774 | name = Release; 775 | }; 776 | AB3DF2471D8DDE660048BE0A /* Debug */ = { 777 | isa = XCBuildConfiguration; 778 | buildSettings = { 779 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 780 | CODE_SIGN_IDENTITY = "Mac Developer"; 781 | COMBINE_HIDPI_IMAGES = YES; 782 | DEVELOPMENT_TEAM = 7YX3D8UMCU; 783 | INFOPLIST_FILE = APNGb/Info.plist; 784 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 785 | MACOSX_DEPLOYMENT_TARGET = 10.11; 786 | OTHER_SWIFT_FLAGS = "-DDebug"; 787 | PRODUCT_BUNDLE_IDENTIFIER = gs.godorojastefan.APNGb; 788 | PRODUCT_NAME = "$(TARGET_NAME)"; 789 | SWIFT_VERSION = 3.0; 790 | }; 791 | name = Debug; 792 | }; 793 | AB3DF2481D8DDE660048BE0A /* Release */ = { 794 | isa = XCBuildConfiguration; 795 | buildSettings = { 796 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 797 | CODE_SIGN_IDENTITY = "Mac Developer"; 798 | COMBINE_HIDPI_IMAGES = YES; 799 | DEVELOPMENT_TEAM = 7YX3D8UMCU; 800 | INFOPLIST_FILE = APNGb/Info.plist; 801 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 802 | MACOSX_DEPLOYMENT_TARGET = 10.11; 803 | OTHER_SWIFT_FLAGS = ""; 804 | PRODUCT_BUNDLE_IDENTIFIER = gs.godorojastefan.APNGb; 805 | PRODUCT_NAME = "$(TARGET_NAME)"; 806 | SWIFT_VERSION = 3.0; 807 | }; 808 | name = Release; 809 | }; 810 | AB3DF24A1D8DDE660048BE0A /* Debug */ = { 811 | isa = XCBuildConfiguration; 812 | buildSettings = { 813 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 814 | BUNDLE_LOADER = "$(TEST_HOST)"; 815 | COMBINE_HIDPI_IMAGES = YES; 816 | DEVELOPMENT_TEAM = Q453Q9XLFV; 817 | INFOPLIST_FILE = APNGbTests/Info.plist; 818 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 819 | PRODUCT_BUNDLE_IDENTIFIER = gs.godorojastefan.APNGbTests; 820 | PRODUCT_NAME = "$(TARGET_NAME)"; 821 | SWIFT_VERSION = 3.0; 822 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APNGb.app/Contents/MacOS/APNGb"; 823 | }; 824 | name = Debug; 825 | }; 826 | AB3DF24B1D8DDE660048BE0A /* Release */ = { 827 | isa = XCBuildConfiguration; 828 | buildSettings = { 829 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 830 | BUNDLE_LOADER = "$(TEST_HOST)"; 831 | COMBINE_HIDPI_IMAGES = YES; 832 | DEVELOPMENT_TEAM = Q453Q9XLFV; 833 | INFOPLIST_FILE = APNGbTests/Info.plist; 834 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 835 | PRODUCT_BUNDLE_IDENTIFIER = gs.godorojastefan.APNGbTests; 836 | PRODUCT_NAME = "$(TARGET_NAME)"; 837 | SWIFT_VERSION = 3.0; 838 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/APNGb.app/Contents/MacOS/APNGb"; 839 | }; 840 | name = Release; 841 | }; 842 | /* End XCBuildConfiguration section */ 843 | 844 | /* Begin XCConfigurationList section */ 845 | AB3DF2271D8DDE660048BE0A /* Build configuration list for PBXProject "APNGb" */ = { 846 | isa = XCConfigurationList; 847 | buildConfigurations = ( 848 | AB3DF2441D8DDE660048BE0A /* Debug */, 849 | AB3DF2451D8DDE660048BE0A /* Release */, 850 | ); 851 | defaultConfigurationIsVisible = 0; 852 | defaultConfigurationName = Release; 853 | }; 854 | AB3DF2461D8DDE660048BE0A /* Build configuration list for PBXNativeTarget "APNGb" */ = { 855 | isa = XCConfigurationList; 856 | buildConfigurations = ( 857 | AB3DF2471D8DDE660048BE0A /* Debug */, 858 | AB3DF2481D8DDE660048BE0A /* Release */, 859 | ); 860 | defaultConfigurationIsVisible = 0; 861 | defaultConfigurationName = Release; 862 | }; 863 | AB3DF2491D8DDE660048BE0A /* Build configuration list for PBXNativeTarget "APNGbTests" */ = { 864 | isa = XCConfigurationList; 865 | buildConfigurations = ( 866 | AB3DF24A1D8DDE660048BE0A /* Debug */, 867 | AB3DF24B1D8DDE660048BE0A /* Release */, 868 | ); 869 | defaultConfigurationIsVisible = 0; 870 | defaultConfigurationName = Release; 871 | }; 872 | /* End XCConfigurationList section */ 873 | }; 874 | rootObject = AB3DF2241D8DDE660048BE0A /* Project object */; 875 | } 876 | -------------------------------------------------------------------------------- /APNGb/APNGb/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 9/17/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | final class AppDelegate: NSObject, NSApplicationDelegate { } 13 | -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-default.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "assembly-default.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "assembly-default@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "assembly-default@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-default.imageset/assembly-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-default.imageset/assembly-default.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-default.imageset/assembly-default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-default.imageset/assembly-default@2x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-default.imageset/assembly-default@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-default.imageset/assembly-default@3x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "assembly-selected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "assembly-selected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "assembly-selected@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-selected.imageset/assembly-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-selected.imageset/assembly-selected.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-selected.imageset/assembly-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-selected.imageset/assembly-selected@2x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-selected.imageset/assembly-selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/assembly-selected.imageset/assembly-selected@3x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-default.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "disassembly-default.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "disassembly-default@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "disassembly-default@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-default.imageset/disassembly-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-default.imageset/disassembly-default.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-default.imageset/disassembly-default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-default.imageset/disassembly-default@2x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-default.imageset/disassembly-default@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-default.imageset/disassembly-default@3x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "disassembly-selected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "disassembly-selected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "disassembly-selected@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-selected.imageset/disassembly-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-selected.imageset/disassembly-selected.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-selected.imageset/disassembly-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-selected.imageset/disassembly-selected@2x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-selected.imageset/disassembly-selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/SideBarItemIcons/disassembly-selected.imageset/disassembly-selected@3x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/start.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "play.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "play@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "play@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/start.imageset/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/StartStop/start.imageset/play.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/start.imageset/play@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/StartStop/start.imageset/play@2x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/start.imageset/play@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/StartStop/start.imageset/play@3x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/stop.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "stop.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "stop@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "stop@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/stop.imageset/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/StartStop/stop.imageset/stop.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/stop.imageset/stop@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/StartStop/stop.imageset/stop@2x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/StartStop/stop.imageset/stop@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/StartStop/stop.imageset/stop@3x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/reorder-cell.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "reorder-cell.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "reorder-cell@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "reorder-cell@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/reorder-cell.imageset/reorder-cell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/reorder-cell.imageset/reorder-cell.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/reorder-cell.imageset/reorder-cell@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/reorder-cell.imageset/reorder-cell@2x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Assets.xcassets/reorder-cell.imageset/reorder-cell@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Assets.xcassets/reorder-cell.imageset/reorder-cell@3x.png -------------------------------------------------------------------------------- /APNGb/APNGb/Cells/AssemblyFrameCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssemblyFrameCellView.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/15/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class AssemblyFrameCellView: NSTableCellView { 12 | 13 | @IBOutlet var nameTextField: NSTextField! 14 | @IBOutlet var sizeTextField: NSTextField! 15 | @IBOutlet var delayTextField: NSTextField! 16 | 17 | static func height() -> CGFloat { 18 | return 60.0 19 | } 20 | 21 | static func identifier() -> String { 22 | return "assembly.frame.cell" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /APNGb/APNGb/Cells/AssemblyFrameRowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssemblyFrameRowView.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/16/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class AssemblyFrameRowView: NSTableRowView { 12 | 13 | override func drawSelection(in dirtyRect: NSRect) { 14 | 15 | if self.selectionHighlightStyle != .none { 16 | let selectionRect = NSInsetRect(self.bounds, 0, 0) 17 | Theme.Color.assemblyFrameCellBorderColor.setStroke() 18 | Theme.Color.assemblyFrameCellBackgroundColor.setFill() 19 | let selectionPath = NSBezierPath.init(roundedRect: selectionRect, 20 | xRadius: 0, 21 | yRadius: 0) 22 | selectionPath.fill() 23 | selectionPath.stroke() 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /APNGb/APNGb/CustomViews/ActionToolbar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActionToolbar.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/13/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | enum ProgressStatus { 12 | case None, Success, Canceled 13 | } 14 | 15 | protocol ActionToolbarDelegate { 16 | 17 | func actionWillStart() -> Bool 18 | func actionWillStop() -> Bool 19 | } 20 | 21 | final class ActionToolbar: NSToolbar { 22 | 23 | var actionDelegate: ActionToolbarDelegate? 24 | 25 | private var progressStatus: ProgressStatus = .None 26 | 27 | @IBOutlet private var progressIndicator: NSProgressIndicator! 28 | @IBOutlet private var loggingLabel: NSTextField! 29 | @IBOutlet private var startStopButton: NSButton! 30 | 31 | override func awakeFromNib() { 32 | progressIndicator.isHidden = true 33 | loggingLabel.stringValue = Resource.String.defaultToolbarLoggingMessage 34 | } 35 | 36 | // MARK: - Update UI 37 | 38 | func updateLogMessage(message: String) { 39 | DispatchQueue.main.async(execute: { 40 | self.loggingLabel.stringValue = message 41 | }) 42 | } 43 | 44 | func taskDone() { 45 | self.setInProgressMode(false) 46 | progressStatus = .Success 47 | startStopButton.state = NSOnState 48 | loggingLabel.stringValue = Resource.String.defaultToolbarLoggingMessage 49 | } 50 | 51 | // MARK: - IBActions 52 | 53 | @IBAction func onStartStopButtomPress(sender: NSButton) { 54 | 55 | if sender.state == NSOnState { 56 | 57 | if actionDelegate?.actionWillStop() == true { 58 | self.setInProgressMode(false) 59 | progressStatus = .Canceled 60 | loggingLabel.stringValue = Resource.String.defaultToolbarLoggingMessage 61 | } 62 | 63 | } else { 64 | 65 | if actionDelegate?.actionWillStart() == true { 66 | self.setInProgressMode(true) 67 | progressStatus = .None 68 | } 69 | } 70 | } 71 | 72 | // MARK: Private 73 | 74 | private func setInProgressMode(_ inProgressMode: Bool) { 75 | progressIndicator.isHidden = inProgressMode ? false : true 76 | 77 | if inProgressMode { 78 | progressIndicator.startAnimation(nil) 79 | } else { 80 | progressIndicator.stopAnimation(nil) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /APNGb/APNGb/CustomViews/DragAndDropValidator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DragAndDropValidator.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 11/2/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | struct DragAndDropValidator { 12 | 13 | var imageTypeIsValid = false 14 | var allowedFileTypes: [String] = [] 15 | 16 | init(withAllowedFileTypes fileTypes: [String]) { 17 | self.allowedFileTypes = fileTypes 18 | } 19 | 20 | mutating func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 21 | 22 | if isImageTypeAllowed(drag: sender) { 23 | imageTypeIsValid = true 24 | return .copy 25 | } else { 26 | imageTypeIsValid = false 27 | return [] 28 | } 29 | } 30 | 31 | func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { 32 | 33 | if imageTypeIsValid { 34 | return .copy 35 | } else { 36 | return [] 37 | } 38 | } 39 | 40 | func draggingResult(_ sender: NSDraggingInfo) -> [String] { 41 | 42 | if let droppedImagesPaths = sender.draggingPasteboard().propertyList(forType: NSFilenamesPboardType) as? Array { 43 | var paths: [String] = [] 44 | 45 | for imagePath in droppedImagesPaths { 46 | let imageUrl = NSURL(fileURLWithPath: imagePath) 47 | let fileExtension = imageUrl.lastPathComponent?.fileExtension() 48 | 49 | if let fileExtension = fileExtension { 50 | 51 | if allowedFileTypes.contains(fileExtension) { 52 | paths.append(imagePath) 53 | } 54 | } 55 | } 56 | 57 | return paths 58 | } 59 | 60 | return [] 61 | } 62 | 63 | /// Checks if dropped image is of necessary extension. 64 | /// 65 | /// - Parameter drag: Information about drag session. 66 | /// - Returns: `true` if image extension is legal, else returns `false`. 67 | func isImageTypeAllowed(drag: NSDraggingInfo) -> Bool { 68 | 69 | if let droppedImagesPaths = drag.draggingPasteboard().propertyList(forType: NSFilenamesPboardType) as? Array { 70 | let url = NSURL(fileURLWithPath: droppedImagesPaths[0]) 71 | 72 | if let fileExtension = url.pathExtension?.lowercased() { 73 | return allowedFileTypes.contains(fileExtension) 74 | } 75 | } 76 | 77 | return false 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /APNGb/APNGb/CustomViews/DragAndDropView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DragAndDropView.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/12/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | protocol DragAndDropDelegate { 12 | func didDropFiles(withPaths paths: [String]) 13 | } 14 | 15 | final class DragAndDropView: NSView { 16 | 17 | var delegate: DragAndDropDelegate? 18 | var allowedFileTypes: [String] = [FileExtension.png] 19 | 20 | private var validator: DragAndDropValidator 21 | 22 | required init?(coder: NSCoder) { 23 | validator = DragAndDropValidator(withAllowedFileTypes: allowedFileTypes) 24 | super.init(coder: coder) 25 | self.register(forDraggedTypes: [NSFilenamesPboardType]) 26 | } 27 | 28 | // MARK: - NSDraggingDestination 29 | 30 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 31 | return validator.draggingEntered(sender) 32 | } 33 | 34 | override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { 35 | return validator.draggingUpdated(sender) 36 | } 37 | 38 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 39 | let paths = validator.draggingResult(sender) 40 | delegate?.didDropFiles(withPaths: paths) 41 | 42 | return true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /APNGb/APNGb/CustomViews/DragAndDropWebView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DragAndDropWebView.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/17/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import WebKit 10 | 11 | final class DragAndDropWebView: WebView { 12 | 13 | var delegate: DragAndDropDelegate? 14 | 15 | private var validator: DragAndDropValidator 16 | private let allowedFileTypes = [FileExtension.png, FileExtension.apng] 17 | 18 | required init?(coder: NSCoder) { 19 | validator = DragAndDropValidator(withAllowedFileTypes: allowedFileTypes) 20 | super.init(coder: coder) 21 | } 22 | 23 | func loadImage(path: String) { 24 | let imageHTML = " ​" 25 | mainFrame.loadHTMLString(imageHTML, baseURL: nil) 26 | } 27 | 28 | // MARK: - NSDraggingDestination 29 | 30 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 31 | return validator.draggingEntered(sender) 32 | } 33 | 34 | override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { 35 | return validator.draggingUpdated(sender) 36 | } 37 | 38 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 39 | let filePaths = validator.draggingResult(sender) 40 | self.loadImage(path: filePaths[0]) 41 | delegate?.didDropFiles(withPaths: filePaths) 42 | 43 | return true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /APNGb/APNGb/DataSource/StripPopUpDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StripPopUpDataSource.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/26/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | enum StripOrientation: String { 12 | case vertical = "Vertical" 13 | case horizontal = "Horizontal" 14 | case none = "None" 15 | 16 | static func argumentValue(for orientation: String) -> String { 17 | 18 | if orientation == vertical.rawValue { 19 | return Argument.verticalStrip 20 | } else if orientation == horizontal.rawValue { 21 | return Argument.horizontalStrip 22 | } else { 23 | return String.empty 24 | } 25 | } 26 | } 27 | 28 | class StripPopUpDataSource: NSObject { 29 | 30 | let orientations = [StripOrientation.none.rawValue, 31 | StripOrientation.vertical.rawValue, 32 | StripOrientation.horizontal.rawValue] 33 | } 34 | -------------------------------------------------------------------------------- /APNGb/APNGb/Executables/apngasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Executables/apngasm -------------------------------------------------------------------------------- /APNGb/APNGb/Executables/apngdis: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/APNGb/APNGb/Executables/apngdis -------------------------------------------------------------------------------- /APNGb/APNGb/Extensions/BoolExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BoolExtension.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/17/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension Bool { 12 | 13 | init(_ num: T) { 14 | self.init(num != 0) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /APNGb/APNGb/Extensions/FileManagerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileManagerExtension.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/19/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | struct FileExtension { 12 | 13 | static let txt = "txt" 14 | static let png = "png" 15 | static let apng = "apng" 16 | } 17 | 18 | extension FileManager { 19 | 20 | func sizeOfFile(atPath path: String) -> Int { 21 | 22 | do { 23 | let fileAttributes = try self.attributesOfItem(atPath: path) as NSDictionary 24 | let fileSizeInKB = fileAttributes.fileSize() / UInt64(1000) 25 | return Int(fileSizeInKB) 26 | } catch let error { 27 | NSLog("\(#function): \(error)") 28 | return 0 29 | } 30 | } 31 | 32 | func writeToFile(content: String, filePath: String) { 33 | 34 | if let fileHandle = FileHandle(forWritingAtPath: filePath) { 35 | fileHandle.seekToEndOfFile() 36 | fileHandle.write(content.data(using: String.Encoding.utf8)!) 37 | } else { 38 | 39 | do { 40 | try content.write(toFile: filePath, 41 | atomically: true, 42 | encoding: String.Encoding.utf8) 43 | } catch let error { 44 | NSLog("\(#function): \(error)") 45 | } 46 | } 47 | } 48 | 49 | func removeItemIfExists(atPath path: String) -> Bool { 50 | 51 | if fileExists(atPath: path) { 52 | 53 | do { 54 | try removeItem(atPath: path) 55 | } catch let error { 56 | NSLog("\(#function): \(error)") 57 | return false 58 | } 59 | 60 | return true 61 | } 62 | 63 | return true 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /APNGb/APNGb/Extensions/NSTableViewExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSTableViewExtension.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/28/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSTableView { 12 | 13 | func reloadDataKeepingSelection() { 14 | let selectedRowIndexes = self.selectedRowIndexes 15 | self.reloadData() 16 | self.selectRowIndexes(selectedRowIndexes, byExtendingSelection: true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /APNGb/APNGb/Extensions/NSViewControllerExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSViewControllerExtension.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/7/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSViewController { 12 | 13 | /// Removes all child view controllers together with associated views. 14 | func removeChildViewControllers() { 15 | 16 | for childViewController in self.childViewControllers { 17 | childViewController.view.removeFromSuperview() 18 | childViewController.removeFromParentViewController() 19 | } 20 | } 21 | 22 | /// Removes all child view controllers together with associated views, ignoring 23 | /// view controllers from the parameter. 24 | /// - Parameter viewControllers: view controllers which won't be removed from 25 | /// `childviewcontrollers` stack. 26 | func removeChildViewControllersExcept(viewControllers: [NSViewController]) { 27 | 28 | for childViewController in self.childViewControllers { 29 | 30 | if viewControllers.contains(childViewController) == false { 31 | 32 | childViewController.view.removeFromSuperview() 33 | childViewController.removeFromParentViewController() 34 | } 35 | } 36 | } 37 | 38 | /// Adds child view controller to `childviewcontrollers` stack and 39 | /// it's view to current view controller's associated view. 40 | /// - Parameter identifier: Storyboard identifier of the child view controller. 41 | /// - Returns: View controller identified by storyboard identifier or `nil` if 42 | /// no view controller was associated with that identifier. 43 | func showChildViewController(withIdentifier identifier: String) -> NSViewController? { 44 | let loadedController = storyboard?.instantiateController(withIdentifier: identifier) 45 | 46 | if loadedController is NSViewController { 47 | let childViewController = loadedController as! NSViewController 48 | self.addChildViewController(childViewController) 49 | childViewController.view.translatesAutoresizingMaskIntoConstraints = false 50 | self.view.addSubview(childViewController.view) 51 | return childViewController 52 | } else { 53 | return nil 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /APNGb/APNGb/Extensions/NSViewExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSViewExtension.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/7/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension NSView { 12 | 13 | var backgroundColor: NSColor? { 14 | 15 | get { 16 | if let colorRef = self.layer?.backgroundColor { 17 | return NSColor(cgColor: colorRef) 18 | } else { 19 | return nil 20 | } 21 | } 22 | 23 | set { 24 | self.wantsLayer = true 25 | self.layer?.backgroundColor = newValue?.cgColor 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /APNGb/APNGb/Extensions/StringExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringExtension.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/20/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension String { 12 | 13 | static let dot = "." 14 | static let empty = "" 15 | static let slash = "/" 16 | static let space = " " 17 | static let colon = ":" 18 | static let kilobyteAbbreviation = "KB" 19 | 20 | func fileName() -> String { 21 | 22 | if let fileNameWithoutExtension = NSURL(fileURLWithPath: self).deletingPathExtension?.lastPathComponent { 23 | return fileNameWithoutExtension 24 | } else { 25 | return String.empty 26 | } 27 | } 28 | 29 | func fileExtension() -> String { 30 | 31 | if let fileExtension = NSURL(fileURLWithPath: self).pathExtension { 32 | return fileExtension 33 | } else { 34 | return String.empty 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /APNGb/APNGb/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 2.0.1 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2016 Godoroja Stefan. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /APNGb/APNGb/Layout/ChildContainerViewLayoutCareTaker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChildContainerViewLayoutCareTaker.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/9/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ChildContainerViewLayoutCareTaker: ViewLayoutCareTaker { 12 | 13 | let marginOffset: CGFloat = 5.0 14 | 15 | func updateLayoutOf(_ view: NSView, 16 | withIdentifier identifier: ViewControllerId, 17 | superview: NSView, 18 | andSiblingView sibling: NSView?) { 19 | 20 | switch identifier { 21 | case .Assembly, 22 | .Disassembly: 23 | view.topAnchor.constraint(equalTo: superview.topAnchor, constant: marginOffset).isActive = true 24 | view.leftAnchor.constraint(equalTo: superview.leftAnchor, constant: marginOffset).isActive = true 25 | view.rightAnchor.constraint(equalTo: superview.rightAnchor, constant: -marginOffset).isActive = true 26 | view.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: -marginOffset).isActive = true 27 | default: 28 | assertionFailure("\(#function): View constraints weren't updated because case wasn't handled") 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /APNGb/APNGb/Layout/ChildViewLayoutCareTaker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssemblyViewLayoutCareTaker.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/15/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ChildViewLayoutCareTaker: ViewLayoutCareTaker { 12 | 13 | func updateLayoutOf(_ view: NSView, withIdentifier identifier: ViewControllerId, superview: NSView, andSiblingView sibling: NSView?) { 14 | 15 | switch identifier { 16 | 17 | case .DropHint: 18 | view.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true 19 | view.leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true 20 | view.rightAnchor.constraint(equalTo: superview.rightAnchor).isActive = true 21 | view.bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true 22 | default: 23 | assertionFailure("\(#function): View constraints weren't updated because case wasn't handled") 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /APNGb/APNGb/Layout/MainContainerViewLayoutCareTaker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainContainerViewLayoutCareTaker.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/9/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | protocol ViewLayoutCareTaker { 12 | 13 | func updateLayoutOf(_ view: NSView, 14 | withIdentifier identifier: ViewControllerId, 15 | superview: NSView, 16 | andSiblingView sibling: NSView?) 17 | } 18 | 19 | class MainContainerViewLayoutCareTaker: ViewLayoutCareTaker { 20 | 21 | func updateLayoutOf(_ view: NSView, 22 | withIdentifier identifier: ViewControllerId, 23 | superview: NSView, 24 | andSiblingView sibling: NSView?) { 25 | 26 | switch identifier { 27 | 28 | case .SideBar: 29 | view.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true 30 | view.heightAnchor.constraint(equalToConstant: ViewSize.windowHeight).isActive = true 31 | view.leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true 32 | view.widthAnchor.constraint(equalToConstant: ViewSize.SideBar.width).isActive = true 33 | case .ChildContainer: 34 | view.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true 35 | view.heightAnchor.constraint(equalToConstant: ViewSize.windowHeight).isActive = true 36 | view.widthAnchor.constraint(equalToConstant: ViewSize.ChildContainer.width).isActive = true 37 | 38 | if let sibling = sibling { 39 | view.leftAnchor.constraint(equalTo: sibling.rightAnchor, constant: ViewSize.splitViewSeparatorWidth).isActive = true 40 | } 41 | 42 | case .Preferences: 43 | view.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true 44 | view.heightAnchor.constraint(equalToConstant: ViewSize.windowHeight).isActive = true 45 | view.widthAnchor.constraint(equalToConstant: ViewSize.Preferences.width).isActive = true 46 | 47 | if let sibling = sibling { 48 | view.leftAnchor.constraint(equalTo: sibling.rightAnchor, constant: ViewSize.splitViewSeparatorWidth).isActive = true 49 | } 50 | 51 | default: 52 | assertionFailure("\(#function): View constraints weren't updated because case wasn't handled") 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /APNGb/APNGb/Layout/PreferencesViewLayoutCareTaker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreferencesViewLayoutCareTaker.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/10/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class PreferencesViewLayoutCareTaker: ViewLayoutCareTaker { 12 | 13 | func updateLayoutOf(_ view: NSView, 14 | withIdentifier identifier: ViewControllerId, 15 | superview: NSView, 16 | andSiblingView sibling: NSView?) { 17 | 18 | switch identifier { 19 | 20 | case .Preferences: 21 | view.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true 22 | view.heightAnchor.constraint(equalTo: superview.heightAnchor).isActive = true 23 | view.leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true 24 | view.widthAnchor.constraint(equalTo: superview.widthAnchor).isActive = true 25 | default: 26 | assertionFailure("\(#function): View constraints weren't updated because case wasn't handled") 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/Argument.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Argument.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/24/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | struct Argument { 12 | 13 | static let numberOfLoops = "-l" 14 | static let skipFirstFrame = "-f" 15 | static let enablePalette = "-kp" 16 | static let enableColorType = "-kc" 17 | static let enableZlib = "-z0" 18 | static let enable7zip = "-z1" 19 | static let enableZopfli = "-z2" 20 | static let iteration = "-i" 21 | static let horizontalStrip = "-hs" 22 | static let verticalStrip = "-vs" 23 | } 24 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/AssemblyArguments.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssemblyArguments.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/14/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class AssemblyArguments: NSObject, CommandArgumentable, CommandExecutableProtocol { 12 | 13 | var frameNamePrefix = "apng-frame" 14 | var playback: Playback 15 | var optimization: Optimization 16 | var compression: Compression 17 | var allFramesDelay: FrameDelay 18 | var selectedFramesDelay: FrameDelay 19 | var strip: Strip 20 | var animationFrames = [AnimationFrame]() { 21 | 22 | didSet { 23 | 24 | if animationFrames.count == 0 { 25 | animatedImagePath = String.empty 26 | } else { 27 | animatedImagePath = "url-path" 28 | } 29 | } 30 | } 31 | 32 | private var animatedImagePath = String.empty 33 | 34 | override init() { 35 | playback = Playback() 36 | optimization = Optimization() 37 | compression = Compression() 38 | allFramesDelay = FrameDelay() 39 | selectedFramesDelay = FrameDelay(withCategory: .Selected, 40 | andState: false) 41 | strip = Strip() 42 | } 43 | 44 | // MARK: - CommandArgumentable 45 | 46 | func havePassedValidation() -> Bool { 47 | let arguments = commandArguments().0 48 | 49 | for argument in arguments { 50 | 51 | if argument == String.empty { 52 | return false 53 | } 54 | } 55 | 56 | return true 57 | } 58 | 59 | func commandArguments() -> ([String], Any?) { 60 | var arguments: [String] = [animatedImagePath, frameNamePrefix] 61 | arguments.append(contentsOf: playback.commandArguments().0) 62 | arguments.append(contentsOf: optimization.commandArguments().0) 63 | arguments.append(contentsOf: compression.commandArguments().0) 64 | arguments.append(contentsOf: strip.commandArguments().0) 65 | 66 | return (arguments, animationFrames) 67 | } 68 | 69 | // MARK: - CommandExecutableProtocol 70 | 71 | func commandExecutable() -> CommandExecutable { 72 | return .assembly 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/AssemblyProcess.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssemblyProcess.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/20/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class AssemblyProcess: ExecutableProcess { 12 | 13 | private let generatedAnimatedImageName = "apngb-animated.png" 14 | 15 | override init(withCommand command: Command, andAdditionalData additionalData: Any? = nil) { 16 | DirectoryManager.shared.createWorkingDirectory(forCommandExecutable: .assembly) 17 | let animatedImageNewUrl = DirectoryManager.shared.createUrlForFile(withName: generatedAnimatedImageName, 18 | forCommandExecutable: .assembly) 19 | if let animatedImageNewUrl = animatedImageNewUrl { 20 | command.arguments?[0] = animatedImageNewUrl.path 21 | 22 | // Copy frames in assembly folder. 23 | var sourceUrls: [URL] = [] 24 | var destinationUrls: [URL] = [] 25 | let frameName = command.arguments![1] 26 | let imageFrames = additionalData as! [AnimationFrame] 27 | let workingDirectory = DirectoryManager.shared.workingDirectoryUrl(forCommandExecutable: .assembly) 28 | 29 | var frameIndex = 0 30 | for frame in imageFrames { 31 | sourceUrls.append(URL(fileURLWithPath: frame.path)) 32 | 33 | let fileNameWithoutExtension = frameName + "\(frameIndex)" + String.dot 34 | let imageName = fileNameWithoutExtension + FileExtension.png 35 | let destinationUrl = workingDirectory?.appendingPathComponent(imageName) 36 | destinationUrls.append(destinationUrl!) 37 | 38 | let textFileName = fileNameWithoutExtension + FileExtension.txt 39 | let textFilePath = workingDirectory?.appendingPathComponent(textFileName).path 40 | let contentWithDelayValue = "delay=" + frame.displayableFrameDelay 41 | FileManager.default.createFile(atPath: textFilePath!, 42 | contents: contentWithDelayValue.data(using: .utf8), 43 | attributes: nil) 44 | frameIndex += 1 45 | } 46 | 47 | // Set path for source image frame argument to be one from temporary directory. 48 | command.arguments?[1] = destinationUrls[0].path 49 | 50 | DirectoryManager.shared.copyFilesInWorkingDirectory(forCommandExecutable: .assembly, 51 | atPaths: sourceUrls, 52 | toPath: destinationUrls) 53 | } else { 54 | debugPrint("\(#function): Can't create url for animated image which will be created from frames. Assembling will fail.") 55 | } 56 | 57 | super.init(withCommand: command) 58 | } 59 | 60 | override func cleanup() { 61 | DirectoryManager.shared.cleanupWorkingDirectory(forCommandExecutable: .assembly) 62 | } 63 | 64 | override func didFinishedWithSuccess(success: Bool, url: URL?) { 65 | 66 | if success { 67 | DirectoryManager.shared.moveFiles(forCommandExecutable: .assembly, 68 | toPath: url!, 69 | withNames: [generatedAnimatedImageName], 70 | toIgnore: true) 71 | } 72 | 73 | self.cleanup() 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/Command.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Command.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/9/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | /// Makes type to reset it's valuee to initial. 12 | protocol Reseatable { 13 | 14 | func reset() 15 | } 16 | 17 | /// Store UNIX executables names. 18 | enum CommandExecutable: String { 19 | case none = "" 20 | case assembly = "apngasm" 21 | case disassembly = "apngdis" 22 | } 23 | 24 | protocol CommandExecutableProtocol { 25 | 26 | /// Identifies command associated executable name. 27 | /// 28 | /// - Returns: UNIX executable name. 29 | func commandExecutable() -> CommandExecutable 30 | } 31 | 32 | /// Specifies a set of methods used by command instance's client 33 | /// to get it's arguments or information about them. 34 | protocol CommandArgumentable { 35 | 36 | /// Check if arguments have passed validation. 37 | /// 38 | /// - Returns: `true` if arguments passed validation, else returns `false`. 39 | func havePassedValidation() -> Bool 40 | 41 | /// Provide command's arguments and additional data. 42 | /// 43 | /// - Returns: Tuple containing a list of arguments and additional data instance. 44 | func commandArguments() -> ([String], Any?) 45 | } 46 | 47 | /// Describes a Terminal command. 48 | class Command: NSObject { 49 | 50 | var name: String 51 | var arguments: [String]? 52 | 53 | /// Custom initializer. 54 | /// 55 | /// - Parameter name: Name of UNIX executable. 56 | init(withExecutable executable: CommandExecutable) { 57 | self.name = executable.rawValue 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/Compression.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Compression.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/14/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class Compression: NSObject, CommandArgumentable { 12 | 13 | var enableZlib = false 14 | var enable7zip = true 15 | var enableZopfli = false 16 | var sevenZipIterations = 15 17 | var zopfliIterations = 15 18 | 19 | override func setNilValueForKey(_ key: String) { 20 | 21 | if key == #keyPath(Compression.sevenZipIterations) { 22 | sevenZipIterations = 15 23 | } 24 | 25 | if key == #keyPath(Compression.zopfliIterations) { 26 | zopfliIterations = 15 27 | } 28 | } 29 | 30 | // MARK: - KVO 31 | 32 | override func setValue(_ value: Any?, forKey key: String) { 33 | 34 | if key == #keyPath(Compression.enableZlib) { 35 | self.updateValue(value, forKeys: [#keyPath(Compression.enable7zip), 36 | #keyPath(Compression.enableZopfli)]) 37 | 38 | } else if key == #keyPath(Compression.enable7zip) { 39 | self.updateValue(value, forKeys: [#keyPath(Compression.enableZlib), 40 | #keyPath(Compression.enableZopfli)]) 41 | } else if key == #keyPath(Compression.enableZopfli) { 42 | self.updateValue(value, forKeys: [#keyPath(Compression.enableZlib), 43 | #keyPath(Compression.enable7zip)]) 44 | } 45 | 46 | super.setValue(value, forKey: key) 47 | } 48 | 49 | // MARK: - CommandArgumentable 50 | 51 | func havePassedValidation() -> Bool { 52 | return true 53 | } 54 | 55 | func commandArguments() -> ([String], Any?) { 56 | var arguments: [String] = [] 57 | 58 | if enableZlib == true { 59 | arguments.append(Argument.enableZlib) 60 | } 61 | 62 | if enable7zip == true { 63 | arguments.append(Argument.enable7zip) 64 | arguments.append(Argument.iteration + "\(sevenZipIterations)") 65 | } 66 | 67 | if enableZopfli == true { 68 | arguments.append(Argument.enableZopfli) 69 | arguments.append(Argument.iteration + "\(zopfliIterations)") 70 | } 71 | 72 | return (arguments, nil) 73 | } 74 | 75 | // MARK: - Private 76 | 77 | private func updateValue(_ value: Any?, forKeys keys: [String]) { 78 | 79 | if let value = value as? Bool { 80 | 81 | if value == true { 82 | 83 | for key in keys { 84 | self.setValue(false, forKey: key) 85 | } 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/DisassemblyArguments.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisassemblyArguments.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/19/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class DisassemblyArguments: NSObject, CommandArgumentable, CommandExecutableProtocol { 12 | 13 | var animatedImagePath = String.empty 14 | var framesNamePrefix = "frame" 15 | 16 | func isAnimatedImagePathValid() -> Bool { 17 | 18 | if animatedImagePath == String.empty { 19 | return false 20 | } else { 21 | return true 22 | } 23 | } 24 | 25 | // MARK: - CommandArgumenting 26 | 27 | func havePassedValidation() -> Bool { 28 | let arguments = commandArguments().0 29 | 30 | for argument in arguments { 31 | 32 | if argument == String.empty { 33 | return false 34 | } 35 | } 36 | 37 | return true 38 | } 39 | 40 | func commandArguments() -> ([String], Any?) { 41 | return ([animatedImagePath, framesNamePrefix], nil) 42 | } 43 | 44 | // MARK: CommandExecutableProtocol 45 | 46 | func commandExecutable() -> CommandExecutable { 47 | return .disassembly 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/DisassemblyProcess.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisassemblyProcess.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/20/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class DisassemblyProcess: ExecutableProcess { 12 | 13 | private let workingAnimatedImageName = "workingAnimatedImage.png" 14 | 15 | override init(withCommand command: Command, andAdditionalData additionalData: Any? = nil) { 16 | DirectoryManager.shared.createWorkingDirectory(forCommandExecutable: .disassembly) 17 | 18 | if let animatedImagePath = command.arguments?[0] { 19 | let animatedImageUrl = URL(fileURLWithPath: animatedImagePath) 20 | let animatedImageNewUrl = DirectoryManager.shared.createUrlForFile(withName: workingAnimatedImageName, 21 | forCommandExecutable: .disassembly) 22 | 23 | if let animatedImageNewUrl = animatedImageNewUrl { 24 | // Update argument value which holds path to animated image. This is required because animated image 25 | // will be copied to a temporary folder, where frames will also reside. 26 | command.arguments?[0] = animatedImageNewUrl.path 27 | 28 | DirectoryManager.shared.copyFilesInWorkingDirectory(forCommandExecutable: .disassembly, 29 | atPaths: [animatedImageUrl], 30 | toPath: [animatedImageNewUrl]) 31 | 32 | } else { 33 | debugPrint("\(#function): Animated image url from temporary directory is nil. Disassemblig will fail.") 34 | } 35 | 36 | } else { 37 | debugPrint("\(#function): Arguments array doesn't contain path to the animated image. Disassembling will fail.") 38 | } 39 | 40 | super.init(withCommand: command) 41 | } 42 | 43 | override func cleanup() { 44 | DirectoryManager.shared.cleanupWorkingDirectory(forCommandExecutable: .disassembly) 45 | } 46 | 47 | override func didFinishedWithSuccess(success: Bool, url: URL?) { 48 | 49 | if success { 50 | DirectoryManager.shared.moveFiles(forCommandExecutable: .disassembly, 51 | toPath: url!, 52 | withNames: [self.workingAnimatedImageName], 53 | toIgnore: false) 54 | } 55 | 56 | self.cleanup() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/ExecutableProcess.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExecutableProcess.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/9/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | struct ExecutableProcessFactory { 12 | 13 | static func createProcess(identifiedBy executable: CommandExecutable, 14 | and command: Command, 15 | withData additionalData: Any?) -> ExecutableProcess? { 16 | switch executable { 17 | case .assembly: 18 | return AssemblyProcess(withCommand: command, andAdditionalData: additionalData) 19 | case .disassembly: 20 | return DisassemblyProcess(withCommand: command) 21 | default: 22 | return nil 23 | } 24 | } 25 | } 26 | 27 | class ExecutableProcess: NSObject { 28 | 29 | var initialHandler: VoidHandler 30 | var progressHandler: ((String) -> ())? 31 | var terminationHandler: VoidHandler 32 | var cancelled = false 33 | 34 | private var fileHandle: FileHandle? 35 | private var task = Process() 36 | 37 | init(withCommand command: Command, andAdditionalData additionalData: Any? = nil) { 38 | super.init() 39 | 40 | task.launchPath = Bundle.main.path(forResource: command.name, ofType: nil) 41 | task.arguments = command.arguments 42 | task.terminationHandler = { process in 43 | 44 | if let handler = self.terminationHandler { 45 | DispatchQueue.main.async(execute: { 46 | handler() 47 | }) 48 | } 49 | } 50 | 51 | let outputPipe = Pipe() 52 | task.standardOutput = outputPipe 53 | fileHandle = outputPipe.fileHandleForReading 54 | fileHandle?.waitForDataInBackgroundAndNotify() 55 | NotificationCenter.default.addObserver(self, 56 | selector: #selector(receivedData(notification:)), 57 | name: NSNotification.Name.NSFileHandleDataAvailable, 58 | object: nil) 59 | } 60 | 61 | func start() { 62 | 63 | if let handler = self.initialHandler { 64 | handler() 65 | } 66 | 67 | task.launch() 68 | } 69 | 70 | func stop() { 71 | task.terminate() 72 | } 73 | 74 | func cleanup() { 75 | assertionFailure("\(#function) must be implemented in subclass") 76 | } 77 | 78 | func didFinishedWithSuccess(success: Bool, url: URL?) { 79 | assertionFailure("\(#function) must be implemented in subclass") 80 | } 81 | 82 | // MARK: - Private 83 | 84 | @objc private func receivedData(notification : NSNotification) { 85 | 86 | if let fileHandle = fileHandle { 87 | let data = fileHandle.availableData 88 | 89 | if data.count > 0 { 90 | fileHandle.waitForDataInBackgroundAndNotify() 91 | let outputString = String(data: data, 92 | encoding: String.Encoding.ascii) 93 | 94 | if let outputString = outputString { 95 | self.progressHandler?(outputString) 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/FrameDelay.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FrameDelay.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/14/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | enum FrameDelayCategory { 12 | case All, Selected 13 | } 14 | 15 | enum NotificationIdentifier: String { 16 | case didChangeAllFramesDelay = "didChangeAllFramesDelay" 17 | case didChangeSelectedFramesDelay = "didChangeSelectedFramesDelay" 18 | } 19 | 20 | final class FrameDelay: NSObject, CommandArgumentable { 21 | 22 | var category: FrameDelayCategory 23 | var enabled: Bool 24 | var seconds = 1 { 25 | 26 | didSet { 27 | self.notifyObservers() 28 | } 29 | } 30 | var frames = 10 { 31 | 32 | didSet { 33 | self.notifyObservers() 34 | } 35 | } 36 | 37 | init(withCategory category: FrameDelayCategory = .All, andState state: Bool = true) { 38 | self.category = category 39 | 40 | if category == .Selected { 41 | self.enabled = false 42 | } else { 43 | self.enabled = state 44 | } 45 | } 46 | 47 | override func setNilValueForKey(_ key: String) { 48 | 49 | if key == #keyPath(FrameDelay.seconds) { 50 | seconds = 1 51 | } 52 | 53 | if key == #keyPath(FrameDelay.frames) { 54 | frames = 10 55 | } 56 | } 57 | 58 | // MARK: - CommandArgumentable 59 | 60 | func havePassedValidation() -> Bool { 61 | return true 62 | } 63 | 64 | func commandArguments() -> ([String], Any?) { 65 | var arguments: [String] = [] 66 | 67 | if enabled { 68 | arguments.append("\(seconds) \(frames)") 69 | } 70 | 71 | return (arguments, nil) 72 | } 73 | 74 | // MARK: - Private 75 | 76 | private func notifyObservers() { 77 | 78 | if self.category == .Selected { 79 | NotificationCenter.default.post(name: NSNotification.Name(NotificationIdentifier.didChangeSelectedFramesDelay.rawValue), 80 | object: nil) 81 | } else { 82 | NotificationCenter.default.post(name: NSNotification.Name(NotificationIdentifier.didChangeAllFramesDelay.rawValue), 83 | object: nil) 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/Optimization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Optimization.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/14/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class Optimization: NSObject, CommandArgumentable { 12 | 13 | var enablePalette = true 14 | var enableColorType = true 15 | 16 | // MARK: - CommandArgumentable 17 | 18 | func havePassedValidation() -> Bool { 19 | return true 20 | } 21 | 22 | func commandArguments() -> ([String], Any?) { 23 | var arguments: [String] = [] 24 | 25 | if enablePalette == true { 26 | arguments.append(Argument.enablePalette) 27 | } 28 | 29 | if enableColorType == true { 30 | arguments.append(Argument.enableColorType) 31 | } 32 | 33 | return (arguments, nil) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/Playback.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Playback.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/14/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class Playback: NSObject, CommandArgumentable { 12 | 13 | var numberOfLoops = 0 14 | var skipFirstFrame = false 15 | 16 | override func setNilValueForKey(_ key: String) { 17 | 18 | if key == #keyPath(Playback.numberOfLoops) { 19 | numberOfLoops = 0 20 | } 21 | } 22 | 23 | // MARK: - CommandArgumentable 24 | 25 | func havePassedValidation() -> Bool { 26 | return true 27 | } 28 | 29 | func commandArguments() -> ([String], Any?) { 30 | var arguments: [String] = [] 31 | 32 | if numberOfLoops > 0 { 33 | arguments.append(Argument.numberOfLoops + "\(numberOfLoops)") 34 | } 35 | 36 | if skipFirstFrame == true { 37 | arguments.append(Argument.skipFirstFrame) 38 | } 39 | 40 | return (arguments, nil) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /APNGb/APNGb/Library/Strip.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Strip.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/24/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class Strip: NSObject, CommandArgumentable { 12 | 13 | var orientation = StripOrientation.none.rawValue 14 | var numberOfFrames = 0 15 | 16 | override func setNilValueForKey(_ key: String) { 17 | 18 | if key == #keyPath(Strip.numberOfFrames) { 19 | numberOfFrames = 0 20 | } 21 | } 22 | 23 | // MARK: - CommandArgumentable 24 | 25 | func havePassedValidation() -> Bool { 26 | 27 | if numberOfFrames == 0 { 28 | return false 29 | } 30 | 31 | if orientation == StripOrientation.none.rawValue { 32 | return false 33 | } 34 | 35 | return true 36 | } 37 | 38 | func commandArguments() -> ([String], Any?) { 39 | var arguments = [String]() 40 | 41 | if numberOfFrames > 0 { 42 | arguments.append(StripOrientation.argumentValue(for: orientation) + "\(numberOfFrames)") 43 | } 44 | 45 | return (arguments, nil) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /APNGb/APNGb/Models/AnimationFrame.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimationFrame.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 10/18/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class AnimationFrame { 12 | 13 | var delaySeconds = 1 14 | var delayFrames = 10 15 | var displayableFrameDelay: String { 16 | get { 17 | return "\(delaySeconds)/\(delayFrames)" 18 | } 19 | } 20 | 21 | private(set) var path: String 22 | private(set) var size: Int 23 | private(set) var name: String 24 | 25 | init(url: NSURL, size: Int) { 26 | self.size = size 27 | 28 | if let lastPathComponent = url.lastPathComponent { 29 | self.name = lastPathComponent 30 | } else { 31 | self.name = String.empty 32 | } 33 | 34 | if let urlAsPath = url.path { 35 | self.path = urlAsPath 36 | } else { 37 | self.path = String.empty 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /APNGb/APNGb/Resources/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf470 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;\red0\green0\blue0;\red67\green67\blue67;} 4 | \margl1440\margr1440\vieww9000\viewh8400\viewkind0 5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc\partightenfactor0 6 | 7 | \f0\b\fs24 \cf0 Credits to Max Stepin 8 | \b0 \cf2 \ 9 | 10 | \fs22 APNG Assembler 2.91\ 11 | \pard\pardeftab720\qc\partightenfactor0 12 | \cf2 \expnd0\expndtw0\kerning0 13 | APNG Disassembler 2.8} -------------------------------------------------------------------------------- /APNGb/APNGb/Utilities/Closure.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Closure.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/7/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | typealias VoidHandler = (()->())? 12 | -------------------------------------------------------------------------------- /APNGb/APNGb/Utilities/DirectoryManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DirectoryManager.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/21/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class DirectoryManager { 12 | 13 | static let shared = DirectoryManager() 14 | 15 | private init() {} 16 | 17 | func cleanupWorkingDirectory(forCommandExecutable executable: CommandExecutable) { 18 | let directoryUrl = self.workingDirectoryUrl(forCommandExecutable: executable) 19 | 20 | if let directoryUrl = directoryUrl { 21 | _ = FileManager.default.removeItemIfExists(atPath: directoryUrl.path) 22 | } 23 | } 24 | 25 | func moveFiles(forCommandExecutable executable: CommandExecutable, 26 | toPath destinationUrl: URL, 27 | withNames fileNames: [String], 28 | toIgnore ignore: Bool) { 29 | 30 | let directoryUrl = self.workingDirectoryUrl(forCommandExecutable: executable) 31 | 32 | do { 33 | let files = try FileManager.default.contentsOfDirectory(at: directoryUrl!, 34 | includingPropertiesForKeys: nil, 35 | options: .skipsHiddenFiles) 36 | for fileUrl in files { 37 | let fileName = fileUrl.lastPathComponent 38 | let fileExtension = fileUrl.pathExtension 39 | 40 | if fileNames.contains(fileName) == ignore && fileExtension != FileExtension.txt { 41 | let updatedDestinationUrl = destinationUrl.appendingPathComponent(fileName) 42 | try _ = FileManager.default.replaceItemAt(updatedDestinationUrl, 43 | withItemAt: fileUrl) 44 | } 45 | } 46 | 47 | } catch let error { 48 | NSLog("\(#function): \(error)") 49 | } 50 | } 51 | 52 | func copyFilesInWorkingDirectory(forCommandExecutable executable: CommandExecutable, atPaths sourceUrls: [URL], toPath destinationUrls: [URL]) { 53 | let bound = sourceUrls.count 54 | 55 | for index in stride(from: 0, to: bound, by: 1) { 56 | copyFile(atPath: sourceUrls[index], 57 | toPath: destinationUrls[index]) 58 | } 59 | } 60 | 61 | func createUrlForFile(withName name: String, forCommandExecutable executable: CommandExecutable) -> URL? { 62 | let workingDirectoryUrl = self.workingDirectoryUrl(forCommandExecutable: executable) 63 | return workingDirectoryUrl?.appendingPathComponent(name) 64 | } 65 | 66 | func workingDirectoryUrl(forCommandExecutable executable: CommandExecutable) -> URL? { 67 | let temporaryDirectoryUrl = NSURL(fileURLWithPath: NSTemporaryDirectory()) 68 | let mainDirectoryUrl = temporaryDirectoryUrl.appendingPathComponent(Resource.Directory.main) 69 | var destinationDirectoryUrl: URL? = nil 70 | 71 | switch executable { 72 | case .assembly: 73 | destinationDirectoryUrl = mainDirectoryUrl?.appendingPathComponent(Resource.Directory.assembly) 74 | case .disassembly: 75 | destinationDirectoryUrl = mainDirectoryUrl?.appendingPathComponent(Resource.Directory.disassembly) 76 | default: 77 | destinationDirectoryUrl = nil 78 | } 79 | 80 | return destinationDirectoryUrl 81 | } 82 | 83 | func createWorkingDirectory(forCommandExecutable executable: CommandExecutable) { 84 | let directoryUrl = self.workingDirectoryUrl(forCommandExecutable: executable) 85 | 86 | if let directoryUrl = directoryUrl { 87 | let directoryWasRemoved = FileManager.default.removeItemIfExists(atPath: directoryUrl.path) 88 | 89 | if directoryWasRemoved { 90 | do { 91 | try FileManager.default.createDirectory(at: directoryUrl, 92 | withIntermediateDirectories: true, 93 | attributes: nil) 94 | #if DEBUG 95 | NSWorkspace.shared().open(directoryUrl) 96 | #endif 97 | } catch let error { 98 | NSLog("\(#function): \(error)") 99 | } 100 | } 101 | } 102 | } 103 | 104 | // MARK: Private 105 | 106 | private func copyFile(atPath sourcePath: URL, toPath destinationPath: URL) { 107 | 108 | do { 109 | try FileManager.default.copyItem(atPath: sourcePath.path, 110 | toPath: destinationPath.path) 111 | 112 | } catch let error { 113 | NSLog("\(#function): \(error)") 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /APNGb/APNGb/Utilities/Resource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Resource.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/20/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | struct Resource { 12 | 13 | struct String { 14 | static let dropAnimatedImageHere = "Drop animated image here" 15 | static let dropFramesHere = "Drop frames here" 16 | static let selectFolderToSaveFrames = "Select directory where animated frames will be saved" 17 | static let selectFolderToSaveAnimatedImage = "Select directory where animated image will be saved" 18 | static let defaultToolbarLoggingMessage = "Idle..." 19 | static let size = "Size" 20 | static let delay = "Delay" 21 | } 22 | 23 | struct Directory { 24 | static let main = "APNGb" 25 | static let assembly = "Assembly" 26 | static let disassembly = "Disassembly" 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /APNGb/APNGb/Utilities/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/17/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | struct Theme { 12 | 13 | private static let workingPaneColor = NSColor(colorLiteralRed: 246 / 255, 14 | green: 246 / 255, 15 | blue: 246 / 255, 16 | alpha: 1.0) 17 | private static let selectedCellBorderColor = NSColor(colorLiteralRed: 22 / 255, 18 | green: 127 / 255, 19 | blue: 249 / 255, 20 | alpha: 0.3) 21 | private static let selectedCellBackgroundColor = selectedCellBorderColor 22 | 23 | struct Color { 24 | 25 | static let sidebarBackground = Theme.workingPaneColor 26 | static let preferencesPane = Theme.workingPaneColor 27 | static let assemblyFrameCellBorderColor = Theme.selectedCellBorderColor 28 | static let assemblyFrameCellBackgroundColor = Theme.selectedCellBackgroundColor 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /APNGb/APNGb/Utilities/ViewControllerId.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewControllerId.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/10/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | /// Maps identifiers for all view controllers from the application. 10 | /// 11 | /// - Assembly: AssemblyViewController identifier 12 | /// - Disassembly: DisassemblyViewController identifier 13 | /// - Preferences: PreferencesContainerViewController identifier 14 | /// - DropHint: DropHintViewController identifier 15 | /// - ChildContainer: ChildContainerViewController identifier 16 | /// - SideBar: SideBarViewController identifier 17 | /// - AssemblyPreferences: AssemblyPreferencesViewController identifier 18 | /// - DisassemblyPreferences: DisassemblyPreferencesViewController identifier 19 | /// - Unknown: Default value if view controller doesn't have an identifier. 20 | enum ViewControllerId: Int { 21 | case Assembly 22 | case Disassembly 23 | case Preferences 24 | case DropHint 25 | case ChildContainer 26 | case SideBar 27 | case AssemblyPreferences 28 | case DisassemblyPreferences 29 | case Unknown = 999 30 | } 31 | 32 | extension ViewControllerId { 33 | 34 | /// Custom initalizer. 35 | /// 36 | /// - Parameter rawValue: Int value used to identify a view controller. 37 | init(fromRawValue rawValue: Int) { 38 | self = ViewControllerId(rawValue: rawValue) ?? .Unknown 39 | } 40 | 41 | /// Identifies view controller storyboard id. 42 | /// 43 | /// - Returns: Storyboard identifier if view controller 44 | /// has a valid identifier, else returns an empty string. 45 | func storyboardVersion() -> String { 46 | switch self { 47 | case .Assembly: 48 | return "assembly.view" 49 | case .Disassembly: 50 | return "disassembly.view" 51 | case .Preferences: 52 | return "preferences.view" 53 | case .DropHint: 54 | return "drophint.view" 55 | case .ChildContainer: 56 | return "childcontainer.view" 57 | case .AssemblyPreferences: 58 | return "assemblypreferences.view" 59 | case .DisassemblyPreferences: 60 | return "disassemblypreferences.view" 61 | default: 62 | return String.empty 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /APNGb/APNGb/Utilities/ViewSize.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewSize.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/10/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | /// Keeps dimension values of all views from the application. 12 | struct ViewSize { 13 | 14 | static let windowHeight: CGFloat = 475.0 15 | static let windowWidth: CGFloat = 695.0 16 | static let splitViewSeparatorWidth: CGFloat = 1.0 17 | } 18 | 19 | extension ViewSize { 20 | 21 | /// Store side bar view dimension properties. 22 | struct SideBar { 23 | 24 | static let width: CGFloat = 50.0 25 | } 26 | } 27 | 28 | extension ViewSize { 29 | 30 | /// Store child container view dimension properties. 31 | struct ChildContainer { 32 | 33 | static let width: CGFloat = 425.0 34 | } 35 | } 36 | 37 | extension ViewSize { 38 | 39 | /// Store preferences view dimension properties. 40 | struct Preferences { 41 | 42 | static let width: CGFloat = 200.0 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/AssemblyPreferencesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssemblyPreferencesViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/9/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class AssemblyPreferencesViewController: NSViewController { 12 | 13 | dynamic var assemblyArguments: AssemblyArguments? 14 | dynamic var stripPopUpDataSource: StripPopUpDataSource 15 | 16 | required init?(coder: NSCoder) { 17 | stripPopUpDataSource = StripPopUpDataSource() 18 | super.init(coder: coder) 19 | } 20 | 21 | override func viewDidLoad() { 22 | self.view.backgroundColor = Theme.Color.preferencesPane 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/AssemblyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AssemblyViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 9/18/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class AssemblyViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource, DragAndDropDelegate { 12 | 13 | var assemblyArguments: AssemblyArguments? { 14 | 15 | didSet { 16 | self.updateUI() 17 | } 18 | } 19 | 20 | private var dropHintViewController: DropHintViewController? 21 | private var viewLayoutCareTaker: ChildViewLayoutCareTaker 22 | 23 | private let animationFrameType = "AnimationFrame" 24 | 25 | @IBOutlet private var tableView: NSTableView! 26 | @IBOutlet private var tableViewContainer: NSScrollView! 27 | 28 | required init?(coder: NSCoder) { 29 | viewLayoutCareTaker = ChildViewLayoutCareTaker() 30 | super.init(coder: coder) 31 | } 32 | 33 | deinit { 34 | NotificationCenter.default.removeObserver(self) 35 | } 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | self.addDropHintViewController() 40 | self.configureTableView() 41 | self.setDragAndDropDelegate() 42 | 43 | NotificationCenter.default.addObserver(self, 44 | selector: #selector(updateAllFramesDelay(sender:)), 45 | name: NSNotification.Name(NotificationIdentifier.didChangeAllFramesDelay.rawValue), 46 | object: nil) 47 | NotificationCenter.default.addObserver(self, 48 | selector: #selector(updateSelectedFramesDelay(sender:)), 49 | name: NSNotification.Name(NotificationIdentifier.didChangeSelectedFramesDelay.rawValue), 50 | object: nil) 51 | } 52 | 53 | @objc private func updateAllFramesDelay(sender: Notification) { 54 | let allFramesDelay = assemblyArguments?.allFramesDelay 55 | let frames = assemblyArguments?.animationFrames 56 | 57 | if let allFramesDelay = allFramesDelay { 58 | 59 | if let frames = frames { 60 | 61 | for frame in frames { 62 | frame.delayFrames = allFramesDelay.frames 63 | frame.delaySeconds = allFramesDelay.seconds 64 | } 65 | } 66 | } 67 | 68 | tableView.reloadDataKeepingSelection() 69 | } 70 | 71 | @objc private func updateSelectedFramesDelay(sender: Notification) { 72 | let selectedFramesDelay = assemblyArguments?.selectedFramesDelay 73 | let frames = assemblyArguments?.animationFrames 74 | 75 | if let selectedFramesDelay = selectedFramesDelay { 76 | 77 | if let frames = frames { 78 | 79 | for index in tableView.selectedRowIndexes { 80 | let frame = frames[index] 81 | frame.delayFrames = selectedFramesDelay.frames 82 | frame.delaySeconds = selectedFramesDelay.seconds 83 | } 84 | } 85 | } 86 | 87 | tableView.reloadDataKeepingSelection() 88 | } 89 | 90 | private func addDropHintViewController() { 91 | 92 | if dropHintViewController == nil { 93 | dropHintViewController = showChildViewController(withIdentifier: ViewControllerId.DropHint.storyboardVersion()) as! DropHintViewController? 94 | 95 | if let view = dropHintViewController?.view { 96 | 97 | if let superview = view.superview { 98 | viewLayoutCareTaker.updateLayoutOf(view, 99 | withIdentifier: ViewControllerId.DropHint, 100 | superview: superview, 101 | andSiblingView: nil) 102 | } 103 | } 104 | 105 | dropHintViewController?.hintMessage = Resource.String.dropFramesHere 106 | } 107 | } 108 | 109 | // MARK: - NSTableView 110 | 111 | func tableViewSelectionDidChange(_ notification: Notification) { 112 | self.updateSelectedFramesDelayFieldsAvailability() 113 | } 114 | 115 | // MARK: - NSTableViewDataSource 116 | 117 | func numberOfRows(in tableView: NSTableView) -> Int { 118 | 119 | if let assemblyArguments = assemblyArguments { 120 | return assemblyArguments.animationFrames.count 121 | } else { 122 | return 0 123 | } 124 | } 125 | 126 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 127 | let cellView = tableView.make(withIdentifier: AssemblyFrameCellView.identifier(), 128 | owner: self) as! AssemblyFrameCellView 129 | 130 | let imageFrame = assemblyArguments?.animationFrames[row] 131 | 132 | if let imageFrame = imageFrame { 133 | cellView.imageView!.image = NSImage(contentsOf: NSURL(fileURLWithPath: imageFrame.path) as URL) 134 | cellView.nameTextField.stringValue = imageFrame.name 135 | cellView.sizeTextField.stringValue = "\(Resource.String.size)\(String.colon) \(imageFrame.size) \(String.kilobyteAbbreviation)" 136 | cellView.delayTextField.stringValue = "\(Resource.String.delay)\(String.colon) \(imageFrame.displayableFrameDelay)" 137 | } 138 | 139 | return cellView 140 | } 141 | 142 | func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? { 143 | return AssemblyFrameRowView() 144 | } 145 | 146 | func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool { 147 | let data = NSKeyedArchiver.archivedData(withRootObject: rowIndexes) 148 | pboard.declareTypes([animationFrameType], owner: self) 149 | pboard.setData(data, forType: animationFrameType) 150 | 151 | return true 152 | } 153 | 154 | func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation { 155 | 156 | if dropOperation == .above { 157 | return .move 158 | } else { 159 | return [] 160 | } 161 | } 162 | 163 | func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool { 164 | let pasteboard = info.draggingPasteboard() 165 | let pasteboardData = pasteboard.data(forType: animationFrameType) 166 | 167 | if let pasteboardData = pasteboardData { 168 | 169 | if let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: pasteboardData) as? IndexSet { 170 | var oldIndex = 0 171 | var newIndex = 0 172 | 173 | for rowIndex in rowIndexes { 174 | 175 | if rowIndex < row { 176 | self.replaceAnimationFrame(atIndex: (rowIndex + oldIndex), 177 | toIndex: (row - 1)) 178 | tableView.moveRow(at: rowIndex + oldIndex, to: row - 1) 179 | oldIndex -= 1 180 | } else { 181 | self.replaceAnimationFrame(atIndex: rowIndex, 182 | toIndex: (row + newIndex)) 183 | tableView.moveRow(at: rowIndex, to: row + newIndex) 184 | newIndex += 1 185 | } 186 | } 187 | } 188 | } 189 | 190 | return true 191 | } 192 | 193 | // MARK: - NSTableViewDelegate 194 | 195 | func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { 196 | return AssemblyFrameCellView.height() 197 | } 198 | 199 | // MARK: - DragAndDropImageDelegate 200 | 201 | func didDropFiles(withPaths paths: [String]) { 202 | 203 | for imagePath in paths { 204 | let imageSizeInKB = FileManager.default.sizeOfFile(atPath: imagePath) 205 | let droppedImage = AnimationFrame(url: URL(fileURLWithPath: imagePath) as NSURL, 206 | size: imageSizeInKB) 207 | assemblyArguments?.animationFrames.append(droppedImage) 208 | } 209 | 210 | self.updateUI() 211 | } 212 | 213 | // MARK: - Delete event 214 | 215 | func delete(_ sender: NSMenuItem) { 216 | let selectedRowIndexes = tableView.selectedRowIndexes.reversed() 217 | for index in selectedRowIndexes { 218 | assemblyArguments?.animationFrames.remove(at: index) 219 | } 220 | 221 | self.updateUI() 222 | } 223 | 224 | // MARK: - Private 225 | 226 | private func updateUI() { 227 | showTableViewIfNeeded() 228 | tableView.reloadData() 229 | self.updateSelectedFramesDelayFieldsAvailability() 230 | } 231 | 232 | private func showTableViewIfNeeded() { 233 | 234 | if let count = assemblyArguments?.animationFrames.count { 235 | 236 | if count > 0 { 237 | tableViewContainer.isHidden = false 238 | } else { 239 | tableViewContainer.isHidden = true 240 | } 241 | 242 | } else { 243 | tableViewContainer.isHidden = true 244 | } 245 | } 246 | 247 | private func configureTableView() { 248 | tableView.register(forDraggedTypes: [animationFrameType]) 249 | tableViewContainer.isHidden = true 250 | } 251 | 252 | private func setDragAndDropDelegate() { 253 | if let dragAndDropView = self.view as? DragAndDropView { 254 | dragAndDropView.delegate = self 255 | } else { 256 | debugPrint("\(#function): View Controller's view is not a DragAndDropView subclass. Drag & Drop will fail.") 257 | } 258 | } 259 | 260 | private func replaceAnimationFrame(atIndex soureIndex: Int, toIndex destinationIndex: Int) { 261 | let frame = assemblyArguments?.animationFrames[soureIndex] 262 | assemblyArguments?.animationFrames.remove(at: soureIndex) 263 | assemblyArguments?.animationFrames.insert(frame!, at: destinationIndex) 264 | } 265 | 266 | private func updateSelectedFramesDelayFieldsAvailability() { 267 | 268 | if tableView.selectedRowIndexes.count > 0 { 269 | assemblyArguments?.selectedFramesDelay.setValue(true, forKey: #keyPath(FrameDelay.enabled)) 270 | } else { 271 | assemblyArguments?.selectedFramesDelay.setValue(false, forKey: #keyPath(FrameDelay.enabled)) 272 | } 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/ChildContainerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChildContainerViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/7/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ChildContainerViewController: NSViewController { 12 | 13 | var assemblyArguments: AssemblyArguments? 14 | var disassemblyArguments: DisassemblyArguments? 15 | 16 | private var viewLayoutCareTaker: ChildContainerViewLayoutCareTaker 17 | 18 | required init?(coder: NSCoder) { 19 | viewLayoutCareTaker = ChildContainerViewLayoutCareTaker() 20 | super.init(coder: coder) 21 | } 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | } 26 | 27 | func addChildViewController(withIndentifier identifier: ViewControllerId) { 28 | self.removeChildViewControllers() 29 | let childViewController = self.showChildViewController(withIdentifier: identifier.storyboardVersion()) 30 | 31 | if let viewController = childViewController as? AssemblyViewController { 32 | viewController.assemblyArguments = assemblyArguments 33 | } else if let viewController = childViewController as? DisassemblyViewController { 34 | viewController.disassemblyArguments = disassemblyArguments 35 | } 36 | 37 | if let view = childViewController?.view { 38 | 39 | if let superview = view.superview { 40 | viewLayoutCareTaker.updateLayoutOf(view, 41 | withIdentifier: identifier, 42 | superview: superview, 43 | andSiblingView: nil) 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/DisassemblyPreferencesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisassemblyPreferencesViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/9/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class DisassemblyPreferencesViewController: NSViewController { 12 | 13 | dynamic var disassemblyArguments: DisassemblyArguments? 14 | 15 | override func viewDidLoad() { 16 | self.view.backgroundColor = Theme.Color.preferencesPane 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/DisassemblyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisassemblyViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 9/18/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import WebKit 11 | 12 | final class DisassemblyViewController: NSViewController, DragAndDropDelegate { 13 | 14 | var disassemblyArguments: DisassemblyArguments? { 15 | 16 | didSet { 17 | self.updateUI() 18 | } 19 | } 20 | 21 | private var dropHintViewController: DropHintViewController? 22 | private var viewLayoutCareTaker: ChildViewLayoutCareTaker 23 | 24 | @IBOutlet private var destinationWebView: DragAndDropWebView! 25 | 26 | required init?(coder: NSCoder) { 27 | viewLayoutCareTaker = ChildViewLayoutCareTaker() 28 | super.init(coder: coder) 29 | } 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | self.addDropHintViewController() 34 | self.configureWebView() 35 | } 36 | 37 | // MARK: - DragAndDropImageViewDelegate 38 | 39 | func didDropFiles(withPaths paths: [String]) { 40 | disassemblyArguments?.animatedImagePath = paths[0] 41 | self.updateUI() 42 | } 43 | 44 | // MARK: - Private 45 | 46 | private func configureWebView() { 47 | destinationWebView.delegate = self 48 | destinationWebView.drawsBackground = false 49 | destinationWebView.mainFrame.frameView.allowsScrolling = false 50 | } 51 | 52 | private func updateUI() { 53 | 54 | if disassemblyArguments?.isAnimatedImagePathValid() == true { 55 | dropHintViewController?.view.isHidden = true 56 | destinationWebView.loadImage(path: disassemblyArguments!.animatedImagePath) 57 | } else { 58 | dropHintViewController?.view.isHidden = false 59 | } 60 | } 61 | 62 | // MARK: - Child view controllers presentation 63 | 64 | private func addDropHintViewController() { 65 | 66 | if dropHintViewController == nil { 67 | dropHintViewController = showChildViewController(withIdentifier: ViewControllerId.DropHint.storyboardVersion()) as! DropHintViewController? 68 | 69 | if let view = dropHintViewController?.view { 70 | 71 | if let superview = view.superview { 72 | viewLayoutCareTaker.updateLayoutOf(view, 73 | withIdentifier: ViewControllerId.DropHint, 74 | superview: superview, 75 | andSiblingView: nil) 76 | } 77 | } 78 | 79 | dropHintViewController?.hintMessage = Resource.String.dropAnimatedImageHere 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/DropHintViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropHintViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/8/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class DropHintViewController: NSViewController { 12 | 13 | @IBOutlet private var hintLabel: NSTextField! 14 | 15 | var hintMessage = String.empty { 16 | 17 | didSet { 18 | hintLabel.stringValue = hintMessage 19 | } 20 | } 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | self.disableDraggingForAllViews() 25 | } 26 | 27 | // MARK: Private 28 | 29 | private func disableDraggingForAllViews() { 30 | self.view.unregisterDraggedTypes() 31 | 32 | for view in self.view.subviews { 33 | view.unregisterDraggedTypes() 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/MainContainerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainContainerViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/6/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class MainContainerViewController: NSSplitViewController, Clickable, CommandArgumentable, CommandExecutableProtocol { 12 | 13 | private var assemblyArguments = AssemblyArguments() 14 | private var disassemblyArguments = DisassemblyArguments() 15 | private var viewLayoutCareTaker: MainContainerViewLayoutCareTaker 16 | private var sideBarViewController: SideBarViewController? 17 | private var childContainerViewController: ChildContainerViewController? 18 | private var preferencesContainerViewController: PreferencesContainerViewController? 19 | private var selectedViewControllerId: ViewControllerId = .Unknown 20 | 21 | required init?(coder: NSCoder) { 22 | viewLayoutCareTaker = MainContainerViewLayoutCareTaker() 23 | super.init(coder: coder) 24 | } 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | self.setupChildViewControllers() 29 | self.presentInitialChildViewControllers() 30 | } 31 | 32 | // MARK: ExecutableNameProtocol 33 | 34 | func commandExecutable() -> CommandExecutable { 35 | 36 | if selectedViewControllerId == .Assembly { 37 | return assemblyArguments.commandExecutable() 38 | } else if selectedViewControllerId == .Disassembly { 39 | return disassemblyArguments.commandExecutable() 40 | } else { 41 | return .none 42 | } 43 | } 44 | 45 | // MARK: CommandArgumentable 46 | 47 | func commandArguments() -> ([String], Any?) { 48 | 49 | if selectedViewControllerId == .Assembly { 50 | return assemblyArguments.commandArguments() 51 | } else if selectedViewControllerId == .Disassembly { 52 | return disassemblyArguments.commandArguments() 53 | } else { 54 | return ([], nil) 55 | } 56 | } 57 | 58 | func havePassedValidation() -> Bool { 59 | 60 | if selectedViewControllerId == .Assembly { 61 | return assemblyArguments.havePassedValidation() 62 | } else if selectedViewControllerId == .Disassembly { 63 | return disassemblyArguments.havePassedValidation() 64 | } else { 65 | return false 66 | } 67 | } 68 | 69 | // MARK: SideBarViewControllerDelegate 70 | 71 | func didClickOnItem(atIndex index: Int) { 72 | selectedViewControllerId = ViewControllerId(fromRawValue: index) 73 | childContainerViewController?.addChildViewController(withIndentifier: selectedViewControllerId) 74 | preferencesContainerViewController?.addChildViewController(withIndentifier: selectedViewControllerId) 75 | } 76 | 77 | // MARK: Private 78 | 79 | private func presentInitialChildViewControllers() { 80 | didClickOnItem(atIndex: 0) 81 | } 82 | 83 | private func setupChildViewControllers() { 84 | 85 | for childViewController in self.childViewControllers { 86 | 87 | // Find SideBar VC 88 | if childViewController is SideBarViewController { 89 | sideBarViewController = childViewController as? SideBarViewController 90 | 91 | if let view = sideBarViewController?.view { 92 | 93 | if let superview = view.superview { 94 | viewLayoutCareTaker.updateLayoutOf(view, 95 | withIdentifier: ViewControllerId.SideBar, 96 | superview: superview, 97 | andSiblingView: nil) 98 | } 99 | } 100 | 101 | sideBarViewController?.delegate = self 102 | } 103 | // Find Child Container VC 104 | else if childViewController is ChildContainerViewController { 105 | childContainerViewController = childViewController as? ChildContainerViewController 106 | childContainerViewController?.assemblyArguments = assemblyArguments 107 | childContainerViewController?.disassemblyArguments = disassemblyArguments 108 | 109 | if let view = childContainerViewController?.view { 110 | 111 | if let superview = view.superview { 112 | viewLayoutCareTaker.updateLayoutOf(view, 113 | withIdentifier: ViewControllerId.ChildContainer, 114 | superview: superview, 115 | andSiblingView: sideBarViewController?.view) 116 | } 117 | } 118 | 119 | } 120 | // Find Preferences VC 121 | else if childViewController is PreferencesContainerViewController { 122 | preferencesContainerViewController = childViewController as? PreferencesContainerViewController 123 | preferencesContainerViewController?.assemblyArguments = assemblyArguments 124 | preferencesContainerViewController?.disassemblyArguments = disassemblyArguments 125 | 126 | if let view = preferencesContainerViewController?.view { 127 | 128 | if let superview = view.superview { 129 | viewLayoutCareTaker.updateLayoutOf(view, 130 | withIdentifier: ViewControllerId.Preferences, 131 | superview: superview, 132 | andSiblingView: childContainerViewController?.view) 133 | } 134 | } 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/MainWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainWindowController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/16/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MainWindowController: NSWindowController, ActionToolbarDelegate { 12 | 13 | private var process: ExecutableProcess? 14 | 15 | @IBOutlet private var actionToolbar: ActionToolbar! 16 | 17 | override func windowDidLoad() { 18 | super.windowDidLoad() 19 | self.hideWindowTitle() 20 | self.setupActionBar() 21 | } 22 | 23 | // MARK: ActionToolbarDelegate 24 | 25 | func actionWillStart() -> Bool { 26 | 27 | if let content = self.contentViewController as? MainContainerViewController { 28 | let executable = content.commandExecutable() 29 | let arguments = content.commandArguments().0 30 | let additionalData = content.commandArguments().1 31 | 32 | if content.havePassedValidation() { 33 | let command = Command(withExecutable: executable) 34 | command.arguments = arguments 35 | 36 | process = ExecutableProcessFactory.createProcess(identifiedBy: executable, 37 | and: command, 38 | withData: additionalData) 39 | process?.progressHandler = { output in 40 | #if DEBUG 41 | debugPrint(output) 42 | #endif 43 | 44 | // TODO: weak approach, must be refactored 45 | let errorHasOccured = output.lowercased().contains("error") 46 | 47 | if errorHasOccured { 48 | self.process?.cancelled = true 49 | } 50 | 51 | self.actionToolbar.updateLogMessage(message: output) 52 | } 53 | process?.terminationHandler = { 54 | self.actionToolbar.taskDone() 55 | 56 | if self.process?.cancelled == false { 57 | 58 | let onOkButtonPressedHandler = { url in 59 | self.process?.didFinishedWithSuccess(success: true, 60 | url: url) 61 | } 62 | let onCancelButtonPressedHandler = { 63 | self.process?.didFinishedWithSuccess(success: false, 64 | url: nil) 65 | } 66 | 67 | let hintMessage = self.hintMessageForProcess(process: self.process) 68 | self.showSaveToDirectoryPanel(hintMessage: hintMessage, 69 | onOkButtonPressed: onOkButtonPressedHandler, 70 | onCancelButtonPressed: onCancelButtonPressedHandler) 71 | } else { 72 | self.process?.cancelled = false 73 | self.process?.cleanup() 74 | } 75 | } 76 | process?.start() 77 | 78 | return true 79 | } else { 80 | self.actionToolbar.taskDone() 81 | } 82 | } 83 | 84 | return false 85 | } 86 | 87 | func actionWillStop() -> Bool { 88 | process?.cancelled = true 89 | process?.stop() 90 | 91 | return true 92 | } 93 | 94 | // MARK: Private 95 | 96 | private func hideWindowTitle() { 97 | self.window?.titleVisibility = .hidden 98 | } 99 | 100 | private func setupActionBar() { 101 | actionToolbar.actionDelegate = self 102 | } 103 | 104 | private func showSaveToDirectoryPanel(hintMessage: String, 105 | onOkButtonPressed: @escaping (URL?) -> ()?, 106 | onCancelButtonPressed: @escaping () -> ()?) { 107 | let openPanel = NSOpenPanel() 108 | openPanel.message = hintMessage 109 | openPanel.canChooseFiles = false 110 | openPanel.canChooseDirectories = true 111 | openPanel.allowsMultipleSelection = false 112 | openPanel.beginSheetModal(for: self.window!, 113 | completionHandler: { response in 114 | 115 | if response == NSFileHandlingPanelOKButton { 116 | let destinationDirectoryUrl = openPanel.urls[0] 117 | onOkButtonPressed(destinationDirectoryUrl) 118 | } else { 119 | onCancelButtonPressed() 120 | } 121 | }) 122 | } 123 | 124 | private func hintMessageForProcess(process: ExecutableProcess?) -> String { 125 | 126 | if process is AssemblyProcess { 127 | return Resource.String.selectFolderToSaveAnimatedImage 128 | } else if process is DisassemblyProcess { 129 | return Resource.String.selectFolderToSaveFrames 130 | } else { 131 | return String.empty 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/PreferencesContainerViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreferencesContainerViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/7/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PreferencesContainerViewController: NSViewController { 12 | 13 | var assemblyArguments: AssemblyArguments? 14 | var disassemblyArguments: DisassemblyArguments? 15 | 16 | private var viewLayoutCareTaker: PreferencesViewLayoutCareTaker 17 | private var assemblyPreferencesViewController: AssemblyPreferencesViewController? 18 | private var disassemblyPreferencesViewController: DisassemblyPreferencesViewController? 19 | 20 | required init?(coder: NSCoder) { 21 | viewLayoutCareTaker = PreferencesViewLayoutCareTaker() 22 | super.init(coder: coder) 23 | } 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | } 28 | 29 | func addChildViewController(withIndentifier identifier: ViewControllerId) { 30 | self.removeChildViewControllers() 31 | 32 | switch identifier { 33 | case ViewControllerId.Assembly: 34 | assemblyPreferencesViewController = self.showChildViewController(withIdentifier: ViewControllerId.AssemblyPreferences.storyboardVersion()) as! AssemblyPreferencesViewController? 35 | assemblyPreferencesViewController?.assemblyArguments = assemblyArguments 36 | 37 | if let view = assemblyPreferencesViewController?.view { 38 | 39 | if let superview = view.superview { 40 | viewLayoutCareTaker.updateLayoutOf(view, 41 | withIdentifier: ViewControllerId.Preferences, 42 | superview: superview, 43 | andSiblingView: nil) 44 | } 45 | } 46 | 47 | case ViewControllerId.Disassembly: 48 | disassemblyPreferencesViewController = self.showChildViewController(withIdentifier: ViewControllerId.DisassemblyPreferences.storyboardVersion()) as! DisassemblyPreferencesViewController? 49 | disassemblyPreferencesViewController?.disassemblyArguments = disassemblyArguments 50 | 51 | if let view = disassemblyPreferencesViewController?.view { 52 | 53 | if let superview = view.superview { 54 | 55 | viewLayoutCareTaker.updateLayoutOf(view, 56 | withIdentifier: ViewControllerId.Preferences, 57 | superview: superview, 58 | andSiblingView: nil) 59 | } 60 | } 61 | 62 | default: 63 | NSLog("\(#function): Unexpected case") 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/SideBarItemGroup.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SideBarItemGroup.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/7/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | protocol Clickable { 12 | func didClickOnItem(atIndex index: Int) 13 | } 14 | 15 | class SideBarItemGroup: NSObject { 16 | 17 | var delegate: Clickable? 18 | 19 | private var items: [NSButton] = [] 20 | 21 | func addItem(item: NSButton) { 22 | items.append(item) 23 | } 24 | 25 | func setup() { 26 | 27 | for item in items { 28 | item.target = self 29 | item.action = #selector(didClickOn(item:)) 30 | } 31 | } 32 | 33 | func setDefaultSelectedItem(atIndex index: Int) { 34 | 35 | if index < items.count { 36 | let item = items[index] 37 | item.state = NSOnState 38 | } 39 | } 40 | 41 | // MARK: Private 42 | 43 | @objc 44 | private func didClickOn(item: NSButton) { 45 | let selectedItemIndex = items.index(of: item) 46 | self.delegate?.didClickOnItem(atIndex: selectedItemIndex!) 47 | 48 | for item in items { 49 | item.state = NSOffState 50 | } 51 | 52 | item.state = NSOnState 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /APNGb/APNGb/ViewControllers/SideBarViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SideBarViewController.swift 3 | // APNGb 4 | // 5 | // Created by Stefan Godoroja on 12/6/16. 6 | // Copyright © 2016 Godoroja Stefan. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class SideBarViewController: NSViewController, Clickable { 12 | 13 | var delegate: Clickable? 14 | 15 | private var sideBarItemGroup: SideBarItemGroup? 16 | 17 | @IBOutlet private var assemblyItemButton: NSButton! 18 | @IBOutlet private var disassemblyItemButton: NSButton! 19 | 20 | // MARK: - ViewController life-cycle 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | self.applyStyle() 25 | self.setupSideBar() 26 | } 27 | 28 | // MARK: - Clickable 29 | 30 | func didClickOnItem(atIndex index: Int) { 31 | delegate?.didClickOnItem(atIndex: index) 32 | } 33 | 34 | // MARK: - Private 35 | 36 | private func applyStyle() { 37 | self.view.backgroundColor = Theme.Color.sidebarBackground 38 | } 39 | 40 | private func setupSideBar() { 41 | sideBarItemGroup = SideBarItemGroup() 42 | sideBarItemGroup?.delegate = self 43 | sideBarItemGroup?.addItem(item: assemblyItemButton) 44 | sideBarItemGroup?.addItem(item: disassemblyItemButton) 45 | sideBarItemGroup?.setDefaultSelectedItem(atIndex: 0) 46 | sideBarItemGroup?.setup() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /APNGb/APNGbTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Additional-Resources/apng-examples/DancingPeaks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/Additional-Resources/apng-examples/DancingPeaks.gif -------------------------------------------------------------------------------- /Additional-Resources/apng-examples/bouncing_beach_ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/Additional-Resources/apng-examples/bouncing_beach_ball.png -------------------------------------------------------------------------------- /Additional-Resources/apng-examples/elephant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/Additional-Resources/apng-examples/elephant.png -------------------------------------------------------------------------------- /Additional-Resources/apng-examples/firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/Additional-Resources/apng-examples/firefox.png -------------------------------------------------------------------------------- /Additional-Resources/apng-examples/mechanism.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/Additional-Resources/apng-examples/mechanism.png -------------------------------------------------------------------------------- /Additional-Resources/design/icon/app-icon.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/Additional-Resources/design/icon/app-icon.sketch -------------------------------------------------------------------------------- /Additional-Resources/design/icon/app-logo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shgodoroja/APNGb/c795c105bf2a3fa090232592d6d6f6aa5884d21a/Additional-Resources/design/icon/app-logo.sketch -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ======================================================== 2 | 3 | APNG Assembler 2.91 4 | Creates APNG animation from PNG/TGA image sequence. 5 | http://apngasm.sourceforge.net/ 6 | 7 | Copyright (c) 2010-2016 Max Stepin 8 | maxst@users.sourceforge.net 9 | 10 | License: zlib license 11 | 12 | 13 | APNG Disassembler 2.8 14 | Deconstructs APNG files into individual frames. 15 | http://apngdis.sourceforge.net/ 16 | 17 | Copyright (c) 2010-2015 Max Stepin 18 | maxst@users.sourceforge.net 19 | 20 | License: zlib license 21 | 22 | =========================================================== 23 | 24 | MIT License 25 | 26 | Copyright (c) 2016 Ștefan Godoroja 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining a copy 29 | of this software and associated documentation files (the "Software"), to deal 30 | in the Software without restriction, including without limitation the rights 31 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | copies of the Software, and to permit persons to whom the Software is 33 | furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in all 36 | copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 43 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 44 | SOFTWARE. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![header-logo](https://cloud.githubusercontent.com/assets/2619031/21511755/0dcd98dc-cca5-11e6-86bf-d5b07a477523.png) 2 |

Over 11k downloads* :tada:

3 | 4 | APNGb 2.0.1 ([Download](https://github.com/shgodoroja/APNGb/releases/download/v2.0.1/APNGb-v2.0.1.app.zip)) 5 | ===== 6 | APNGb is a macOS app which creates animated png from a series of png frames and disassembles animated png into a series of png frames. Assembling has optimization and compression capabilities, option to change frame delay for all or selected frames, playback options. See `Assembling feature` and `Disassembling feature` sections for more details. App supports macOS 10.11 and above. 7 | 8 | **It's built on the top of 2 executables created by Max Stepin: [APNG Assembler](http://apngasm.sourceforge.net) and [APNG Disassembler](http://apngdis.sourceforge.net). Big credits to Max!** 9 | 10 | What is apng ? 11 | ------ 12 | > The Animated Portable Network Graphics (APNG) file format is an extension to the Portable Network Graphics (PNG) specification. It allows for animated PNG files that work similarly to animated GIF files, while supporting 24-bit images and 8-bit transparency not available for GIFs. It also retains backward compatibility with non-animated PNG files. 13 | 14 | Why apng is better than gif ? 15 | ------ 16 | Both GIF and APNG are lossless, but APNG tend to be smaller in size and provides better image quality (color, transparency). APNG is supported by web-browsers like Safari (both macOS and iOS), Firefox (desktop and Android), Chrome (add-ons), Opera (v12 and earlier). 17 | 18 | Take a look at below examples: 19 | 20 | ![world-cup-2014-42](https://cloud.githubusercontent.com/assets/2619031/21534194/c3e98950-cd63-11e6-84ed-043c16400368.png) 21 | 22 | APNG = 30 823 bytes 23 | 24 | ![world-cup-2014-42](https://cloud.githubusercontent.com/assets/2619031/21534196/c4b08316-cd63-11e6-8ae1-82aaf2a5cc95.gif) 25 | 26 | GIF = 43 132 bytes 27 | 28 | Assembling feature 29 | ------ 30 | Creates an animated apng/png from a series of png images. 31 | 32 | > A number of optimization techniques used to make APNG files as small as possible: inter-frame optimization utilizing alpha-blend and dispose operations, smaller than the full-size subframes, dirty transparency, color type and palette optimizations, and various compression options: zlib, 7zip, Zopfli. 33 | 34 | Disassembling feature 35 | ------ 36 | Breaks an apng/png file into a series of png images. 37 | 38 | > Decoding is implemented by parsing all chunks in the APNG file, remuxing them into a sequence of static PNG images, as shown in the diagram below, and then using regular (unpatched) libpng to decode them. 39 | Then, after processing blend/dispose operations, we finally get a vector of full-size frames in 32 bpp as the result. 40 | 41 | Few app screenshots 42 | ----- 43 | ![screen shot 2016-12-28 at 02 09 47](https://cloud.githubusercontent.com/assets/2619031/21534538/0464a1a4-cd69-11e6-8422-595f304cbefb.png) 44 | ![screen shot 2016-12-28 at 02 10 03](https://cloud.githubusercontent.com/assets/2619031/21534540/0465b4b8-cd69-11e6-8c7d-d99f140a50d3.png) 45 | ![screen shot 2016-12-28 at 02 10 23](https://cloud.githubusercontent.com/assets/2619031/21534541/046616c4-cd69-11e6-8c61-d604cfe38e91.png) 46 | ![screen shot 2016-12-28 at 02 11 48](https://cloud.githubusercontent.com/assets/2619031/21534543/048288f4-cd69-11e6-9d65-0f5877309f78.png) 47 | ![screen shot 2016-12-28 at 02 11 56](https://cloud.githubusercontent.com/assets/2619031/21534542/0480acdc-cd69-11e6-97b0-aa7b25121990.png) 48 | 49 | License 50 | ------ 51 | * MIT License, Copyright (c) 2019 Stefan Godoroja. 52 | * APNG Assembler and APNG Disassembler are released under zlib/libpng license. 53 | [More details](https://github.com/mancunianetz/APNGb/blob/master/LICENSE) 54 | 55 | Notes 56 | ------ 57 | \* Based on Sourceforge and Github Stats. 58 | -------------------------------------------------------------------------------- /credits.html: -------------------------------------------------------------------------------- 1 |
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
2 | 3 |
Icons made by Dave Gandy from www.flaticon.com is licensed by CC 3.0 BY
4 | 5 |
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
6 | 7 |
Icons made by Icon Works from www.flaticon.com is licensed by CC 3.0 BY
8 | 9 |
Icons made by Alfredo Hernandez from www.flaticon.com is licensed by CC 3.0 BY
10 | 11 |
Icons made by Google from www.flaticon.com is licensed by CC 3.0 BY
--------------------------------------------------------------------------------