diff --git a/.github/workflows/runnable.yml b/.github/workflows/runnable.yml index 7fcdb1b..3c0d0ec 100644 --- a/.github/workflows/runnable.yml +++ b/.github/workflows/runnable.yml @@ -78,3 +78,23 @@ jobs: - run: flutter pub get - run: sudo echo "y" | sudo $ANDROID_HOME/tools/bin/sdkmanager "ndk;21.4.7075529" - run: cd example; flutter build apk --debug + + test_macOS: + needs: analyze + name: Test macOS build on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ macos-latest ] + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-java@v1 + with: + java-version: '11.x' + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - run: dart --version + - run: flutter --version + - run: flutter pub get + - run: cd example; flutter build macos --debug diff --git a/example/lib/add_text_page.dart b/example/lib/add_text_page.dart index 7d1d26a..bbd478e 100644 --- a/example/lib/add_text_page.dart +++ b/example/lib/add_text_page.dart @@ -55,44 +55,50 @@ class _AddTextPageState extends State { ], ), ), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - onPressed: () async { - await addText(fontName); - }, - child: const Text('add'), - ), - ElevatedButton( - onPressed: () async { - await addText(''); - }, - child: const Text('add use defaultFont'), - ), - ], + Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: () async { + await addText(fontName); + }, + child: const Text('add text'), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: () async { + await addText(''); + }, + child: const Text('add use defaultFont'), + ), ), - ElevatedButton( - child: Text('download and register font'), - onPressed: () async { - final aliFontUrl = - 'https://cdn.jsdelivr.net/gh/kikt-blog/ali_font@master/Alibaba-PuHuiTi-Medium.ttf'; + Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + child: Text('download and register font'), + onPressed: () async { + final aliFontUrl = + 'https://cdn.jsdelivr.net/gh/kikt-blog/ali_font@master/Alibaba-PuHuiTi-Medium.ttf'; - final body = await http.get(Uri.parse(aliFontUrl)); + final body = await http.get(Uri.parse(aliFontUrl)); - final tmpDir = await pp.getTemporaryDirectory(); - final f = File( - '${tmpDir.absolute.path}/${DateTime.now().millisecondsSinceEpoch}.ttf'); - f.writeAsBytesSync(body.bodyBytes); + final tmpDir = await pp.getTemporaryDirectory(); + final f = File( + '${tmpDir.absolute.path}/${DateTime.now().millisecondsSinceEpoch}.ttf'); + f.writeAsBytesSync(body.bodyBytes); - fontName = await FontManager.registerFont(f); + fontName = await FontManager.registerFont(f); - showToast('register $fontName success'); - }, + showToast('register $fontName success'); + }, + ), ), - TextField( - controller: _controller, + Padding( + padding: const EdgeInsets.all(16), + child: TextField( + controller: _controller, + ), ), // Slider(value: null, onChanged: null), ], diff --git a/example/lib/draw_example_page.dart b/example/lib/draw_example_page.dart index fa906fa..6b71f5b 100644 --- a/example/lib/draw_example_page.dart +++ b/example/lib/draw_example_page.dart @@ -35,11 +35,15 @@ class _DrawExamplePageState extends State { ], ), ), - addLine(), - addRect(), - addOval(), - addPoints(), - buildDrawPath(), + ...[addLine(), addRect(), addOval(), addPoints(), buildDrawPath()] + .map( + (e) => Container( + width: 200, + height: 48, + margin: const EdgeInsets.all(8), + child: e, + ), + ), ], ), ); diff --git a/example/lib/home_page.dart b/example/lib/home_page.dart index 15a16b9..c8330b7 100644 --- a/example/lib/home_page.dart +++ b/example/lib/home_page.dart @@ -152,10 +152,3 @@ class _SimpleExamplePageState extends State { setProvider(img); } } - -Widget buildButton(String text, VoidCallback onTap) { - return TextButton( - child: Text(text), - onPressed: onTap, - ); -} diff --git a/example/lib/widget/examples.dart b/example/lib/widget/examples.dart index 89c7ad6..b7a43b8 100644 --- a/example/lib/widget/examples.dart +++ b/example/lib/widget/examples.dart @@ -23,21 +23,32 @@ class _ExamplesState extends State { @override Widget build(BuildContext context) { - return ListView.builder( - itemCount: widgets.length, - itemBuilder: (BuildContext context, int index) { - return _buildButton(widgets[index]); - }, + return Column( + children: widgets.map((e) => _buildButton(e)).toList(), ); } Widget _buildButton(Widget widget) { - return ElevatedButton( - onPressed: () { - Navigator.push(context, - MaterialPageRoute(builder: (BuildContext ctx) => widget)); - }, - child: Text(widget.runtimeType.toString()), + return Align( + alignment: Alignment.center, + child: Container( + margin: const EdgeInsets.only(top: 16), + width: 300, + height: 48, + child: ElevatedButton( + onPressed: () { + Navigator.push(context, + MaterialPageRoute(builder: (BuildContext ctx) => widget)); + }, + child: Text(toTitle(widget)), + ), + ), ); } + + String toTitle(Widget widget) { + final src = widget.runtimeType.toString(); + final regex = RegExp('([A-Z]){1}[a-z]+'); + return regex.allMatches(src).map((e) => e.group(0)).join(' '); + } } diff --git a/example/macos/.gitignore b/example/macos/.gitignore new file mode 100644 index 0000000..d2fd377 --- /dev/null +++ b/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..4b81f9b --- /dev/null +++ b/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..5caa9d1 --- /dev/null +++ b/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..9047de1 --- /dev/null +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import image_editor +import path_provider_macos + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ImageEditorPlugin.register(with: registry.registrar(forPlugin: "ImageEditorPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) +} diff --git a/example/macos/Podfile b/example/macos/Podfile new file mode 100644 index 0000000..9ec46f8 --- /dev/null +++ b/example/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e770fad --- /dev/null +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,634 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 17FB8559E8E3B00AA1E9E053 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 86B0324C3D4D9744C8010B64 /* Pods_Runner.framework */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 2434AB9A9C627917BFB4466B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 5CCDEC9E76C820DB74239BDA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 7BD719B3070995003DD09290 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 86B0324C3D4D9744C8010B64 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 17FB8559E8E3B00AA1E9E053 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 9F3ADB943EA198C48A6DE3AF /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + 9F3ADB943EA198C48A6DE3AF /* Pods */ = { + isa = PBXGroup; + children = ( + 5CCDEC9E76C820DB74239BDA /* Pods-Runner.debug.xcconfig */, + 7BD719B3070995003DD09290 /* Pods-Runner.release.xcconfig */, + 2434AB9A9C627917BFB4466B /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 86B0324C3D4D9744C8010B64 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 980227B1F583C111A3469EAF /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 5975D8372C413374481DB5C0 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 5975D8372C413374481DB5C0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 980227B1F583C111A3469EAF /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.15; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..ae8ff59 --- /dev/null +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..3c4935a Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..ed4cc16 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..483be61 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bcbf36d Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..9c0a652 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..e71a726 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..8a31fe2 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/example/macos/Runner/Base.lproj/MainMenu.xib b/example/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..537341a --- /dev/null +++ b/example/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..8501f78 --- /dev/null +++ b/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = top.kikt.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 top.kikt. All rights reserved. diff --git a/example/macos/Runner/Configs/Debug.xcconfig b/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/example/macos/Runner/Configs/Release.xcconfig b/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/example/macos/Runner/Configs/Warnings.xcconfig b/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/example/macos/Runner/DebugProfile.entitlements b/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..3ba6c12 --- /dev/null +++ b/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/example/macos/Runner/Info.plist b/example/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/example/macos/Runner/MainFlutterWindow.swift b/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..2722837 --- /dev/null +++ b/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/example/macos/Runner/Release.entitlements b/example/macos/Runner/Release.entitlements new file mode 100644 index 0000000..7a2230d --- /dev/null +++ b/example/macos/Runner/Release.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/example/pubspec.yaml b/example/pubspec.yaml index fb18dc4..738caae 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -14,10 +14,10 @@ dependencies: image_editor: path: ../ - extended_image: ^6.2.1 + extended_image: ^6.1.0 image_picker: ^0.8.5+3 - oktoast: ^3.2.0 - path_provider: ^2.0.11 + oktoast: ^3.1.5 + path_provider: ^2.0.0 flutter: uses-material-design: true diff --git a/macos/Classes/FICommonUtils.h b/macos/Classes/FICommonUtils.h new file mode 100644 index 0000000..3069c79 --- /dev/null +++ b/macos/Classes/FICommonUtils.h @@ -0,0 +1,14 @@ +// +// Created by jinglong cai on 2022/7/11. +// + +#import + + +@interface FICommonUtils : NSObject + ++ (NSMutableArray *)pointToArray:(CGPoint)point; + ++ (CGPoint)arrayToPoint:(NSArray *)arr; + +@end \ No newline at end of file diff --git a/macos/Classes/FICommonUtils.m b/macos/Classes/FICommonUtils.m new file mode 100644 index 0000000..6678030 --- /dev/null +++ b/macos/Classes/FICommonUtils.m @@ -0,0 +1,30 @@ +// +// Created by jinglong cai on 2022/7/11. +// + +#import "FICommonUtils.h" + + +@implementation FICommonUtils { + +} ++ (NSMutableArray *)pointToArray:(CGPoint)point { + NSMutableArray *arr = [NSMutableArray new]; + + [arr addObject:@(point.x)]; + [arr addObject:@(point.y)]; + + return arr; +} + ++ (CGPoint)arrayToPoint:(NSArray *)arr { + CGPoint result; + + result.x = [arr[0] doubleValue]; + result.x = [arr[1] doubleValue]; + + return result; +} + + +@end \ No newline at end of file diff --git a/macos/Classes/FIConvertUtils.h b/macos/Classes/FIConvertUtils.h new file mode 100644 index 0000000..44a7195 --- /dev/null +++ b/macos/Classes/FIConvertUtils.h @@ -0,0 +1,206 @@ +// +// Created by cjl on 2020/5/21. +// + +#import "FIImport.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +#define kCGBlendModeSrc 12345 +#define kCGBlendModeDst 12346 + +@class FIEditorOptionGroup; + +@interface FIConvertUtils : NSObject ++ (FIEditorOptionGroup *)getOptions:(NSArray *)dict; +@end + +@protocol FIOption + ++ (id)createFromDict:(NSDictionary *)dict; + +@end + +@interface FIFlipOption : NSObject + +@property(assign, nonatomic) BOOL horizontal; +@property(assign, nonatomic) BOOL vertical; + +@end + +@interface FIClipOption : NSObject +@property(assign, nonatomic) int x; +@property(assign, nonatomic) int y; +@property(assign, nonatomic) int width; +@property(assign, nonatomic) int height; +@end + +@interface FIRotateOption : NSObject +@property(assign, nonatomic) int degree; +@end + +@interface FIFormatOption : NSObject +@property(assign, nonatomic) int format; +@property(assign, nonatomic) int quality; +@end + +@interface FIColorOption : NSObject +@property(strong, nonatomic) NSArray *matrix; + +- (CGFloat)getValue:(NSUInteger)index; +@end + +@interface FIScaleOption : NSObject +@property(assign, nonatomic) int width; +@property(assign, nonatomic) int height; +@end + +@interface FIAddText : NSObject +@property(nonatomic, copy) NSString *text; +@property(nonatomic, assign) int x; +@property(nonatomic, assign) int y; +@property(nonatomic, assign) int fontSizePx; +@property(nonatomic, assign) int r; +@property(nonatomic, assign) int g; +@property(nonatomic, assign) int b; +@property(nonatomic, assign) int a; +@property(nonatomic, assign) NSString *fontName; +@end + +@interface FIAddTextOption : NSObject +@property(nonatomic, strong) NSArray *texts; +@end + +@interface FIMixImageOption : NSObject +@property(nonatomic, assign) int x; +@property(nonatomic, assign) int y; +@property(nonatomic, assign) int width; +@property(nonatomic, assign) int height; +@property(nonatomic, strong) NSData *src; +@property(nonatomic, strong) NSNumber *blendMode; +@end + +@interface FIEditorOptionGroup : NSObject +@property(nonatomic, strong) FIFormatOption *fmt; +@property(nonatomic, strong) NSArray *options; +@end + +@interface FIMergeImage : NSObject + +@property(nonatomic, strong) NSData *data; +@property(nonatomic, assign) int x; +@property(nonatomic, assign) int y; +@property(nonatomic, assign) int width; +@property(nonatomic, assign) int height; +@end + +@interface FIMergeOption : NSObject + +@property(nonatomic, strong) NSArray *images; +@property(nonatomic, assign) CGSize size; +@property(nonatomic, strong) FIFormatOption *format; + +@end + +@interface FIValueOption : NSObject +@property(nonatomic, strong) NSDictionary *map; +@end + +@interface FIPaint : FIValueOption + +@property(nonatomic, strong) FIColor *color; +@property(nonatomic, assign) int paintWeight; +@property(nonatomic, assign) bool fill; + +- (float)r; + +- (float)g; + +- (float)b; + +- (float)a; + +@end + +@interface FIDrawPart : FIValueOption + +- (FIPaint *)paint; + +- (CGRect)rect:(NSString *)key; + +- (CGPoint)point:(NSString *)key; + +@end + +@interface FIDrawOption : FIValueOption + +- (NSArray *)parts; + +@end + +@interface FILineDrawPart : FIDrawPart + +- (CGPoint)start; + +- (CGPoint)end; +@end + +@interface FIPointsDrawPart : FIDrawPart + +- (NSArray *)points; +@end + +@interface FIRectDrawPart : FIDrawPart + +- (CGRect)rect; +@end + +@interface FIOvalDrawPart : FIDrawPart + +- (CGRect)rect; +@end + +@interface FIPathDrawPart : FIDrawPart + +- (NSArray *)parts; + +- (BOOL) autoClose; + +@end + +@interface FIPathMove : FIDrawPart + +- (CGPoint)offset; +@end + + +@interface FIPathLine : FIDrawPart + +- (CGPoint)offset; +@end + +@interface FIPathArc : FIDrawPart + +- (CGFloat)start; + +- (CGFloat)sweep; + +- (BOOL)useCenter; + +- (CGRect)rect; +@end + +@interface FIPathBezier : FIDrawPart + +- (int)kind; + +- (CGPoint)target; + +- (CGPoint)control1; + +- (CGPoint)control2; +@end + + +NS_ASSUME_NONNULL_END diff --git a/macos/Classes/FIConvertUtils.m b/macos/Classes/FIConvertUtils.m new file mode 100644 index 0000000..10606cf --- /dev/null +++ b/macos/Classes/FIConvertUtils.m @@ -0,0 +1,551 @@ +// +// Created by cjl on 2020/5/21. +// + +#import "FIConvertUtils.h" +#import "FICommonUtils.h" + +@implementation FIConvertUtils { +} ++ (FIEditorOptionGroup *)getOptions:(NSArray *)dict { + FIEditorOptionGroup *group = [FIEditorOptionGroup new]; + NSMutableArray *optionArray = [NSMutableArray new]; + group.options = optionArray; + for (NSDictionary *map in dict) { + NSString *type = (NSString *) map[@"type"]; + NSDictionary *value = map[@"value"]; + NSObject *option; + if ([@"flip" isEqualToString:type]) { + option = [FIFlipOption createFromDict:value]; + } else if ([@"clip" isEqualToString:type]) { + option = [FIClipOption createFromDict:value]; + } else if ([@"rotate" isEqualToString:type]) { + option = [FIRotateOption createFromDict:value]; + } else if ([@"color" isEqualToString:type]) { + option = [FIColorOption createFromDict:value]; + } else if ([@"scale" isEqualToString:type]) { + option = [FIScaleOption createFromDict:value]; + } else if ([@"add_text" isEqualToString:type]) { + option = [FIAddTextOption createFromDict:value]; + } else if ([@"mix_image" isEqualToString:type]) { + option = [FIMixImageOption createFromDict:value]; + } else if ([@"draw" isEqualToString:type]) { + option = [FIDrawOption createFromDict:value]; + } + if (option) { + [optionArray addObject:option]; + } + } + return group; +} + +@end + +@implementation FIFlipOption { +} ++ (id)createFromDict:(NSDictionary *)dict { + FIFlipOption *option = [FIFlipOption new]; + option.horizontal = [dict[@"h"] boolValue]; + option.vertical = [dict[@"v"] boolValue]; + return option; +} + +@end + +@implementation FIClipOption { +} ++ (id)createFromDict:(NSDictionary *)dict { + FIClipOption *option = [FIClipOption new]; + option.x = [dict[@"x"] intValue]; + option.y = [dict[@"y"] intValue]; + option.width = [dict[@"width"] intValue]; + option.height = [dict[@"height"] intValue]; + return option; +} + +@end + +@implementation FIRotateOption { +} ++ (id)createFromDict:(NSDictionary *)dict { + FIRotateOption *option = [FIRotateOption new]; + option.degree = [dict[@"degree"] intValue]; + return option; +} + +@end + +@implementation FIFormatOption { +} ++ (id)createFromDict:(NSDictionary *)dict { + FIFormatOption *option = [FIFormatOption new]; + option.format = [dict[@"format"] intValue]; + option.quality = [dict[@"quality"] intValue]; + return option; +} + +@end + +@implementation FIColorOption + ++ (id)createFromDict:(NSDictionary *)dict { + NSArray *array = dict[@"matrix"]; + FIColorOption *option = [FIColorOption new]; + option.matrix = array; + return option; +} + +- (CGFloat)getValue:(NSUInteger)index { + return [self.matrix[index] floatValue]; +} + +@end + +@implementation FIScaleOption ++ (id)createFromDict:(NSDictionary *)dict { + FIScaleOption *option = [FIScaleOption new]; + option.width = [dict[@"width"] intValue]; + option.height = [dict[@"height"] intValue]; + return option; +} + +@end + +@implementation FIAddText ++ (id)createFromDict:(NSDictionary *)dict { + FIAddText *text = [FIAddText new]; + + text.text = dict[@"text"]; + text.fontSizePx = [dict[@"size"] intValue]; + + text.x = [dict[@"x"] intValue]; + text.y = [dict[@"y"] intValue]; + + text.r = [dict[@"r"] intValue]; + text.g = [dict[@"g"] intValue]; + text.b = [dict[@"b"] intValue]; + text.a = [dict[@"a"] intValue]; + text.fontName = dict[@"fontName"]; + + return text; +} +@end + +@implementation FIAddTextOption + ++ (id)createFromDict:(NSDictionary *)dict { + FIAddTextOption *opt = [FIAddTextOption new]; + + NSArray *src = dict[@"texts"]; + NSMutableArray *arr = [NSMutableArray new]; + + for (NSDictionary *value in src) { + FIAddText *addText = [FIAddText createFromDict:value]; + [arr addObject:addText]; + } + + opt.texts = arr; + + return opt; +} + +@end + +static NSDictionary *mixBlendModeDict; + +@implementation FIMixImageOption { +} + ++ (id)createFromDict:(NSDictionary *)dict { + if (!mixBlendModeDict) { + mixBlendModeDict = @{ + @"clear": @(kCGBlendModeClear), + @"src": @(kCGBlendModeSrc), + @"dst": @(kCGBlendModeDst), + @"srcOver": @(kCGBlendModeNormal), + @"dstOver": @(kCGBlendModeDestinationOver), + @"srcIn": @(kCGBlendModeSourceIn), + @"dstIn": @(kCGBlendModeDestinationIn), + @"srcOut": @(kCGBlendModeSourceOut), + @"dstOut": @(kCGBlendModeDestinationOver), + @"srcATop": @(kCGBlendModeSourceAtop), + @"dstATop": @(kCGBlendModeDestinationAtop), + @"xor": @(kCGBlendModeXOR), + @"darken": @(kCGBlendModeDarken), + @"lighten": @(kCGBlendModeLighten), + @"multiply": @(kCGBlendModeMultiply), + @"screen": @(kCGBlendModeScreen), + @"overlay": @(kCGBlendModeOverlay), + + }; + } + + FIMixImageOption *option = [FIMixImageOption new]; + + option.x = [dict[@"x"] intValue]; + option.y = [dict[@"y"] intValue]; + option.width = [dict[@"w"] intValue]; + option.height = [dict[@"h"] intValue]; + + option.src = ((FlutterStandardTypedData *) dict[@"target"][@"memory"]).data; + NSString *modeString = dict[@"mixMode"]; + option.blendMode = mixBlendModeDict[modeString]; + + return option; +} +@end + +@implementation FIEditorOptionGroup { +} + +@end + +@implementation FIMergeImage + ++ (nonnull id)createFromDict:(nonnull NSDictionary *)dict { + FIMergeImage *image = [FIMergeImage new]; + + image.data = ((FlutterStandardTypedData *) dict[@"src"][@"memory"]).data; + NSDictionary *position = dict[@"position"]; + image.x = [position[@"x"] intValue]; + image.y = [position[@"y"] intValue]; + image.width = [position[@"w"] intValue]; + image.height = [position[@"h"] intValue]; + + return image; +} + +@end + +@implementation FIMergeOption + ++ (nonnull id)createFromDict:(nonnull NSDictionary *)dict { + FIMergeOption *opt = [FIMergeOption new]; + + NSArray *imageOpt = dict[@"images"]; + + NSMutableArray *optionArray = [NSMutableArray new]; + opt.images = optionArray; + for (NSDictionary *value in imageOpt) { + [optionArray addObject:[FIMergeImage createFromDict:value]]; + } + + int w = [dict[@"w"] intValue]; + int h = [dict[@"h"] intValue]; + + opt.size = CGSizeMake(w, h); + + opt.format = [FIFormatOption createFromDict:dict[@"fmt"]]; + + return opt; +} + +@end + +@implementation FIDrawOption + ++ (nonnull id)createFromDict:(nonnull NSDictionary *)dict { + FIDrawOption *option = [FIDrawOption new]; + option.map = dict; + return option; +} + +- (NSArray *)parts { + NSMutableArray *array = [NSMutableArray new]; + for (NSDictionary *dict in self.map[@"parts"]) { + NSString *key = dict[@"key"]; + NSDictionary *value = dict[@"value"]; + if (key) { + FIDrawPart *part; + if ([key isEqualToString:@"rect"]) { + part = [FIRectDrawPart createFromDict:value]; + } else if ([key isEqualToString:@"oval"]) { + part = [FIOvalDrawPart createFromDict:value]; + } else if ([key isEqualToString:@"line"]) { + part = [FILineDrawPart createFromDict:value]; + } else if ([key isEqualToString:@"point"]) { + part = [FIPointsDrawPart createFromDict:value]; + } else if ([key isEqualToString:@"path"]) { + part = [FIPathDrawPart createFromDict:value]; + } + + if (part) { + [array addObject:part]; + } + } + } + + return array; +} + +@end + +@implementation FIPaint + ++ (id)createFromDict:(NSDictionary *)dict { + FIPaint *ins = [FIPaint new]; + ins.map = dict; + + ins.fill = [dict[@"paintStyleFill"] boolValue]; + ins.paintWeight = [dict[@"lineWeight"] intValue]; + CGFloat r = [ins r]; + CGFloat g = [ins g]; + CGFloat b = [ins b]; + CGFloat a = [ins a]; + ins.color = [FIColor colorWithRed:r / 255.0 green:g / 255.0 blue:b / 255.0 alpha:a / 255.0]; + + return ins; +} + +- (float)r { + return [self.map[@"color"][@"r"] floatValue] / 255; +} + +- (float)g { + return [self.map[@"color"][@"g"] floatValue] / 255; +} + +- (float)b { + return [self.map[@"color"][@"b"] floatValue] / 255 ; +} + +- (float)a { + return [self.map[@"color"][@"a"] floatValue] / 255; +} + + +@end + +@implementation FIDrawPart ++ (id)createFromDict:(NSDictionary *)dict { + FIDrawPart *ins = [FIDrawPart new]; + ins.map = dict; + return ins; +} + +- (FIPaint *)paint { + return [FIPaint createFromDict:self.map[@"paint"]]; +} + +- (CGRect)rect:(NSString *)key { + NSDictionary *m = self.map[key]; + float left = [m[@"left"] floatValue]; + float top = [m[@"top"] floatValue]; + float w = [m[@"width"] floatValue]; + float h = [m[@"height"] floatValue]; + CGRect result = CGRectMake(left, top, w, h); + return result; +} + +- (CGPoint)point:(NSString *)key { + NSDictionary *m = self.map[key]; + float x = [m[@"x"] floatValue]; + float y = [m[@"y"] floatValue]; + return CGPointMake(x, y); +} + + +@end + +@implementation FIValueOption +@end + +@implementation FILineDrawPart + ++ (id)createFromDict:(NSDictionary *)dict { + FILineDrawPart *part = [FILineDrawPart new]; + part.map = dict; + return part; +} + +- (CGPoint)start { + return [self point:@"start"]; +} + + +- (CGPoint)end { + return [self point:@"end"]; +} + +@end + +@implementation FIPointsDrawPart + ++ (id)createFromDict:(NSDictionary *)dict { + FIPointsDrawPart *part = [FIPointsDrawPart new]; + part.map = dict; + return part; +} + +- (NSArray *)points { + NSMutableArray *result = [NSMutableArray new]; + NSArray *offsets = self.map[@"offset"]; + for (NSDictionary *map in offsets) { + int x = [map[@"x"] intValue]; + int y = [map[@"y"] intValue]; + CGPoint point = CGPointMake(x, y); +// [result addObject:[NSValue valueWithCGPoint:point]]; + [result addObject:[FICommonUtils pointToArray:point]]; + } + + return result; +} + +@end + +@implementation FIRectDrawPart + ++ (id)createFromDict:(NSDictionary *)dict { + FIRectDrawPart *part = [FIRectDrawPart new]; + part.map = dict; + return part; +} + +- (CGRect)rect { + return [self rect:@"rect"]; +} + +@end + +@implementation FIOvalDrawPart + ++ (id)createFromDict:(NSDictionary *)dict { + FIOvalDrawPart *part = [FIOvalDrawPart new]; + part.map = dict; + return part; +} + +- (CGRect)rect { + return [self rect:@"rect"]; +} + +@end + +@implementation FIPathDrawPart + ++ (id)createFromDict:(NSDictionary *)dict { + FIPathDrawPart *part = [FIPathDrawPart new]; + part.map = dict; + return part; +} + +- (BOOL)autoClose { + return [self.map[@"autoClose"] boolValue]; +} + +- (NSArray *)parts { + NSMutableArray *result = [NSMutableArray new]; + + NSArray *arr = self.map[@"parts"]; + + for (int i = 0; i < arr.count; ++i) { + NSDictionary *kv = arr[(NSUInteger) i]; + NSString *key = kv[@"key"]; + NSDictionary *value = kv[@"value"]; + if (key && value) { + FIDrawPart *part; + if ([key isEqualToString:@"move"]) { + part = [FIPathMove createFromDict:value]; + } else if ([key isEqualToString:@"lineTo"]) { + part = [FIPathLine createFromDict:value]; + } else if ([key isEqualToString:@"bezier"]) { + part = [FIPathBezier createFromDict:value]; + } else if ([key isEqualToString:@"arcTo"]) { + part = [FIPathArc createFromDict:value]; + } + + if (part) { + [result addObject:part]; + } + } + } + + return result; +} + +@end + + +@implementation FIPathMove + ++ (id)createFromDict:(NSDictionary *)dict { + FIPathMove *ins = [FIPathMove new]; + ins.map = dict; + return ins; +} + +- (CGPoint)offset { + return [self point:@"offset"]; +} + +@end + + +@implementation FIPathLine + ++ (id)createFromDict:(NSDictionary *)dict { + FIPathLine *ins = [FIPathLine new]; + ins.map = dict; + return ins; +} + +- (CGPoint)offset { + return [self point:@"offset"]; +} + +@end + +@implementation FIPathArc + ++ (id)createFromDict:(NSDictionary *)dict { + FIPathArc *ins = [FIPathArc new]; + ins.map = dict; + return ins; +} + +- (CGFloat)start { + return [self.map[@"start"] floatValue]; +} + +- (CGFloat)sweep { + return [self.map[@"sweep"] floatValue]; +} + +- (BOOL)useCenter { + return [self.map[@"sweep"] boolValue]; +} + +- (CGRect)rect { + return [self rect:@"rect"]; +} + +@end + +@implementation FIPathBezier + ++ (id)createFromDict:(NSDictionary *)dict { + FIPathBezier *ins = [FIPathBezier new]; + ins.map = dict; + return ins; +} + +- (int)kind { + return [self.map[@"kind"] intValue]; +} + +- (CGPoint)target { + return [self point:@"target"]; +} + + +- (CGPoint)control1 { + return [self point:@"c1"]; +} + + +- (CGPoint)control2 { + return [self point:@"c2"]; +} + +@end + diff --git a/macos/Classes/FIEPlugin.h b/macos/Classes/FIEPlugin.h new file mode 100644 index 0000000..e38ba61 --- /dev/null +++ b/macos/Classes/FIEPlugin.h @@ -0,0 +1,17 @@ +// +// FIEPlugin.h +// image_editor +// +// Created by Caijinglong on 2020/5/22. +// + +#import "FIImport.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIEPlugin : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/macos/Classes/FIEPlugin.m b/macos/Classes/FIEPlugin.m new file mode 100644 index 0000000..9a9f14d --- /dev/null +++ b/macos/Classes/FIEPlugin.m @@ -0,0 +1,206 @@ +// +// FIEPlugin.m +// image_editor +// +// Created by Caijinglong on 2020/5/22. +// + +#import "FIEPlugin.h" +#import "FIConvertUtils.h" +#import "FIMerger.h" +#import "FIUIImageHandler.h" + +typedef void (^FontBlock)(FIFont *font,NSString* name); +typedef void (^VoidBlock)(void); + +@implementation FIEPlugin { + dispatch_queue_global_t _queue; +} + ++ (void)registerWithRegistrar: + (nonnull NSObject *)registrar { + FIEPlugin *plugin = [FIEPlugin new]; + [plugin initQueue]; + FlutterMethodChannel *channel = [FlutterMethodChannel + methodChannelWithName:@"com.fluttercandies/image_editor" + binaryMessenger:registrar.messenger]; + + [registrar addMethodCallDelegate:plugin channel:channel]; +} + +- (void)handleMethodCall:(FlutterMethodCall *)call + result:(FlutterResult)result { + NSString *method = call.method; + id args = call.arguments; + if ([method isEqualToString:@"getCachePath"]) { + result(NSTemporaryDirectory()); + } else if ([method isEqualToString:@"memoryToFile"]) { + [self handleArgs:args outMemory:NO result:result]; + } else if ([method isEqualToString:@"memoryToMemory"]) { + [self handleArgs:args outMemory:YES result:result]; + } else if ([method isEqualToString:@"fileToMemory"]) { + [self handleArgs:args outMemory:YES result:result]; + } else if ([method isEqualToString:@"fileToFile"]) { + [self handleArgs:args outMemory:NO result:result]; + } else if ([method isEqualToString:@"mergeToMemory"]) { + [self handleMerge:args outMemory:YES result:result]; + } else if ([method isEqualToString:@"mergeToFile"]) { + [self handleMerge:args outMemory:NO result:result]; + } else if ([method isEqualToString:@"registerFont"]) { + [self asyncExec:^{ + NSString *path = args[@"path"]; + NSData *data = [NSData dataWithContentsOfFile:path]; + [self registerFontWithData:data block:^(FIFont *font, NSString *name) { + result(name); + }]; + }]; + } else { + result(FlutterMethodNotImplemented); + } +} + +- (void)initQueue { + _queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); +} + +- (void)asyncExec:(VoidBlock)block { + dispatch_async(_queue, ^(){ + @autoreleasepool { + block(); + } + }); +} + +- (void)handleMerge:(id)args + outMemory:(BOOL)outMemory + result:(FlutterResult)result { + [self asyncExec:^{ + FIMerger *merger = [FIMerger new]; + merger.option = [FIMergeOption createFromDict:args[@"option"]]; + NSData *data = [merger process]; + + if (!data) { + result([FlutterError errorWithCode:@"cannot merge image" + message:nil + details:nil]); + return; + } + + if (outMemory) { + dispatch_async(dispatch_get_main_queue(), ^{ + result(data); + }); + } else { + NSString *filePath = [merger makeOutputPath]; + [data writeToFile:filePath atomically:YES]; + dispatch_async(dispatch_get_main_queue(), ^{ + result(filePath); + }); + } + }]; +} + +- (void)handleArgs:(id)args + outMemory:(BOOL)outMemory + result:(FlutterResult)result { + [self asyncExec:^{ + FIImage *image = [self getUIImage:args]; + if (!image) { + dispatch_async(dispatch_get_main_queue(), ^{ + result([FlutterError errorWithCode:@"decode image error" + message:nil + details:nil]); + }); + return; + } + + NSDictionary *dict = args; + NSArray *optionArray = dict[@"options"]; + FIEditorOptionGroup *optionGroup = [FIConvertUtils getOptions:optionArray]; + optionGroup.fmt = [FIFormatOption createFromDict:dict[@"fmt"]]; + + FIUIImageHandler *handler = [FIUIImageHandler new]; + handler.image = image; + handler.optionGroup = optionGroup; + + [handler handleImage]; + if (outMemory) { + NSData *data = [handler outputMemory]; + dispatch_async(dispatch_get_main_queue(), ^{ + result(data); + }); + } else { + NSString *target = args[@"target"]; + BOOL success = [handler outputFile:target]; + dispatch_async(dispatch_get_main_queue(), ^{ + if (success) { + result(target); + } else { + result([FlutterError errorWithCode:@"cannot handle" + message:nil + details:nil]); + } + }); + } + }]; +} + +- (FIImage *)getUIImage:(id)args { + NSDictionary *dict = args; + NSString *path = dict[@"src"]; + if (path) { + NSURL *url = [NSURL URLWithString:path]; + if (!url) { + return nil; + } + +#if TARGET_OS_IOS + + return [FIImage imageWithContentsOfFile:url.absoluteString]; +#endif + +#if TARGET_OS_OSX + return [[FIImage alloc] initWithContentsOfFile:url.absoluteString]; +#endif + } + + FlutterStandardTypedData *fData = dict[@"image"]; + if (!fData) { + return nil; + } + + NSData *data = fData.data; + if (!data) { + return nil; + } +#if TARGET_OS_IOS + return [FIImage imageWithData:data]; +#endif + +#if TARGET_OS_OSX + return [[FIImage alloc] initWithData:data]; +#endif +} + +- (void)registerFontWithData:(NSData *)data block:(FontBlock)block { + + CFErrorRef error; + CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + CGFontRef font = CGFontCreateWithDataProvider(provider); + NSString *name = + (NSString *)CFBridgingRelease(CGFontCopyPostScriptName(font)); + + if (!CTFontManagerRegisterGraphicsFont(font, &error)) { + CFStringRef errorDescription = CFErrorCopyDescription(error); + NSLog(@"Failed to load font: %@", errorDescription); + CFRelease(errorDescription); + } else { + NSLog(@"register font success : %@", name); + } + FIFont *uiFont = [FIFont fontWithName:name size:14]; + CFRelease(font); + CFRelease(provider); + block(uiFont, name); +} + +@end diff --git a/macos/Classes/FIImport.h b/macos/Classes/FIImport.h new file mode 100644 index 0000000..ae5299d --- /dev/null +++ b/macos/Classes/FIImport.h @@ -0,0 +1,33 @@ +// +// FIImport.h +// Pods +// + +#ifndef FIImport_h +#define FIImport_h + +#if TARGET_OS_IOS +#import +typedef UIImage FIImage; +typedef UIFont FIFont; +typedef UIColor FIColor; +typedef UIBezierPath FIBezierPath; +typedef UIFont FIFont; +#endif + +#if TARGET_OS_OSX +#import +typedef NSImage FIImage; +typedef NSFont FIFont; +typedef NSColor FIColor; +typedef NSBezierPath FIBezierPath; +typedef NSFont FIFont; +#endif + +#if TARGET_OS_OSX +#import +#elif TARGET_OS_IOS +#import +#endif + +#endif /* PMImport_h */ diff --git a/macos/Classes/FIMerger.h b/macos/Classes/FIMerger.h new file mode 100644 index 0000000..3f3da37 --- /dev/null +++ b/macos/Classes/FIMerger.h @@ -0,0 +1,23 @@ +// +// FIMerger.h +// GPUImage +// +// Created by Caijinglong on 2020/5/27. +// + +#import +#import "FIConvertUtils.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface FIMerger : NSObject + +@property(nonatomic, strong) FIMergeOption *option; + +- (NSData *)process; + +- (NSString *)makeOutputPath; + +@end + +NS_ASSUME_NONNULL_END diff --git a/macos/Classes/FIMerger.m b/macos/Classes/FIMerger.m new file mode 100644 index 0000000..ff426e0 --- /dev/null +++ b/macos/Classes/FIMerger.m @@ -0,0 +1,61 @@ +// +// FIMerger.m +// GPUImage +// +// Created by Caijinglong on 2020/5/27. +// + +#import "FIMerger.h" +#import "FIUIImageHandler.h" + +@implementation FIMerger { +} + +- (NSData *)process { + FIMergeOption *opt = self.option; + + CGContextRef ctx = createCGContext(opt.size.width, opt.size.height); + if (!ctx) { + return nil; + } + + for (FIMergeImage *mergeImage in opt.images) { + CGRect rect = CGRectMake(mergeImage.x, mergeImage.y, mergeImage.width, mergeImage.height); + CGImageRef ref = [[[NSImage alloc] initWithData:mergeImage.data] CGImage]; + CGContextDrawImage(ctx, rect, ref); + } + + FIImage *newImage = getImageFromCGContext(ctx); + + releaseCGContext(ctx); + if (!newImage) { + return nil; + } + + return [self outputMemory:newImage]; +} + +- (NSData *)outputMemory:(FIImage *)image { + FIFormatOption *fmt = self.option.format; + + CGImageRef ref = [image CGImage]; + + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:ref]; + + if (fmt.format == 0) { + return [bitmap representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; + } else { + NSDictionary *props = @{NSImageCompressionFactor: @((fmt.quality / 100))}; + return [bitmap representationUsingType:NSBitmapImageFileTypeJPEG properties:props]; + } +} + +- (NSString *)makeOutputPath { + NSString *extName = self.option.format.format == 1 ? @"jpg" : @"png"; + NSString *cachePath = + NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; + long time = (long)[NSDate date].timeIntervalSince1970; + return [NSString stringWithFormat:@"%@/%ld.%@", cachePath, time, extName]; +} + +@end diff --git a/macos/Classes/FIUIImageHandler.h b/macos/Classes/FIUIImageHandler.h new file mode 100644 index 0000000..c58d307 --- /dev/null +++ b/macos/Classes/FIUIImageHandler.h @@ -0,0 +1,51 @@ +// +// FIUIImageHandler.h +// image_editor +// +// Created by Caijinglong on 2020/5/22. +// + +#import "FIImport.h" +#import +#import "FIConvertUtils.h" + +NS_ASSUME_NONNULL_BEGIN + +#ifdef TARGET_OS_OSX + +@interface NSImage (ext) +- (CGSize)pixelSize; + +- (CGFloat)retinaScale; + +- (CGImageRef)CGImage; + +- (CIImage *)CIImage; + +@end + +#endif + +void releaseCGContext(CGContextRef ref); + +CGContextRef createCGContext(size_t pixelsWide, size_t pixelsHigh); + +FIImage *getImageFromCGContext(CGContextRef context); + +@interface FIUIImageHandler : NSObject + +@property(strong, nonatomic) FIImage *image; +@property(strong, nonatomic) FIEditorOptionGroup *optionGroup; + +- (void)handleImage; + +- (BOOL)outputFile:(NSString *)targetPath; + +- (NSData *)outputMemory; + ++ (FIImage *)fixImageOrientation:(FIImage *)image; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/macos/Classes/FIUIImageHandler.m b/macos/Classes/FIUIImageHandler.m new file mode 100644 index 0000000..08977f4 --- /dev/null +++ b/macos/Classes/FIUIImageHandler.m @@ -0,0 +1,1087 @@ +// +// FIUIImageHandler.m +// image_editor +// +// Created by Caijinglong on 2020/5/22. +// + +#import "FIUIImageHandler.h" +#import "FICommonUtils.h" +#import +#import + +#if TARGET_OS_OSX + +void releaseCGContext(CGContextRef ref) { + char *bitmapData = CGBitmapContextGetData(ref); + if (bitmapData) free(bitmapData); + CGContextRelease(ref); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" +CGContextRef createCGContext(size_t pixelsWide, size_t pixelsHigh) { + CGContextRef context = NULL; + CGColorSpaceRef colorSpace; + void *bitmapData; + size_t bitmapByteCount; + size_t bitmapBytesPerRow; + + bitmapBytesPerRow = (pixelsWide * 4);// 1 + bitmapByteCount = (bitmapBytesPerRow * pixelsHigh); + + colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);// 2 + bitmapData = calloc(bitmapByteCount, sizeof(uint8_t));// 3 + if (bitmapData == NULL) { + fprintf(stderr, "Memory not allocated!"); + return NULL; + } + context = CGBitmapContextCreate(bitmapData,// 4 + pixelsWide, + pixelsHigh, + 8, // bits per component + bitmapBytesPerRow, + colorSpace, + kCGImageAlphaPremultipliedLast); + if (context == NULL) { + free(bitmapData);// 5 + fprintf(stderr, "Context not created!"); + return NULL; + } + CGColorSpaceRelease(colorSpace);// 6 + + return context;// 7 +} +#pragma clang diagnostic pop + +FIImage *getImageFromCGContext(CGContextRef context) { + size_t h = CGBitmapContextGetHeight(context); + size_t w = CGBitmapContextGetWidth(context); + + CGImageRef pImage = CGBitmapContextCreateImage(context); + + NSImage *image = [[FIImage alloc] initWithCGImage:pImage size:CGSizeMake(w, h)]; + CGImageRelease(pImage); + return image; +} + + +@implementation NSImage (ext) +- (CGSize)pixelSize { + if (self.representations.count == 0) { + return self.size; + } + NSInteger width = self.representations[0].pixelsWide; + NSInteger height = self.representations[0].pixelsHigh; + return CGSizeMake(width, height); +} + +- (CGFloat)retinaScale { + if (self.pixelSize.width == 0) { + return 1; + } + return self.pixelSize.width / self.size.width; +} + +- (CGImageRef)CGImage { + CGRect rect = CGRectMake(0, 0, self.pixelSize.width, self.pixelSize.height); + return [self CGImageForProposedRect:&rect context:nil hints:nil]; +} + + +- (CIImage *)CIImage { + CGImageRef srcImage = [self CGImage]; + return [[CIImage alloc] initWithCGImage:srcImage]; +} + +@end + +#endif + +@implementation FIUIImageHandler { + FIImage *outImage; +} + +- (void)handleImage { + outImage = self.image; + [self fixOrientation]; + for (NSObject *option in self.optionGroup.options) { + if ([option isKindOfClass:[FIFlipOption class]]) { + [self flip:(FIFlipOption *) option]; + } else if ([option isKindOfClass:[FIClipOption class]]) { + [self clip:(FIClipOption *) option]; + } else if ([option isKindOfClass:[FIRotateOption class]]) { + [self rotate:(FIRotateOption *) option]; + } else if ([option isKindOfClass:[FIColorOption class]]) { + [self colorMatrix:(FIColorOption *) option]; + } else if ([option isKindOfClass:[FIScaleOption class]]) { + [self scale:(FIScaleOption *) option]; + } else if ([option isKindOfClass:[FIAddTextOption class]]) { + [self addText:(FIAddTextOption *) option]; + } else if ([option isKindOfClass:[FIMixImageOption class]]) { + [self mixImage:(FIMixImageOption *) option]; + } else if ([option isKindOfClass:[FIDrawOption class]]) { + [self drawImage:(FIDrawOption *) option]; + } + } +} + +#pragma mark output + +- (BOOL)outputFile:(NSString *)targetPath { + NSData *data = [self outputMemory]; + if (!data) { + return NO; + } + NSURL *url = [NSURL fileURLWithPath:targetPath]; + [data writeToURL:url atomically:YES]; + return YES; +} + +- (void)fixOrientation { + outImage = [FIUIImageHandler fixImageOrientation:outImage]; +} + +#if TARGET_OS_OSX + +- (FIImage *)getImageWith:(CGContextRef)context { + return getImageFromCGContext(context); +} + +- (NSData *)outputMemory { + FIFormatOption *fmt = self.optionGroup.fmt; + + CGImageRef ref = [outImage CGImage]; + + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:ref]; + + if (fmt.format == 0) { + return [bitmap representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; + } else { + NSDictionary *props = @{NSImageCompressionFactor: @((fmt.quality / 100))}; + return [bitmap representationUsingType:NSBitmapImageFileTypeJPEG properties:props]; + } +} + ++ (FIImage *)fixImageOrientation:(FIImage *)image { + CGSize size = image.pixelSize; + CGContextRef context = createCGContext(size.width, size.height); + + CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), image.CGImage); + + FIImage *result = getImageFromCGContext(context); + + releaseCGContext(context); + + return result; +} + +#pragma mark flip + +- (FIImage *)toFIImage:(CGImageRef)cg width:(CGFloat)width height:(CGFloat)height { + CGSize size = CGSizeMake(width, height); + NSImage *image = [[FIImage alloc] initWithCGImage:cg size:size]; + + CGImageRelease(cg); + + return image; +} + +- (void)flip:(FIFlipOption *)option { + BOOL h = option.horizontal; + BOOL v = option.vertical; + if (!h && !v) { + return; + } + + NSSize size = outImage.size; + NSRect rect = NSMakeRect(0, 0, size.width, size.height); + + CGContextRef ctx = createCGContext(size.width, size.height); + + CGImageRef cg = [outImage CGImage]; + + CGContextClipToRect(ctx, rect); + + if (!v && h) { + CGContextTranslateCTM(ctx, size.width, 0); + CGContextScaleCTM(ctx, -1, 1); + } else if (v && !h) { + CGContextTranslateCTM(ctx, 0, size.height); + CGContextScaleCTM(ctx, 1, -1); + } else if (v && h) { + CGContextRotateCTM(ctx, M_PI); + CGContextTranslateCTM(ctx, -size.width, -size.height); + } + + CGContextDrawImage(ctx, rect, cg); + + outImage = [self getImageWith:ctx]; + releaseCGContext(ctx); +} + +#pragma mark clip + +- (void)clip:(FIClipOption *)option { + CGFloat w = option.width; + CGFloat h = option.height; + CGFloat x = option.x; + CGFloat y = option.y; + + CGRect targetRect = CGRectMake(x, y, w, h); + + CGImageRef srcImage = [outImage CGImage]; + + CGImageRef resultCg = CGImageCreateWithImageInRect(srcImage, targetRect); + + NSImage *image = [[NSImage alloc] initWithCGImage:resultCg size:NSMakeSize(w, h)]; + outImage = image; + + CGImageRelease(resultCg); +} + +#pragma mark rotate + +- (void)rotate:(FIRotateOption *)option { + double degree = 360 - option.degree; + CGFloat angle = [self convertDegreeToRadians:degree]; + CGSize oldSize = outImage.size; + CGRect oldRect = CGRectMake(0, 0, oldSize.width, oldSize.height); + CGAffineTransform aff = CGAffineTransformMakeRotation(angle); + CGRect newRect = CGRectApplyAffineTransform(oldRect, aff); + CGSize newSize = newRect.size; + + CGContextRef ctx = createCGContext((size_t) newSize.width, (size_t) newSize.height); + + if (!ctx) { + return; + } + + CGContextTranslateCTM(ctx, newSize.width / 2, newSize.height / 2); + CGContextRotateCTM(ctx, angle); + + CGImageRef cg = [outImage CGImage]; + + CGRect rect = CGRectMake(-oldSize.width / 2, -oldSize.height / 2, oldSize.width, oldSize.height); + CGContextDrawImage(ctx, rect, cg); + FIImage *newImage = [self getImageWith:ctx]; + releaseCGContext(ctx); + + if (!newImage) { + return; + } + + outImage = newImage; +} + +- (CGFloat)convertDegreeToRadians:(CGFloat)degree { + return degree * M_PI / 180; +} + +#pragma mark color matrix + + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "KeyValueCodingInspection" +- (void)colorMatrix:(FIColorOption *)option { + if (!outImage) { + return; + } + + CIFilter *filter = [CIFilter filterWithName:@"CIColorMatrix"]; + NSObject *matrix = (NSObject *) filter; + + [filter setDefaults]; + + CIImage *inputCIImage = [outImage CIImage]; + NSLog(@"input size = %@", NSStringFromRect([inputCIImage extent])); + [matrix setValue:inputCIImage forKey:kCIInputImageKey]; +// [matrix setRVector:[self getCIVector:option start:0]]; + [matrix setValue:[self getCIVector:option start:0] forKey:@"inputRVector"]; + [matrix setValue:[self getCIVector:option start:5] forKey:@"inputGVector"]; + [matrix setValue:[self getCIVector:option start:10] forKey:@"inputBVector"]; + [matrix setValue:[self getCIVector:option start:15] forKey:@"inputAVector"]; + [matrix setValue:[self getOffsetCIVector:option] forKey:@"inputBiasVector"]; + + CIImage *outputCIImage = [matrix outputImage]; + + if (!outputCIImage) { + return; + } + + CIContext *ctx = [[CIContext alloc] initWithOptions:@{}]; + CGImageRef cgImage = [ctx createCGImage:outputCIImage fromRect:[outputCIImage extent]]; + + FIImage *newImage = [self toFIImage:cgImage width:outImage.size.width height:outImage.size.height]; + + if (!newImage) { + return; + } + + outImage = newImage; +} +#pragma clang diagnostic pop + +- (CIVector *)getCIVector:(FIColorOption *)option start:(NSUInteger)start { + CGFloat v1 = [option getValue:start]; + CGFloat v2 = [option getValue:start + 1]; + CGFloat v3 = [option getValue:start + 2]; + CGFloat v4 = [option getValue:start + 3]; + return [CIVector vectorWithX:v1 Y:v2 Z:v3 W:v4]; +} + +- (CIVector *)getOffsetCIVector:(FIColorOption *)option { + CGFloat v1 = [option getValue:4]; + CGFloat v2 = [option getValue:9]; + CGFloat v3 = [option getValue:14]; + CGFloat v4 = [option getValue:19]; + return [CIVector vectorWithX:v1 Y:v2 Z:v3 W:v4]; +} + +#pragma mark scale + +- (void)scale:(FIScaleOption *)option { + if (!outImage) { + return; + } + + double width = option.width; + double height = option.height; + + CGContextRef context = createCGContext((size_t) width, (size_t) height); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), [outImage CGImage]); + + outImage = [self getImageWith:context]; + + releaseCGContext(context); +} + +#pragma mark add text + +- (void)addText:(FIAddTextOption *)option { + if (!outImage) { + return; + } + + if (option.texts.count == 0) { + return; + } + + CGContextRef ctx = createCGContext((size_t) outImage.size.width, (size_t) outImage.size.height); + + if (!ctx) { + return; + } + + CGContextDrawImage(ctx, CGRectMake(0, 0, outImage.size.width, outImage.size.height), [outImage CGImage]); + + for (FIAddText *text in option.texts) { + NSColor *color = [NSColor colorWithRed:(text.r / 255.0) green:(text.g / 255.0) blue:(text.b / 255.0) alpha:(text.a / 255.0)]; + + FIFont *font; + + if ([@"" isEqualToString:text.fontName]) { + font = [FIFont systemFontOfSize:text.fontSizePx]; + } else { + font = [FIFont fontWithName:text.fontName size:text.fontSizePx]; + } + + CGFloat w = outImage.size.width - text.x; + CGFloat h = outImage.size.height - text.y; + + CGRect rect = CGRectMake(text.x, text.y, w, h); + + [self addTextWithContext:ctx text:text.text color:color rect:rect fontName:font.fontName textSize:text.fontSizePx]; + } + + NSImage *newImage = [self getImageWith:ctx]; + + releaseCGContext(ctx); + + if (!newImage) { + return; + } + + outImage = newImage; +} + +- (void)addTextWithContext:(CGContextRef)context text:(NSString *)text color:(NSColor *)color rect:(CGRect)range fontName:(NSString *)fontName textSize:(CGFloat)textSize { + + // Set the text matrix. + CGContextSetTextMatrix(context, CGAffineTransformIdentity); + + // Create a path which bounds the area where you will be drawing text. + // The path need not be rectangular. + CGMutablePathRef path = CGPathCreateMutable(); + + // In this simple example, initialize a rectangular path. +// CGRect bounds = CGRectMake(10.0, 10.0, 200.0, 200.0); + CGRect bounds = range; + CGPathAddRect(path, NULL, bounds); + + // Initialize a string. +// CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine."); + CFStringRef textString = (__bridge CFStringRef) text; + + // Create a mutable attributed string with a max length of 0. + // The max length is a hint as to how much internal storage to reserve. + // 0 means no hint. + CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); + + // Copy the textString into the newly created attrString + CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), + textString); + + // Create a color that will be added as an attribute to the attrString. + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + CGFloat components[] = {color.redComponent, color.greenComponent, color.blueComponent, color.alphaComponent}; + + CGColorRef red = CGColorCreate(rgbColorSpace, components); + CGColorSpaceRelease(rgbColorSpace); + + // Set font + CFStringRef cfFontName = (__bridge CFStringRef) fontName; + CTFontRef font = CTFontCreateWithName(cfFontName, textSize, nil); + CFAttributedStringSetAttribute(attrString, CFRangeMake(0, text.length), kCTFontAttributeName, font); + + // Set the color of the first 12 chars to red. + CFAttributedStringSetAttribute(attrString, CFRangeMake(0, text.length), kCTForegroundColorAttributeName, red); + + // Create the framesetter with the attributed string. + CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString); + CFRelease(attrString); + + // Create a frame. + CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); + + // Draw the specified frame in the given context. + CTFrameDraw(frame, context); + + // Release the objects we used. + CFRelease(frame); + CFRelease(path); + CFRelease(framesetter); + CFRelease(font); + +// CFRelease(textString); +// CFRelease(cfFontName); +} + +#pragma mark mix image + +- (void)mixImage:(FIMixImageOption *)option { + if (!outImage) { + return; + } + + CGRect srcRect = CGRectMake(option.x, outImage.size.height - option.y - option.height, option.width, option.height); + CGRect dstRect = CGRectMake(0, 0, outImage.size.width, outImage.size.height); + + CGContextRef ctx = createCGContext((size_t) option.width, (size_t) option.height); + + if ([option.blendMode isEqualToNumber:@(kCGBlendModeDst)]) { + + CGContextDrawImage(ctx, dstRect, [outImage CGImage]); + } else if ([option.blendMode isEqualToNumber:@(kCGBlendModeSrc)]) { + NSImage *src = [[FIImage alloc] initWithData:option.src]; + CGContextDrawImage(ctx, srcRect, [src CGImage]); + } else { + CGContextDrawImage(ctx, dstRect, [outImage CGImage]); + CGContextSetBlendMode(ctx, (CGBlendMode) [option.blendMode intValue]); + NSImage *src = [[FIImage alloc] initWithData:option.src]; + CGContextDrawImage(ctx, srcRect, [src CGImage]); + } + + FIImage *image = [self getImageWith:ctx]; + outImage = image; + releaseCGContext(ctx); +} + +#pragma mark "draw some thing" + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "OCDFAInspection" +- (void)drawImage:(FIDrawOption *)option { + if (!outImage) { + return; + } + + CGContextRef ctx = createCGContext((size_t) outImage.size.width, (size_t) outImage.size.height); + if (!ctx) { + return; + } + + [outImage drawInRect:CGRectMake(0, 0, outImage.size.width, outImage.size.height)]; + + for (FIDrawPart *part in [option parts]) { + if ([part isMemberOfClass:FILineDrawPart.class]) { + [self draw:ctx line:(FILineDrawPart *) part]; + } else if ([part isMemberOfClass:FIOvalDrawPart.class]) { + [self draw:ctx oval:(FIOvalDrawPart *) part]; + } else if ([part isMemberOfClass:FIRectDrawPart.class]) { + [self draw:ctx rect:(FIRectDrawPart *) part]; + } else if ([part isMemberOfClass:FIPointsDrawPart.class]) { + [self draw:ctx points:(FIPointsDrawPart *) part]; + } else if ([part isMemberOfClass:FIPathDrawPart.class]) { + [self draw:ctx path:(FIPathDrawPart *) part]; + } + + } + + FIImage *newImage = [self getImageWith:ctx]; + releaseCGContext(ctx); + + if (!newImage) { + return; + } + + outImage = newImage; +} +#pragma clang diagnostic pop + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "OCDFAInspection" +- (void)draw:(CGContextRef)pContext path:(FIPathDrawPart *)path { + NSArray *parts = [path parts]; + + CGContextRef context = createCGContext((size_t) outImage.size.width, (size_t) outImage.size.height); + + for (FIDrawPart *part in parts) { + if ([part isMemberOfClass:[FIPathMove class]]) { + FIPathMove *move = (FIPathMove *) part; + CGPoint point = move.offset; + CGContextMoveToPoint(context, point.x, point.y); + + } else if ([part isMemberOfClass:[FIPathLine class]]) { + FIPathLine *line = (FIPathLine *) part; + CGPoint point = line.offset; + CGContextAddLineToPoint(context, point.x, point.y); + } else if ([part isMemberOfClass:[FIPathArc class]]) { + FIPathArc *arc = (FIPathArc *) part; + CGRect rect = [arc rect]; + CGPoint point = rect.origin; + CGFloat start = [arc start]; + CGFloat sweep = [arc sweep]; + CGFloat end = start + sweep; + BOOL closeWise = [arc useCenter]; + + CGPoint center = CGPointMake(point.x + rect.size.width / 2, point.y + rect.size.height / 2); +// // TODO: fix: calc radius + CGContextAddArc(context, center.x, center.y, 1, start, end, closeWise); + } else if ([part isMemberOfClass:[FIPathBezier class]]) { + FIPathBezier *bezier = (FIPathBezier *) part; + + int kind = [bezier kind]; + CGPoint point = [bezier target]; + CGPoint c1 = [bezier control1]; + if (kind == 2) { + CGContextAddQuadCurveToPoint(context, c1.x, c1.y, point.x, point.y); + } else if (kind == 3) { + CGPoint c2 = [bezier control2]; + CGContextAddCurveToPoint(context, c1.x, c1.y, c2.x, c2.y, point.x, point.y); + } + } + + } + + if ([path autoClose]) { + CGContextClosePath(context); + } + + [self drawWithPaint:pContext paint:[path paint]]; + + CGPathDrawingMode mode; + if ([path paint].fill) { + mode = kCGPathFill; + } else { + mode = kCGPathStroke; + } + CGContextDrawPath(pContext, mode); + + outImage = [self getImageWith:context]; + releaseCGContext(context); +} +#pragma clang diagnostic pop + +#endif + +#if TARGET_OS_IOS +- (NSData *)outputMemory { + FIFormatOption *fmt = self.optionGroup.fmt; + if (fmt.format == 0) { + return UIImagePNGRepresentation(outImage); + } else { + return UIImageJPEGRepresentation(outImage, ((CGFloat) fmt.quality) / 100); + } +} + ++ (FIImage *)fixImageOrientation:(FIImage *)image { + UIImageOrientation or = image.imageOrientation; + if (or == UIImageOrientationUp) { + return image; + } + + UIGraphicsBeginImageContext(image.size); + + [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)]; + + FIImage *result = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + if (!result) { + return image; + } else { + return result; + } +} + +#pragma mark flip + +- (void)flip:(FIFlipOption *)option { + BOOL h = option.horizontal; + BOOL v = option.vertical; + if (!h && !v) { + return; + } + + CGSize size = outImage.size; + + // UIGraphicsBeginImageContextWithOptions(size, YES, 1); + UIGraphicsBeginImageContext(outImage.size); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (!ctx) { + return; + } + + CGImageRef cg = outImage.CGImage; + + if (cg == nil) { + return; + } + + CGRect rect = CGRectMake(0, 0, size.width, size.height); + + CGContextClipToRect(ctx, rect); + + if (!v && h) { + CGContextRotateCTM(ctx, M_PI); + CGContextTranslateCTM(ctx, -size.width, -size.height); + } else if (v && !h) { + } else if (v && h) { + CGContextTranslateCTM(ctx, size.width, 0); + CGContextScaleCTM(ctx, -1, 1); + } else { + CGContextTranslateCTM(ctx, 0, size.height); + CGContextScaleCTM(ctx, 1, -1); + } + + CGContextDrawImage(ctx, rect, cg); + + FIImage *result = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + if (!result.CGImage) { + return; + } + + outImage = [UIImage imageWithCGImage:result.CGImage + scale:1 + orientation:[outImage imageOrientation]]; +} + +#pragma mark clip + +- (void)clip:(FIClipOption *)option { + CGImageRef cg = outImage.CGImage; + CGRect rect = CGRectMake(option.x, option.y, option.width, option.height); + CGImageRef resultCg = CGImageCreateWithImageInRect(cg, rect); + outImage = [UIImage imageWithCGImage:resultCg]; + CGImageRelease(resultCg); +} + +#pragma mark rotate + +- (void)rotate:(FIRotateOption *)option { + CGFloat redians = [self convertDegreeToRadians:option.degree]; + CGSize oldSize = outImage.size; + CGRect oldRect = CGRectMake(0, 0, oldSize.width, oldSize.height); + CGAffineTransform aff = CGAffineTransformMakeRotation(redians); + CGRect newRect = CGRectApplyAffineTransform(oldRect, aff); + CGSize newSize = newRect.size; + + UIGraphicsBeginImageContext(newSize); + + // UIGraphicsBeginImageContextWithOptions(newSize, YES, outImage.scale); + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (!ctx) { + return; + } + + CGContextTranslateCTM(ctx, newSize.width / 2, newSize.height / 2); + CGContextRotateCTM(ctx, redians); + + [outImage drawInRect:CGRectMake(-oldSize.width / 2, -oldSize.height / 2, oldSize.width, + oldSize.height)]; + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + if (!newImage) { + return; + } + + outImage = newImage; +} + +- (CGFloat)convertDegreeToRadians:(CGFloat)degree { + return degree * M_PI / 180; +} + +#pragma mark color matrix + + +- (void)colorMatrix:(FIColorOption *)option { + if (!outImage) { + return; + } + + CIFilter *filter = [CIFilter filterWithName:@"CIColorMatrix"]; + NSObject *matrix = (NSObject *) filter; + + [filter setDefaults]; + + CIImage *inputCIImage = [[CIImage alloc] initWithImage:outImage options:nil]; + NSLog(@"input size = %@", NSStringFromCGRect([inputCIImage extent])); + [matrix setValue:inputCIImage forKey:kCIInputImageKey]; +// [matrix setRVector:[self getCIVector:option start:0]]; + [matrix setValue:[self getCIVector:option start:0] forKey:@"inputRVector"]; + [matrix setValue:[self getCIVector:option start:5] forKey:@"inputGVector"]; + [matrix setValue:[self getCIVector:option start:10] forKey:@"inputBVector"]; + [matrix setValue:[self getCIVector:option start:15] forKey:@"inputAVector"]; + [matrix setValue:[self getOffsetCIVector:option] forKey:@"inputBiasVector"]; + + CIImage *outputCIImage = [matrix outputImage]; + + if (!outputCIImage) { + return; + } + + CIContext *ctx = [CIContext contextWithOptions:nil]; + CGImageRef cgImage = [ctx createCGImage:outputCIImage fromRect:[outputCIImage extent]]; + + FIImage *newImage = [UIImage imageWithCGImage:cgImage]; + + if (!newImage) { + return; + } + + outImage = newImage; +} + +- (CIVector *)getCIVector:(FIColorOption *)option start:(int)start { + CGFloat v1 = [option getValue:start]; + CGFloat v2 = [option getValue:start + 1]; + CGFloat v3 = [option getValue:start + 2]; + CGFloat v4 = [option getValue:start + 3]; + return [CIVector vectorWithX:v1 Y:v2 Z:v3 W:v4]; +} + +- (CIVector *)getOffsetCIVector:(FIColorOption *)option { + CGFloat v1 = [option getValue:4]; + CGFloat v2 = [option getValue:9]; + CGFloat v3 = [option getValue:14]; + CGFloat v4 = [option getValue:19]; + return [CIVector vectorWithX:v1 Y:v2 Z:v3 W:v4]; +} + +#pragma mark scale + +- (void)scale:(FIScaleOption *)option { + if (!outImage) { + return; + } + + UIGraphicsBeginImageContext(CGSizeMake(option.width, option.height)); + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (!ctx) { + return; + } + + [outImage drawInRect:CGRectMake(0, 0, option.width, option.height)]; + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + if (!newImage) { + return; + } + + outImage = newImage; +} + +#pragma mark add text + +- (void)addText:(FIAddTextOption *)option { + if (!outImage) { + return; + } + + if (option.texts.count == 0) { + return; + } + + // UIGraphicsBeginImageContextWithOptions(outImage.size, YES, outImage.scale); + UIGraphicsBeginImageContext(outImage.size); + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (!ctx) { + return; + } + + [outImage drawInRect:CGRectMake(0, 0, outImage.size.width, outImage.size.height)]; + + for (FIAddText *text in option.texts) { + UIColor *color = [UIColor colorWithRed:(text.r / 255.0) green:(text.g / 255.0) blue:(text.b / 255.0) alpha:(text.a / 255.0)]; + + UIFont *font; + + if ([@"" isEqualToString: text.fontName ]){ + font = [UIFont systemFontOfSize:text.fontSizePx]; + }else{ + font = [UIFont fontWithName:text.fontName size:text.fontSizePx]; + } + + + NSDictionary *attr = @{ + NSFontAttributeName: font, + NSForegroundColorAttributeName: color, + NSBackgroundColorAttributeName: UIColor.clearColor, + }; + + CGFloat w = outImage.size.width - text.x; + CGFloat h = outImage.size.height - text.y; + + CGRect rect = CGRectMake(text.x, text.y, w, h); + + [text.text drawInRect:rect withAttributes:attr]; + } + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + if (!newImage) { + return; + } + + outImage = newImage; +} + +#pragma mark mix image + +- (void)mixImage:(FIMixImageOption *)option { + if (!outImage) { + return; + } + + // UIGraphicsBeginImageContextWithOptions(outImage.size, YES, outImage.scale); + UIGraphicsBeginImageContext(outImage.size); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (!ctx) { + return; + } + + CGRect srcRect = CGRectMake(option.x, option.y, option.width, option.height); + CGRect dstRect = CGRectMake(0, 0, outImage.size.width, outImage.size.height); + if ([option.blendMode isEqualToNumber:@(kCGBlendModeDst)]) { + [outImage drawInRect:dstRect blendMode:[option.blendMode intValue] alpha:YES]; + } else if ([option.blendMode isEqualToNumber:@(kCGBlendModeSrc)]) { + UIImage *src = [UIImage imageWithData:option.src]; + [src drawInRect:srcRect blendMode:[option.blendMode intValue] alpha:YES]; + } else { + [outImage drawInRect:dstRect]; + UIImage *src = [UIImage imageWithData:option.src]; + [src drawInRect:srcRect blendMode:[option.blendMode intValue] alpha:YES]; + } + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + if (!newImage) { + return; + } + + outImage = newImage; +} + +#pragma mark "draw some thing" + +- (void)draw:(CGContextRef)ctx bezier:(FIBezierPath *)bezier paint:(FIPaint *)paint { + CGMutablePathRef path = CGPathCreateMutable(); +// UIColor *color = paint.color; + if (paint.fill) { +// [bezier fill]; +// CGContextSetFillColorWithColor(ctx, color.CGColor); + CGContextSetRGBFillColor(ctx, [paint r], [paint g], [paint b], [paint a]); + CGContextSetLineWidth(ctx, paint.paintWeight); + CGPathAddPath(path, nil, [bezier CGPath]); + CGContextDrawPath(ctx, kCGPathFill); + CGContextFillPath(ctx); + } else { +// [bezier stroke]; +// CGContextSetStrokeColorWithColor(ctx, color.CGColor); + CGContextSetRGBStrokeColor(ctx, [paint r], [paint g], [paint b], [paint a]); + CGContextSetLineWidth(ctx, paint.paintWeight); + CGPathAddPath(path, nil, [bezier CGPath]); + CGContextDrawPath(ctx, kCGPathStroke); + CGContextStrokePath(ctx); + } + + CGPathRelease(path); +} + +- (void)drawImage:(FIDrawOption *)option { + if (!outImage) { + return; + } + + UIGraphicsBeginImageContext(outImage.size); + CGContextRef ctx = UIGraphicsGetCurrentContext(); + if (!ctx) { + return; + } + + [outImage drawInRect:CGRectMake(0, 0, outImage.size.width, outImage.size.height)]; + + for (FIDrawPart *part in [option parts]) { + if ([part isMemberOfClass:FILineDrawPart.class]) { + [self draw:ctx line:(FILineDrawPart *) part]; + } else if ([part isMemberOfClass:FIOvalDrawPart.class]) { + [self draw:ctx oval:(FIOvalDrawPart *) part]; + } else if ([part isMemberOfClass:FIRectDrawPart.class]) { + [self draw:ctx rect:(FIRectDrawPart *) part]; + } else if ([part isMemberOfClass:FIPointsDrawPart.class]) { + [self draw:ctx points:(FIPointsDrawPart *) part]; + } else if ([part isMemberOfClass:FIPathDrawPart.class]) { + [self draw:ctx path:(FIPathDrawPart *) part]; + } + + } + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + if (!newImage) { + return; + } + + outImage = newImage; +} + +- (void)draw:(CGContextRef)pContext path:(FIPathDrawPart *)path { + NSArray *parts = [path parts]; + + FIBezierPath *bezierPath = [FIBezierPath bezierPath]; + + for (FIDrawPart *part in parts) { + if ([part isMemberOfClass:[FIPathMove class]]) { + FIPathMove *move = (FIPathMove *) part; + [bezierPath moveToPoint:[move offset]]; + } else if ([part isMemberOfClass:[FIPathLine class]]) { + FIPathLine *line = (FIPathLine *) part; + [bezierPath addLineToPoint:[line offset]]; + } else if ([part isMemberOfClass:[FIPathArc class]]) { + FIPathArc *arc = (FIPathArc *) part; + CGRect rect = [arc rect]; + CGPoint point = rect.origin; + CGFloat start = [arc start]; + CGFloat sweep = [arc sweep]; + CGFloat end = start + sweep; + BOOL closeWise = [arc useCenter]; + + CGPoint center = CGPointMake(point.x + rect.size.width / 2, point.y + rect.size.height / 2); + // TODO: fix: calc radius + [bezierPath addArcWithCenter:center radius:1 startAngle:start endAngle:end clockwise:closeWise]; + } else if ([part isMemberOfClass:[FIPathBezier class]]) { + FIPathBezier *bezier = (FIPathBezier *) part; + + int kind = [bezier kind]; + CGPoint point = [bezier target]; + CGPoint c1 = [bezier control1]; + if (kind == 2) { + [bezierPath addQuadCurveToPoint:point controlPoint:c1]; + } else if (kind == 3) { + CGPoint c2 = [bezier control2]; + [bezierPath addCurveToPoint:point controlPoint1:c1 controlPoint2:c2]; + } + } + + } + + if ([path autoClose]) { + [bezierPath closePath]; + } + + [self drawWithPaint:pContext paint:[path paint]]; + + CGPathRef pPath = [bezierPath CGPath]; + CGContextAddPath(pContext, pPath); + CGPathDrawingMode mode; + if ([path paint].fill) { + mode = kCGPathFill; + } else { + mode = kCGPathStroke; + } + CGContextDrawPath(pContext, mode); +} + +#endif + + +- (void)draw:(CGContextRef)pContext points:(FIPointsDrawPart *)points { + FIPaint *paint = [points paint]; + paint.fill = YES; + int weight = paint.paintWeight; + + for (NSArray *value in [points points]) { + CGPoint point = [FICommonUtils arrayToPoint:value]; + CGRect rect = CGRectMake(point.x - weight / 2, point.y - weight / 2, weight, weight); + CGContextAddEllipseInRect(pContext, rect); + } + [self drawWithPaint:pContext paint:paint]; +} + +- (void)draw:(CGContextRef)pContext rect:(FIRectDrawPart *)rect { + CGContextAddRect(pContext, [rect rect]); + [self drawWithPaint:pContext paint:[rect paint]]; +} + +- (void)draw:(CGContextRef)pContext oval:(FIOvalDrawPart *)oval { + CGContextAddEllipseInRect(pContext, [oval rect]); + [self drawWithPaint:pContext paint:[oval paint]]; +} + +- (void)draw:(CGContextRef)pContext line:(FILineDrawPart *)line { + CGPoint start = [line start]; + const CGPoint anEnd = [line end]; + CGContextMoveToPoint(pContext, start.x, start.y); + CGContextAddLineToPoint(pContext, anEnd.x, anEnd.y); + [self drawWithPaint:pContext paint:[line paint]]; +} + +- (void)drawWithPaint:(CGContextRef)ctx paint:(FIPaint *)paint { + CGContextSetLineWidth(ctx, paint.paintWeight); + if (paint.fill) { + CGContextSetRGBFillColor(ctx, [paint r], [paint g], [paint b], [paint a]); + CGContextFillPath(ctx); + } else { + CGContextSetRGBStrokeColor(ctx, [paint r], [paint g], [paint b], [paint a]); + CGContextStrokePath(ctx); + } +} + +@end diff --git a/macos/Classes/ImageEditorPlugin.h b/macos/Classes/ImageEditorPlugin.h new file mode 100644 index 0000000..2322342 --- /dev/null +++ b/macos/Classes/ImageEditorPlugin.h @@ -0,0 +1,4 @@ +#import "FIImport.h" + +@interface ImageEditorPlugin : NSObject +@end diff --git a/macos/Classes/ImageEditorPlugin.m b/macos/Classes/ImageEditorPlugin.m new file mode 100644 index 0000000..95e998b --- /dev/null +++ b/macos/Classes/ImageEditorPlugin.m @@ -0,0 +1,9 @@ +#import "ImageEditorPlugin.h" +#import "FIEPlugin.h" + +@implementation ImageEditorPlugin ++ (void)registerWithRegistrar:(NSObject *)registrar { + [FIEPlugin registerWithRegistrar:registrar]; +} + +@end diff --git a/macos/image_editor.podspec b/macos/image_editor.podspec new file mode 100644 index 0000000..ff80775 --- /dev/null +++ b/macos/image_editor.podspec @@ -0,0 +1,28 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint flutter_image_editor.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'image_editor' + s.version = '1.0.0' + s.summary = 'A flutter plugin for edit image data.' + s.description = <<-DESC +A new flutter plugin project. + DESC + s.homepage = 'https://github.com/fluttercandies/flutter_image_editor' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + + s.osx.dependency 'FlutterMacOS' + s.ios.dependency 'Flutter' + + s.ios.deployment_target = '9.0' + s.osx.deployment_target = '10.15' + + # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } +end diff --git a/pubspec.yaml b/pubspec.yaml index dccc83c..b48996b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,3 +22,5 @@ flutter: pluginClass: ImageEditorPlugin ios: pluginClass: ImageEditorPlugin + macos: + pluginClass: ImageEditorPlugin