appium-webdriveragent 5.14.0 → 5.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/WebDriverAgent.xcodeproj/project.pbxproj +20 -20
- package/WebDriverAgentLib/Categories/XCUIApplication+FBHelpers.m +9 -2
- package/WebDriverAgentLib/Commands/FBSessionCommands.m +4 -0
- package/WebDriverAgentLib/Utilities/FBConfiguration.h +15 -2
- package/WebDriverAgentLib/Utilities/FBConfiguration.m +10 -0
- package/WebDriverAgentLib/Utilities/{FBImageIOScaler.h → FBImageProcessor.h} +9 -16
- package/WebDriverAgentLib/Utilities/FBImageProcessor.m +171 -0
- package/WebDriverAgentLib/Utilities/FBImageUtils.h +6 -0
- package/WebDriverAgentLib/Utilities/FBImageUtils.m +34 -0
- package/WebDriverAgentLib/Utilities/FBMjpegServer.m +14 -21
- package/WebDriverAgentLib/Utilities/FBScreenshot.h +0 -13
- package/WebDriverAgentLib/Utilities/FBScreenshot.m +2 -14
- package/WebDriverAgentLib/Utilities/FBSettings.h +1 -0
- package/WebDriverAgentLib/Utilities/FBSettings.m +1 -0
- package/WebDriverAgentTests/IntegrationTests/{FBImageIOScalerTests.m → FBImageProcessorTests.m} +22 -22
- package/WebDriverAgentTests/IntegrationTests/XCUIDeviceHelperTests.m +5 -7
- package/package.json +2 -2
- package/WebDriverAgentLib/Utilities/FBImageIOScaler.m +0 -195
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
## [5.15.2](https://github.com/appium/WebDriverAgent/compare/v5.15.1...v5.15.2) (2023-11-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* fix run test ci ([#814](https://github.com/appium/WebDriverAgent/issues/814)) ([014d04d](https://github.com/appium/WebDriverAgent/commit/014d04df956e47fef67938b089511e80d344f007))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Miscellaneous Chores
|
|
10
|
+
|
|
11
|
+
* a dummy commit to check a package release ([08388fd](https://github.com/appium/WebDriverAgent/commit/08388fd602ee9d588a8780e8d141d748813782ed))
|
|
12
|
+
|
|
13
|
+
## [5.15.1](https://github.com/appium/WebDriverAgent/compare/v5.15.0...v5.15.1) (2023-11-16)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* Content-Type of the MJPEG server ([b4704da](https://github.com/appium/WebDriverAgent/commit/b4704dafc4567e1f0dc8675facfc48a195aae4bf))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Code Refactoring
|
|
22
|
+
|
|
23
|
+
* Optimize screenshots preprocessing ([#812](https://github.com/appium/WebDriverAgent/issues/812)) ([0b41757](https://github.com/appium/WebDriverAgent/commit/0b41757c0d21004afab32860b4e510d4bc426018))
|
|
24
|
+
|
|
25
|
+
## [5.15.0](https://github.com/appium/WebDriverAgent/compare/v5.14.0...v5.15.0) (2023-11-16)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* Add element attributes to the performAccessibilityAudit output ([#808](https://github.com/appium/WebDriverAgent/issues/808)) ([0d7e4a6](https://github.com/appium/WebDriverAgent/commit/0d7e4a697adb7355279583eaa05118f396056e6f))
|
|
31
|
+
|
|
1
32
|
## [5.14.0](https://github.com/appium/WebDriverAgent/compare/v5.13.3...v5.14.0) (2023-11-10)
|
|
2
33
|
|
|
3
34
|
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
315A15072518CC2800A3A064 /* TouchSpotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 315A15062518CC2800A3A064 /* TouchSpotView.m */; };
|
|
39
39
|
315A150A2518D6F400A3A064 /* TouchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 315A15092518D6F400A3A064 /* TouchViewController.m */; };
|
|
40
40
|
6385F4A7220A40760095BBDB /* XCUIApplicationProcessDelay.m in Sources */ = {isa = PBXBuildFile; fileRef = 6385F4A5220A40760095BBDB /* XCUIApplicationProcessDelay.m */; };
|
|
41
|
-
63CCF91221ECE4C700E94ABD /*
|
|
42
|
-
63CCF91321ECE4C700E94ABD /*
|
|
43
|
-
63FD950221F9D06100A3E356 /*
|
|
44
|
-
63FD950321F9D06100A3E356 /*
|
|
45
|
-
63FD950421F9D06200A3E356 /*
|
|
41
|
+
63CCF91221ECE4C700E94ABD /* FBImageProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 63CCF91021ECE4C700E94ABD /* FBImageProcessor.h */; };
|
|
42
|
+
63CCF91321ECE4C700E94ABD /* FBImageProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CCF91121ECE4C700E94ABD /* FBImageProcessor.m */; };
|
|
43
|
+
63FD950221F9D06100A3E356 /* FBImageProcessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageProcessorTests.m */; };
|
|
44
|
+
63FD950321F9D06100A3E356 /* FBImageProcessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageProcessorTests.m */; };
|
|
45
|
+
63FD950421F9D06200A3E356 /* FBImageProcessorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 631B523421F6174300625362 /* FBImageProcessorTests.m */; };
|
|
46
46
|
641EE3452240C1C800173FCB /* UITestingUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = EE9AB7FD1CAEE048008C271F /* UITestingUITests.m */; };
|
|
47
47
|
641EE5D72240C5CA00173FCB /* FBScreenshotCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = EE9AB75F1CAEDF0C008C271F /* FBScreenshotCommands.m */; };
|
|
48
48
|
641EE5D92240C5CA00173FCB /* XCUIElement+FBPickerWheel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7136A4781E8918E60024FC3D /* XCUIElement+FBPickerWheel.m */; };
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
641EE5FE2240C5CA00173FCB /* XCUIElement+FBWebDriverAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = EEE376481D59FAE900ED88DD /* XCUIElement+FBWebDriverAttributes.m */; };
|
|
81
81
|
641EE5FF2240C5CA00173FCB /* XCUIElement+FBForceTouch.m in Sources */ = {isa = PBXBuildFile; fileRef = EE8DDD7C20C5733B004D4925 /* XCUIElement+FBForceTouch.m */; };
|
|
82
82
|
641EE6002240C5CA00173FCB /* FBTouchActionCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 71241D7A1FAE3D2500B9559F /* FBTouchActionCommands.m */; };
|
|
83
|
-
641EE6012240C5CA00173FCB /*
|
|
83
|
+
641EE6012240C5CA00173FCB /* FBImageProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 63CCF91121ECE4C700E94ABD /* FBImageProcessor.m */; };
|
|
84
84
|
641EE6022240C5CA00173FCB /* FBTouchIDCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = EE9AB7631CAEDF0C008C271F /* FBTouchIDCommands.m */; };
|
|
85
85
|
641EE6032240C5CA00173FCB /* FBDebugCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = EE9AB7551CAEDF0C008C271F /* FBDebugCommands.m */; };
|
|
86
86
|
641EE6042240C5CA00173FCB /* NSString+FBXMLSafeString.m in Sources */ = {isa = PBXBuildFile; fileRef = 716E0BCD1E917E810087A825 /* NSString+FBXMLSafeString.m */; };
|
|
@@ -222,7 +222,7 @@
|
|
|
222
222
|
641EE6A32240C5CA00173FCB /* XCUIApplication+FBTouchAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 71BD20711F86116100B36EC2 /* XCUIApplication+FBTouchAction.h */; };
|
|
223
223
|
641EE6A42240C5CA00173FCB /* FBCommandHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9AB7751CAEDF0C008C271F /* FBCommandHandler.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
224
224
|
641EE6A52240C5CA00173FCB /* FBSessionCommands.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9AB7601CAEDF0C008C271F /* FBSessionCommands.h */; };
|
|
225
|
-
641EE6A62240C5CA00173FCB /*
|
|
225
|
+
641EE6A62240C5CA00173FCB /* FBImageProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 63CCF91021ECE4C700E94ABD /* FBImageProcessor.h */; };
|
|
226
226
|
641EE6A72240C5CA00173FCB /* FBSession-Private.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9AB7891CAEDF0C008C271F /* FBSession-Private.h */; };
|
|
227
227
|
641EE6A82240C5CA00173FCB /* NSString+FBXMLSafeString.h in Headers */ = {isa = PBXBuildFile; fileRef = 716E0BCC1E917E810087A825 /* NSString+FBXMLSafeString.h */; };
|
|
228
228
|
641EE6A92240C5CA00173FCB /* FBCommandStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = EE9AB7761CAEDF0C008C271F /* FBCommandStatus.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
@@ -918,11 +918,11 @@
|
|
|
918
918
|
315A15082518D6F400A3A064 /* TouchViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchViewController.h; sourceTree = "<group>"; };
|
|
919
919
|
315A15092518D6F400A3A064 /* TouchViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchViewController.m; sourceTree = "<group>"; };
|
|
920
920
|
44757A831D42CE8300ECF35E /* XCUIDeviceRotationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XCUIDeviceRotationTests.m; sourceTree = "<group>"; };
|
|
921
|
-
631B523421F6174300625362 /*
|
|
921
|
+
631B523421F6174300625362 /* FBImageProcessorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBImageProcessorTests.m; sourceTree = "<group>"; };
|
|
922
922
|
633E904A220DEE7F007CADF9 /* XCUIApplicationProcessDelay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XCUIApplicationProcessDelay.h; sourceTree = "<group>"; };
|
|
923
923
|
6385F4A5220A40760095BBDB /* XCUIApplicationProcessDelay.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XCUIApplicationProcessDelay.m; sourceTree = "<group>"; };
|
|
924
|
-
63CCF91021ECE4C700E94ABD /*
|
|
925
|
-
63CCF91121ECE4C700E94ABD /*
|
|
924
|
+
63CCF91021ECE4C700E94ABD /* FBImageProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FBImageProcessor.h; sourceTree = "<group>"; };
|
|
925
|
+
63CCF91121ECE4C700E94ABD /* FBImageProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FBImageProcessor.m; sourceTree = "<group>"; };
|
|
926
926
|
641EE2DA2240BBE300173FCB /* WebDriverAgentRunner_tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebDriverAgentRunner_tvOS.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
927
927
|
641EE6F82240C5CA00173FCB /* WebDriverAgentLib_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WebDriverAgentLib_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
928
928
|
641EE7042240CDCF00173FCB /* XCUIElement+FBTVFocuse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XCUIElement+FBTVFocuse.h"; sourceTree = "<group>"; };
|
|
@@ -1890,8 +1890,8 @@
|
|
|
1890
1890
|
EE3A18611CDE618F00DE4205 /* FBErrorBuilder.m */,
|
|
1891
1891
|
EE6A89381D0B38640083E92B /* FBFailureProofTestCase.h */,
|
|
1892
1892
|
EE6A89391D0B38640083E92B /* FBFailureProofTestCase.m */,
|
|
1893
|
-
63CCF91021ECE4C700E94ABD /*
|
|
1894
|
-
63CCF91121ECE4C700E94ABD /*
|
|
1893
|
+
63CCF91021ECE4C700E94ABD /* FBImageProcessor.h */,
|
|
1894
|
+
63CCF91121ECE4C700E94ABD /* FBImageProcessor.m */,
|
|
1895
1895
|
7150348521A6DAD600A0F4BA /* FBImageUtils.h */,
|
|
1896
1896
|
7150348621A6DAD600A0F4BA /* FBImageUtils.m */,
|
|
1897
1897
|
EE9B76A31CF7A43900275851 /* FBLogger.h */,
|
|
@@ -2004,7 +2004,7 @@
|
|
|
2004
2004
|
71E504941DF59BAD0020C32A /* XCUIElementAttributesTests.m */,
|
|
2005
2005
|
EEBBD48D1D4785FC00656A81 /* XCUIElementFBFindTests.m */,
|
|
2006
2006
|
EE1E06E11D181CC9007CF043 /* XCUIElementHelperIntegrationTests.m */,
|
|
2007
|
-
631B523421F6174300625362 /*
|
|
2007
|
+
631B523421F6174300625362 /* FBImageProcessorTests.m */,
|
|
2008
2008
|
644D9CCD230E1F1A00C90459 /* FBConfigurationTests.m */,
|
|
2009
2009
|
);
|
|
2010
2010
|
path = IntegrationTests;
|
|
@@ -2381,7 +2381,7 @@
|
|
|
2381
2381
|
641EE6A42240C5CA00173FCB /* FBCommandHandler.h in Headers */,
|
|
2382
2382
|
641EE6A52240C5CA00173FCB /* FBSessionCommands.h in Headers */,
|
|
2383
2383
|
641EE70C2240CE2D00173FCB /* FBTVNavigationTracker.h in Headers */,
|
|
2384
|
-
641EE6A62240C5CA00173FCB /*
|
|
2384
|
+
641EE6A62240C5CA00173FCB /* FBImageProcessor.h in Headers */,
|
|
2385
2385
|
641EE6A72240C5CA00173FCB /* FBSession-Private.h in Headers */,
|
|
2386
2386
|
641EE6A82240C5CA00173FCB /* NSString+FBXMLSafeString.h in Headers */,
|
|
2387
2387
|
64E3502F2AC0B6FE005F3ACB /* NSDictionary+FBUtf8SafeDictionary.h in Headers */,
|
|
@@ -2622,7 +2622,7 @@
|
|
|
2622
2622
|
EE158AC81CBD456F00A3E3F0 /* FBSessionCommands.h in Headers */,
|
|
2623
2623
|
71C8E55125399A6B008572C1 /* XCUIApplication+FBQuiescence.h in Headers */,
|
|
2624
2624
|
641EE70B2240CE2D00173FCB /* FBTVNavigationTracker.h in Headers */,
|
|
2625
|
-
63CCF91221ECE4C700E94ABD /*
|
|
2625
|
+
63CCF91221ECE4C700E94ABD /* FBImageProcessor.h in Headers */,
|
|
2626
2626
|
EE158AE31CBD456F00A3E3F0 /* FBSession-Private.h in Headers */,
|
|
2627
2627
|
716E0BCE1E917E810087A825 /* NSString+FBXMLSafeString.h in Headers */,
|
|
2628
2628
|
EE158ACF1CBD456F00A3E3F0 /* FBCommandStatus.h in Headers */,
|
|
@@ -3122,7 +3122,7 @@
|
|
|
3122
3122
|
641EE6002240C5CA00173FCB /* FBTouchActionCommands.m in Sources */,
|
|
3123
3123
|
719DCF182601EAFB000E765F /* FBNotificationsHelper.m in Sources */,
|
|
3124
3124
|
714EAA102673FDFE005C5B47 /* FBCapabilities.m in Sources */,
|
|
3125
|
-
641EE6012240C5CA00173FCB /*
|
|
3125
|
+
641EE6012240C5CA00173FCB /* FBImageProcessor.m in Sources */,
|
|
3126
3126
|
641EE6022240C5CA00173FCB /* FBTouchIDCommands.m in Sources */,
|
|
3127
3127
|
641EE6032240C5CA00173FCB /* FBDebugCommands.m in Sources */,
|
|
3128
3128
|
641EE6042240C5CA00173FCB /* NSString+FBXMLSafeString.m in Sources */,
|
|
@@ -3236,7 +3236,7 @@
|
|
|
3236
3236
|
EEE3764A1D59FAE900ED88DD /* XCUIElement+FBWebDriverAttributes.m in Sources */,
|
|
3237
3237
|
EE8DDD7E20C5733C004D4925 /* XCUIElement+FBForceTouch.m in Sources */,
|
|
3238
3238
|
71241D7C1FAE3D2500B9559F /* FBTouchActionCommands.m in Sources */,
|
|
3239
|
-
63CCF91321ECE4C700E94ABD /*
|
|
3239
|
+
63CCF91321ECE4C700E94ABD /* FBImageProcessor.m in Sources */,
|
|
3240
3240
|
EE158ACB1CBD456F00A3E3F0 /* FBTouchIDCommands.m in Sources */,
|
|
3241
3241
|
71F5BE51252F14EB00EE9EBA /* FBExceptions.m in Sources */,
|
|
3242
3242
|
EE158ABD1CBD456F00A3E3F0 /* FBDebugCommands.m in Sources */,
|
|
@@ -3307,7 +3307,7 @@
|
|
|
3307
3307
|
files = (
|
|
3308
3308
|
71241D801FAF087500B9559F /* FBW3CMultiTouchActionsIntegrationTests.m in Sources */,
|
|
3309
3309
|
71241D7E1FAF084E00B9559F /* FBW3CTouchActionsIntegrationTests.m in Sources */,
|
|
3310
|
-
63FD950221F9D06100A3E356 /*
|
|
3310
|
+
63FD950221F9D06100A3E356 /* FBImageProcessorTests.m in Sources */,
|
|
3311
3311
|
719CD8FF2126C90200C7D0C2 /* FBAutoAlertsHandlerTests.m in Sources */,
|
|
3312
3312
|
EE2202131ECC612200A29571 /* FBIntegrationTestCase.m in Sources */,
|
|
3313
3313
|
71BD20781F869E0F00B36EC2 /* FBAppiumTouchActionsIntegrationTests.m in Sources */,
|
|
@@ -3324,7 +3324,7 @@
|
|
|
3324
3324
|
buildActionMask = 2147483647;
|
|
3325
3325
|
files = (
|
|
3326
3326
|
EE5095E51EBCC9090028E2FE /* FBTypingTest.m in Sources */,
|
|
3327
|
-
63FD950321F9D06100A3E356 /*
|
|
3327
|
+
63FD950321F9D06100A3E356 /* FBImageProcessorTests.m in Sources */,
|
|
3328
3328
|
EE5095EB1EBCC9090028E2FE /* XCElementSnapshotHitPointTests.m in Sources */,
|
|
3329
3329
|
EE5095EC1EBCC9090028E2FE /* XCUIApplicationHelperTests.m in Sources */,
|
|
3330
3330
|
7136C0F9243A182400921C76 /* FBW3CTypeActionsTests.m in Sources */,
|
|
@@ -3397,7 +3397,7 @@
|
|
|
3397
3397
|
7119E1EC1E891F8600D0B125 /* FBPickerWheelSelectTests.m in Sources */,
|
|
3398
3398
|
71ACF5B8242F2FDC00F0AAD4 /* FBSafariAlertTests.m in Sources */,
|
|
3399
3399
|
EE1E06DA1D1808C2007CF043 /* FBIntegrationTestCase.m in Sources */,
|
|
3400
|
-
63FD950421F9D06200A3E356 /*
|
|
3400
|
+
63FD950421F9D06200A3E356 /* FBImageProcessorTests.m in Sources */,
|
|
3401
3401
|
EE05BAFA1D13003C00A3EB00 /* FBKeyboardTests.m in Sources */,
|
|
3402
3402
|
EE55B3271D1D54CF003AAAEC /* FBScrollingTests.m in Sources */,
|
|
3403
3403
|
EE6A89371D0B35920083E92B /* FBFailureProofTestCaseTests.m in Sources */,
|
|
@@ -369,12 +369,19 @@ NSDictionary<NSNumber *, NSString *> *auditTypeValuesToNames(void) {
|
|
|
369
369
|
if (nil != auditTypeValue) {
|
|
370
370
|
auditType = valuesToNamesMap[auditTypeValue] ?: [auditTypeValue stringValue];
|
|
371
371
|
}
|
|
372
|
+
|
|
373
|
+
id extractedElement = extractIssueProperty(issue, @"element");
|
|
374
|
+
|
|
375
|
+
id<FBXCElementSnapshot> elementSnapshot = [extractedElement fb_takeSnapshot];
|
|
376
|
+
NSDictionary *elementAttributes = elementSnapshot ? [self.class dictionaryForElement:elementSnapshot recursive:NO] : @{};
|
|
377
|
+
|
|
372
378
|
[resultArray addObject:@{
|
|
373
379
|
@"detailedDescription": extractIssueProperty(issue, @"detailedDescription") ?: @"",
|
|
374
380
|
@"compactDescription": extractIssueProperty(issue, @"compactDescription") ?: @"",
|
|
375
381
|
@"auditType": auditType,
|
|
376
|
-
@"element": [
|
|
377
|
-
@"elementDescription": [
|
|
382
|
+
@"element": [extractedElement description] ?: @"",
|
|
383
|
+
@"elementDescription": [extractedElement debugDescription] ?: @"",
|
|
384
|
+
@"elementAttributes": elementAttributes ?: @{},
|
|
378
385
|
}];
|
|
379
386
|
return YES;
|
|
380
387
|
};
|
|
@@ -280,6 +280,7 @@
|
|
|
280
280
|
FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY: @([FBConfiguration mjpegServerScreenshotQuality]),
|
|
281
281
|
FB_SETTING_MJPEG_SERVER_FRAMERATE: @([FBConfiguration mjpegServerFramerate]),
|
|
282
282
|
FB_SETTING_MJPEG_SCALING_FACTOR: @([FBConfiguration mjpegScalingFactor]),
|
|
283
|
+
FB_SETTING_MJPEG_FIX_ORIENTATION: @([FBConfiguration mjpegShouldFixOrientation]),
|
|
283
284
|
FB_SETTING_SCREENSHOT_QUALITY: @([FBConfiguration screenshotQuality]),
|
|
284
285
|
FB_SETTING_KEYBOARD_AUTOCORRECTION: @([FBConfiguration keyboardAutocorrection]),
|
|
285
286
|
FB_SETTING_KEYBOARD_PREDICTION: @([FBConfiguration keyboardPrediction]),
|
|
@@ -327,6 +328,9 @@
|
|
|
327
328
|
if (nil != [settings objectForKey:FB_SETTING_MJPEG_SCALING_FACTOR]) {
|
|
328
329
|
[FBConfiguration setMjpegScalingFactor:[[settings objectForKey:FB_SETTING_MJPEG_SCALING_FACTOR] unsignedIntegerValue]];
|
|
329
330
|
}
|
|
331
|
+
if (nil != [settings objectForKey:FB_SETTING_MJPEG_FIX_ORIENTATION]) {
|
|
332
|
+
[FBConfiguration setMjpegShouldFixOrientation:[[settings objectForKey:FB_SETTING_MJPEG_FIX_ORIENTATION] boolValue]];
|
|
333
|
+
}
|
|
330
334
|
if (nil != [settings objectForKey:FB_SETTING_KEYBOARD_AUTOCORRECTION]) {
|
|
331
335
|
[FBConfiguration setKeyboardAutocorrection:[[settings objectForKey:FB_SETTING_KEYBOARD_AUTOCORRECTION] boolValue]];
|
|
332
336
|
}
|
|
@@ -84,6 +84,16 @@ extern NSString *const FBSnapshotMaxDepthKey;
|
|
|
84
84
|
+ (NSUInteger)mjpegServerScreenshotQuality;
|
|
85
85
|
+ (void)setMjpegServerScreenshotQuality:(NSUInteger)quality;
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
Whether to apply orientation fixes to the streamed JPEG images.
|
|
89
|
+
This is an expensive operation and it is disabled by default, so screenshots
|
|
90
|
+
are returned in portrait, but their actual orientation value could still be found in the EXIF
|
|
91
|
+
metadata.
|
|
92
|
+
! Enablement of this setting may lead to WDA process termination because of an excessive CPU usage.
|
|
93
|
+
*/
|
|
94
|
+
+ (BOOL)mjpegShouldFixOrientation;
|
|
95
|
+
+ (void)setMjpegShouldFixOrientation:(BOOL)enabled;
|
|
96
|
+
|
|
87
97
|
/**
|
|
88
98
|
The framerate at which the background screenshots broadcaster should broadcast
|
|
89
99
|
screenshots in range 1..60. The default value is 10 (Frames Per Second).
|
|
@@ -93,7 +103,7 @@ extern NSString *const FBSnapshotMaxDepthKey;
|
|
|
93
103
|
+ (void)setMjpegServerFramerate:(NSUInteger)framerate;
|
|
94
104
|
|
|
95
105
|
/**
|
|
96
|
-
The quality of
|
|
106
|
+
The quality of display screenshots. The higher quality you set is the bigger screenshot size is.
|
|
97
107
|
The highest quality value is 0 (lossless PNG) or 3 (lossless HEIC). The lowest quality is 2 (highly compressed JPEG).
|
|
98
108
|
The default quality value is 3 (lossless HEIC).
|
|
99
109
|
See https://developer.apple.com/documentation/xctest/xctimagequality?language=objc
|
|
@@ -112,7 +122,10 @@ extern NSString *const FBSnapshotMaxDepthKey;
|
|
|
112
122
|
+ (NSInteger)mjpegServerPort;
|
|
113
123
|
|
|
114
124
|
/**
|
|
115
|
-
The scaling factor for frames of the mjpeg stream
|
|
125
|
+
The scaling factor for frames of the mjpeg stream. The default (and maximum) value is 100,
|
|
126
|
+
which does not perform any scaling. The minimum value must be greater than zero.
|
|
127
|
+
! Setting this to a value less than 100, especially together with orientation fixing enabled
|
|
128
|
+
! may lead to WDA process termination because of an excessive CPU usage.
|
|
116
129
|
*/
|
|
117
130
|
+ (NSUInteger)mjpegScalingFactor;
|
|
118
131
|
+ (void)setMjpegScalingFactor:(NSUInteger)scalingFactor;
|
|
@@ -33,6 +33,7 @@ static BOOL FBShouldUseTestManagerForVisibilityDetection = NO;
|
|
|
33
33
|
static BOOL FBShouldUseSingletonTestManager = YES;
|
|
34
34
|
|
|
35
35
|
static NSUInteger FBMjpegScalingFactor = 100;
|
|
36
|
+
static BOOL FBMjpegShouldFixOrientation = NO;
|
|
36
37
|
static NSUInteger FBMjpegServerScreenshotQuality = 25;
|
|
37
38
|
static NSUInteger FBMjpegServerFramerate = 10;
|
|
38
39
|
|
|
@@ -143,6 +144,15 @@ static UIInterfaceOrientation FBScreenshotOrientation;
|
|
|
143
144
|
FBMjpegScalingFactor = scalingFactor;
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
+ (BOOL)mjpegShouldFixOrientation
|
|
148
|
+
{
|
|
149
|
+
return FBMjpegShouldFixOrientation;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
+ (void)setMjpegShouldFixOrientation:(BOOL)enabled {
|
|
153
|
+
FBMjpegShouldFixOrientation = enabled;
|
|
154
|
+
}
|
|
155
|
+
|
|
146
156
|
+ (BOOL)verboseLoggingEnabled
|
|
147
157
|
{
|
|
148
158
|
return [NSProcessInfo.processInfo.environment[@"VERBOSE_LOGGING"] boolValue];
|
|
@@ -20,7 +20,7 @@ extern const CGFloat FBMaxScalingFactor;
|
|
|
20
20
|
extern const CGFloat FBMinCompressionQuality;
|
|
21
21
|
extern const CGFloat FBMaxCompressionQuality;
|
|
22
22
|
|
|
23
|
-
@interface
|
|
23
|
+
@interface FBImageProcessor : NSObject
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
Puts the passed image on the queue and dispatches a scaling operation. If there is already a image on the
|
|
@@ -29,34 +29,27 @@ extern const CGFloat FBMaxCompressionQuality;
|
|
|
29
29
|
@param image The image to scale down
|
|
30
30
|
@param completionHandler called after successfully scaling down an image
|
|
31
31
|
@param scalingFactor the scaling factor in range 0.01..1.0. A value of 1.0 won't perform scaling at all
|
|
32
|
-
@param compressionQuality the compression quality in range 0.0..1.0 (0.0 for max. compression and 1.0 for lossless compression)
|
|
33
|
-
Only applicable for UTTypeJPEG
|
|
34
32
|
*/
|
|
35
|
-
- (void)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
completionHandler:(void (^)(NSData *))completionHandler;
|
|
33
|
+
- (void)submitImageData:(NSData *)image
|
|
34
|
+
scalingFactor:(CGFloat)scalingFactor
|
|
35
|
+
completionHandler:(void (^)(NSData *))completionHandler;
|
|
39
36
|
|
|
40
37
|
/**
|
|
41
38
|
Scales and crops the source image
|
|
42
39
|
|
|
43
40
|
@param image The source image data
|
|
44
41
|
@param uti Either UTTypePNG or UTTypeJPEG
|
|
45
|
-
@param rect The cropping rectange for the screenshot. The value is expected to be non-scaled one
|
|
46
|
-
since it happens after scaling/orientation change.
|
|
47
|
-
CGRectNull could be used to take a screenshot of the full screen.
|
|
48
42
|
@param scalingFactor Scaling factor in range 0.01..1.0. A value of 1.0 won't perform scaling at all
|
|
49
43
|
@param compressionQuality the compression quality in range 0.0..1.0 (0.0 for max. compression and 1.0 for lossless compression).
|
|
50
44
|
Only works if UTI is set to kUTTypeJPEG
|
|
51
45
|
@param error The actual error instance if the returned result is nil
|
|
52
46
|
@returns Processed image data compressed according to the given UTI or nil in case of a failure
|
|
53
47
|
*/
|
|
54
|
-
- (nullable NSData *)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
error:(NSError **)error;
|
|
48
|
+
- (nullable NSData *)scaledImageWithData:(NSData *)image
|
|
49
|
+
uti:(UTType *)uti
|
|
50
|
+
scalingFactor:(CGFloat)scalingFactor
|
|
51
|
+
compressionQuality:(CGFloat)compressionQuality
|
|
52
|
+
error:(NSError **)error;
|
|
60
53
|
|
|
61
54
|
@end
|
|
62
55
|
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the BSD-style license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree. An additional grant
|
|
7
|
+
* of patent rights can be found in the PATENTS file in the same directory.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#import "FBImageProcessor.h"
|
|
11
|
+
|
|
12
|
+
#import <ImageIO/ImageIO.h>
|
|
13
|
+
#import <UIKit/UIKit.h>
|
|
14
|
+
@import UniformTypeIdentifiers;
|
|
15
|
+
|
|
16
|
+
#import "FBConfiguration.h"
|
|
17
|
+
#import "FBErrorBuilder.h"
|
|
18
|
+
#import "FBImageUtils.h"
|
|
19
|
+
#import "FBLogger.h"
|
|
20
|
+
|
|
21
|
+
const CGFloat FBMinScalingFactor = 0.01f;
|
|
22
|
+
const CGFloat FBMaxScalingFactor = 1.0f;
|
|
23
|
+
const CGFloat FBMinCompressionQuality = 0.0f;
|
|
24
|
+
const CGFloat FBMaxCompressionQuality = 1.0f;
|
|
25
|
+
|
|
26
|
+
@interface FBImageProcessor ()
|
|
27
|
+
|
|
28
|
+
@property (nonatomic) NSData *nextImage;
|
|
29
|
+
@property (nonatomic, readonly) NSLock *nextImageLock;
|
|
30
|
+
@property (nonatomic, readonly) dispatch_queue_t scalingQueue;
|
|
31
|
+
|
|
32
|
+
@end
|
|
33
|
+
|
|
34
|
+
@implementation FBImageProcessor
|
|
35
|
+
|
|
36
|
+
- (id)init
|
|
37
|
+
{
|
|
38
|
+
self = [super init];
|
|
39
|
+
if (self) {
|
|
40
|
+
_nextImageLock = [[NSLock alloc] init];
|
|
41
|
+
_scalingQueue = dispatch_queue_create("image.scaling.queue", NULL);
|
|
42
|
+
}
|
|
43
|
+
return self;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
- (void)submitImageData:(NSData *)image
|
|
47
|
+
scalingFactor:(CGFloat)scalingFactor
|
|
48
|
+
completionHandler:(void (^)(NSData *))completionHandler
|
|
49
|
+
{
|
|
50
|
+
[self.nextImageLock lock];
|
|
51
|
+
if (self.nextImage != nil) {
|
|
52
|
+
[FBLogger verboseLog:@"Discarding screenshot"];
|
|
53
|
+
}
|
|
54
|
+
self.nextImage = image;
|
|
55
|
+
[self.nextImageLock unlock];
|
|
56
|
+
|
|
57
|
+
#pragma clang diagnostic push
|
|
58
|
+
#pragma clang diagnostic ignored "-Wcompletion-handler"
|
|
59
|
+
dispatch_async(self.scalingQueue, ^{
|
|
60
|
+
[self.nextImageLock lock];
|
|
61
|
+
NSData *nextImageData = self.nextImage;
|
|
62
|
+
self.nextImage = nil;
|
|
63
|
+
[self.nextImageLock unlock];
|
|
64
|
+
if (nextImageData == nil) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// We do not want this value to be too high because then we get images larger in size than original ones
|
|
69
|
+
// Although, we also don't want to lose too much of the quality on recompression
|
|
70
|
+
CGFloat recompressionQuality = MAX(0.9,
|
|
71
|
+
MIN(FBMaxCompressionQuality, FBConfiguration.mjpegServerScreenshotQuality / 100.0));
|
|
72
|
+
NSData *thumbnailData = [self.class fixedImageDataWithImageData:nextImageData
|
|
73
|
+
scalingFactor:scalingFactor
|
|
74
|
+
uti:UTTypeJPEG
|
|
75
|
+
compressionQuality:recompressionQuality
|
|
76
|
+
// iOS always returns screnshots in portrait orientation, but puts the real value into the metadata
|
|
77
|
+
// Use it with care. See https://github.com/appium/WebDriverAgent/pull/812
|
|
78
|
+
fixOrientation:FBConfiguration.mjpegShouldFixOrientation
|
|
79
|
+
desiredOrientation:nil];
|
|
80
|
+
completionHandler(thumbnailData ?: nextImageData);
|
|
81
|
+
});
|
|
82
|
+
#pragma clang diagnostic pop
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
+ (nullable NSData *)fixedImageDataWithImageData:(NSData *)imageData
|
|
86
|
+
scalingFactor:(CGFloat)scalingFactor
|
|
87
|
+
uti:(UTType *)uti
|
|
88
|
+
compressionQuality:(CGFloat)compressionQuality
|
|
89
|
+
fixOrientation:(BOOL)fixOrientation
|
|
90
|
+
desiredOrientation:(nullable NSNumber *)orientation
|
|
91
|
+
{
|
|
92
|
+
scalingFactor = MAX(FBMinScalingFactor, MIN(FBMaxScalingFactor, scalingFactor));
|
|
93
|
+
BOOL usesScaling = scalingFactor > 0.0 && scalingFactor < FBMaxScalingFactor;
|
|
94
|
+
@autoreleasepool {
|
|
95
|
+
if (!usesScaling && !fixOrientation) {
|
|
96
|
+
return [uti conformsToType:UTTypePNG] ? FBToPngData(imageData) : FBToJpegData(imageData, compressionQuality);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
UIImage *image = [UIImage imageWithData:imageData];
|
|
100
|
+
if (nil == image
|
|
101
|
+
|| ((image.imageOrientation == UIImageOrientationUp || !fixOrientation) && !usesScaling)) {
|
|
102
|
+
return [uti conformsToType:UTTypePNG] ? FBToPngData(imageData) : FBToJpegData(imageData, compressionQuality);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
CGSize scaledSize = CGSizeMake(image.size.width * scalingFactor, image.size.height * scalingFactor);
|
|
106
|
+
if (!fixOrientation && usesScaling) {
|
|
107
|
+
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
108
|
+
__block UIImage *result = nil;
|
|
109
|
+
[image prepareThumbnailOfSize:scaledSize
|
|
110
|
+
completionHandler:^(UIImage * _Nullable thumbnail) {
|
|
111
|
+
result = thumbnail;
|
|
112
|
+
dispatch_semaphore_signal(semaphore);
|
|
113
|
+
}];
|
|
114
|
+
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
|
115
|
+
if (nil == result) {
|
|
116
|
+
return [uti conformsToType:UTTypePNG] ? FBToPngData(imageData) : FBToJpegData(imageData, compressionQuality);
|
|
117
|
+
}
|
|
118
|
+
return [uti conformsToType:UTTypePNG]
|
|
119
|
+
? UIImagePNGRepresentation(result)
|
|
120
|
+
: UIImageJPEGRepresentation(result, compressionQuality);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
UIGraphicsImageRendererFormat *format = [[UIGraphicsImageRendererFormat alloc] init];
|
|
124
|
+
format.scale = scalingFactor;
|
|
125
|
+
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:scaledSize
|
|
126
|
+
format:format];
|
|
127
|
+
UIImageOrientation desiredOrientation = orientation == nil
|
|
128
|
+
? image.imageOrientation
|
|
129
|
+
: (UIImageOrientation)orientation.integerValue;
|
|
130
|
+
UIImage *uiImage = [UIImage imageWithCGImage:(CGImageRef)image.CGImage
|
|
131
|
+
scale:image.scale
|
|
132
|
+
orientation:desiredOrientation];
|
|
133
|
+
return [uti conformsToType:UTTypePNG]
|
|
134
|
+
? [renderer PNGDataWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
|
|
135
|
+
[uiImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
|
|
136
|
+
}]
|
|
137
|
+
: [renderer JPEGDataWithCompressionQuality:compressionQuality
|
|
138
|
+
actions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
|
|
139
|
+
[uiImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
|
|
140
|
+
}];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
- (nullable NSData *)scaledImageWithData:(NSData *)imageData
|
|
145
|
+
uti:(UTType *)uti
|
|
146
|
+
scalingFactor:(CGFloat)scalingFactor
|
|
147
|
+
compressionQuality:(CGFloat)compressionQuality
|
|
148
|
+
error:(NSError **)error
|
|
149
|
+
{
|
|
150
|
+
NSNumber *orientation = nil;
|
|
151
|
+
#if !TARGET_OS_TV
|
|
152
|
+
if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationPortrait) {
|
|
153
|
+
orientation = @(UIImageOrientationUp);
|
|
154
|
+
} else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationPortraitUpsideDown) {
|
|
155
|
+
orientation = @(UIImageOrientationDown);
|
|
156
|
+
} else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationLandscapeLeft) {
|
|
157
|
+
orientation = @(UIImageOrientationRight);
|
|
158
|
+
} else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationLandscapeRight) {
|
|
159
|
+
orientation = @(UIImageOrientationLeft);
|
|
160
|
+
}
|
|
161
|
+
#endif
|
|
162
|
+
NSData *resultData = [self.class fixedImageDataWithImageData:imageData
|
|
163
|
+
scalingFactor:scalingFactor
|
|
164
|
+
uti:uti
|
|
165
|
+
compressionQuality:compressionQuality
|
|
166
|
+
fixOrientation:YES
|
|
167
|
+
desiredOrientation:orientation];
|
|
168
|
+
return resultData ?: imageData;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@end
|
|
@@ -17,4 +17,10 @@ BOOL FBIsPngImage(NSData *imageData);
|
|
|
17
17
|
/*! Converts the given image data to a PNG representation if necessary */
|
|
18
18
|
NSData *_Nullable FBToPngData(NSData *imageData);
|
|
19
19
|
|
|
20
|
+
/*! Returns YES if the data contains a JPG image */
|
|
21
|
+
BOOL FBIsJpegImage(NSData *imageData);
|
|
22
|
+
|
|
23
|
+
/*! Converts the given image data to a JPG representation if necessary */
|
|
24
|
+
NSData *_Nullable FBToJpegData(NSData *imageData, CGFloat compressionQuality);
|
|
25
|
+
|
|
20
26
|
NS_ASSUME_NONNULL_END
|
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
#import "FBMacros.h"
|
|
13
13
|
#import "FBConfiguration.h"
|
|
14
14
|
|
|
15
|
+
// https://en.wikipedia.org/wiki/List_of_file_signatures
|
|
15
16
|
static uint8_t PNG_MAGIC[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
|
|
16
17
|
static const NSUInteger PNG_MAGIC_LEN = 8;
|
|
18
|
+
static uint8_t JPG_MAGIC[] = { 0xff, 0xd8, 0xff };
|
|
19
|
+
static const NSUInteger JPG_MAGIC_LEN = 3;
|
|
17
20
|
|
|
18
21
|
BOOL FBIsPngImage(NSData *imageData)
|
|
19
22
|
{
|
|
@@ -45,3 +48,34 @@ NSData *FBToPngData(NSData *imageData) {
|
|
|
45
48
|
UIImage *image = [UIImage imageWithData:imageData];
|
|
46
49
|
return nil == image ? nil : (NSData *)UIImagePNGRepresentation(image);
|
|
47
50
|
}
|
|
51
|
+
|
|
52
|
+
BOOL FBIsJpegImage(NSData *imageData)
|
|
53
|
+
{
|
|
54
|
+
if (nil == imageData || [imageData length] < JPG_MAGIC_LEN) {
|
|
55
|
+
return NO;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static NSData* jpgMagicStartData = nil;
|
|
59
|
+
static dispatch_once_t onceJpgToken;
|
|
60
|
+
dispatch_once(&onceJpgToken, ^{
|
|
61
|
+
jpgMagicStartData = [NSData dataWithBytesNoCopy:(void*)JPG_MAGIC length:JPG_MAGIC_LEN freeWhenDone:NO];
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
#pragma clang diagnostic push
|
|
65
|
+
#pragma clang diagnostic ignored "-Wassign-enum"
|
|
66
|
+
NSRange range = [imageData rangeOfData:jpgMagicStartData options:kNilOptions range:NSMakeRange(0, JPG_MAGIC_LEN)];
|
|
67
|
+
#pragma clang diagnostic pop
|
|
68
|
+
return range.location != NSNotFound;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
NSData *FBToJpegData(NSData *imageData, CGFloat compressionQuality) {
|
|
72
|
+
if (nil == imageData || [imageData length] < JPG_MAGIC_LEN) {
|
|
73
|
+
return nil;
|
|
74
|
+
}
|
|
75
|
+
if (FBIsJpegImage(imageData)) {
|
|
76
|
+
return imageData;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
UIImage *image = [UIImage imageWithData:imageData];
|
|
80
|
+
return nil == image ? nil : (NSData *)UIImageJPEGRepresentation(image, compressionQuality);
|
|
81
|
+
}
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
#import "FBConfiguration.h"
|
|
18
18
|
#import "FBLogger.h"
|
|
19
19
|
#import "FBScreenshot.h"
|
|
20
|
-
#import "
|
|
20
|
+
#import "FBImageProcessor.h"
|
|
21
|
+
#import "FBImageUtils.h"
|
|
21
22
|
#import "XCUIScreen.h"
|
|
22
23
|
|
|
23
24
|
static const NSUInteger MAX_FPS = 60;
|
|
@@ -31,7 +32,7 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
|
|
|
31
32
|
|
|
32
33
|
@property (nonatomic, readonly) dispatch_queue_t backgroundQueue;
|
|
33
34
|
@property (nonatomic, readonly) NSMutableArray<GCDAsyncSocket *> *listeningClients;
|
|
34
|
-
@property (nonatomic, readonly)
|
|
35
|
+
@property (nonatomic, readonly) FBImageProcessor *imageProcessor;
|
|
35
36
|
@property (nonatomic, readonly) long long mainScreenID;
|
|
36
37
|
|
|
37
38
|
@end
|
|
@@ -48,7 +49,7 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
|
|
|
48
49
|
dispatch_async(_backgroundQueue, ^{
|
|
49
50
|
[self streamScreenshot];
|
|
50
51
|
});
|
|
51
|
-
|
|
52
|
+
_imageProcessor = [[FBImageProcessor alloc] init];
|
|
52
53
|
_mainScreenID = [XCUIScreen.mainScreen displayID];
|
|
53
54
|
}
|
|
54
55
|
return self;
|
|
@@ -82,15 +83,11 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
|
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
CGFloat scalingFactor = [FBConfiguration mjpegScalingFactor] / 100.0f;
|
|
86
|
-
BOOL usesScaling = fabs(FBMaxScalingFactor - scalingFactor) > DBL_EPSILON;
|
|
87
|
-
CGFloat compressionQuality = FBConfiguration.mjpegServerScreenshotQuality / 100.0f;
|
|
88
|
-
// If scaling is applied we perform another JPEG compression after scaling
|
|
89
|
-
// To get the desired compressionQuality we need to do a lossless compression here
|
|
90
|
-
CGFloat screenshotCompressionQuality = usesScaling ? FBMaxCompressionQuality : compressionQuality;
|
|
91
86
|
NSError *error;
|
|
87
|
+
CGFloat compressionQuality = MAX(FBMinCompressionQuality,
|
|
88
|
+
MIN(FBMaxCompressionQuality, FBConfiguration.mjpegServerScreenshotQuality / 100.0));
|
|
92
89
|
NSData *screenshotData = [FBScreenshot takeInOriginalResolutionWithScreenID:self.mainScreenID
|
|
93
|
-
compressionQuality:
|
|
90
|
+
compressionQuality:compressionQuality
|
|
94
91
|
uti:UTTypeJPEG
|
|
95
92
|
timeout:FRAME_TIMEOUT
|
|
96
93
|
error:&error];
|
|
@@ -100,22 +97,18 @@ static const char *QUEUE_NAME = "JPEG Screenshots Provider Queue";
|
|
|
100
97
|
return;
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}];
|
|
110
|
-
} else {
|
|
111
|
-
[self sendScreenshot:screenshotData];
|
|
112
|
-
}
|
|
100
|
+
CGFloat scalingFactor = FBConfiguration.mjpegScalingFactor / 100.0;
|
|
101
|
+
[self.imageProcessor submitImageData:screenshotData
|
|
102
|
+
scalingFactor:scalingFactor
|
|
103
|
+
completionHandler:^(NSData * _Nonnull scaled) {
|
|
104
|
+
[self sendScreenshot:scaled];
|
|
105
|
+
}];
|
|
113
106
|
|
|
114
107
|
[self scheduleNextScreenshotWithInterval:timerInterval timeStarted:timeStarted];
|
|
115
108
|
}
|
|
116
109
|
|
|
117
110
|
- (void)sendScreenshot:(NSData *)screenshotData {
|
|
118
|
-
NSString *chunkHeader = [NSString stringWithFormat:@"--BoundaryString\r\nContent-type: image/
|
|
111
|
+
NSString *chunkHeader = [NSString stringWithFormat:@"--BoundaryString\r\nContent-type: image/jpeg\r\nContent-Length: %@\r\n\r\n", @(screenshotData.length)];
|
|
119
112
|
NSMutableData *chunk = [[chunkHeader dataUsingEncoding:NSUTF8StringEncoding] mutableCopy];
|
|
120
113
|
[chunk appendData:screenshotData];
|
|
121
114
|
[chunk appendData:(id)[@"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
|
@@ -24,19 +24,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
24
24
|
+ (nullable NSData *)takeInOriginalResolutionWithQuality:(NSUInteger)quality
|
|
25
25
|
error:(NSError **)error;
|
|
26
26
|
|
|
27
|
-
/**
|
|
28
|
-
Retrieves non-scaled screenshot of the particular screen rectangle
|
|
29
|
-
|
|
30
|
-
@param quality The number in range 0-3, where 0 is PNG (lossless), 3 is HEIC (lossless), 1- low quality JPEG and 2 - high quality JPEG
|
|
31
|
-
@param rect The bounding rectange for the screenshot. The value is expected be non-scaled one.
|
|
32
|
-
CGRectNull could be used to take a screenshot of the full screen.
|
|
33
|
-
@param error If there is an error, upon return contains an NSError object that describes the problem.
|
|
34
|
-
@return Device screenshot as PNG-encoded data or nil in case of failure
|
|
35
|
-
*/
|
|
36
|
-
+ (nullable NSData *)takeInOriginalResolutionWithQuality:(NSUInteger)quality
|
|
37
|
-
rect:(CGRect)rect
|
|
38
|
-
error:(NSError **)error;
|
|
39
|
-
|
|
40
27
|
/**
|
|
41
28
|
Retrieves non-scaled screenshot of the whole screen
|
|
42
29
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
#import "FBConfiguration.h"
|
|
15
15
|
#import "FBErrorBuilder.h"
|
|
16
|
-
#import "
|
|
16
|
+
#import "FBImageProcessor.h"
|
|
17
17
|
#import "FBLogger.h"
|
|
18
18
|
#import "FBMacros.h"
|
|
19
19
|
#import "FBXCodeCompatibility.h"
|
|
@@ -59,30 +59,19 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
+ (NSData *)takeInOriginalResolutionWithQuality:(NSUInteger)quality
|
|
62
|
-
rect:(CGRect)rect
|
|
63
62
|
error:(NSError **)error
|
|
64
63
|
{
|
|
65
64
|
XCUIScreen *mainScreen = XCUIScreen.mainScreen;
|
|
66
65
|
return [self.class takeWithScreenID:mainScreen.displayID
|
|
67
66
|
scale:SCREENSHOT_SCALE
|
|
68
67
|
compressionQuality:[self.class compressionQualityWithQuality:quality]
|
|
69
|
-
rect:rect
|
|
70
68
|
sourceUTI:[self.class imageUtiWithQuality:quality]
|
|
71
69
|
error:error];
|
|
72
70
|
}
|
|
73
71
|
|
|
74
|
-
+ (NSData *)takeInOriginalResolutionWithQuality:(NSUInteger)quality
|
|
75
|
-
error:(NSError **)error
|
|
76
|
-
{
|
|
77
|
-
return [self.class takeInOriginalResolutionWithQuality:quality
|
|
78
|
-
rect:CGRectNull
|
|
79
|
-
error:error];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
72
|
+ (NSData *)takeWithScreenID:(long long)screenID
|
|
83
73
|
scale:(CGFloat)scale
|
|
84
74
|
compressionQuality:(CGFloat)compressionQuality
|
|
85
|
-
rect:(CGRect)rect
|
|
86
75
|
sourceUTI:(UTType *)uti
|
|
87
76
|
error:(NSError **)error
|
|
88
77
|
{
|
|
@@ -94,9 +83,8 @@ NSString *formatTimeInterval(NSTimeInterval interval) {
|
|
|
94
83
|
if (nil == screenshotData) {
|
|
95
84
|
return nil;
|
|
96
85
|
}
|
|
97
|
-
return [[[
|
|
86
|
+
return [[[FBImageProcessor alloc] init] scaledImageWithData:screenshotData
|
|
98
87
|
uti:UTTypePNG
|
|
99
|
-
rect:rect
|
|
100
88
|
scalingFactor:1.0 / scale
|
|
101
89
|
compressionQuality:FBMaxCompressionQuality
|
|
102
90
|
error:error];
|
|
@@ -17,6 +17,7 @@ extern NSString* const FB_SETTING_USE_COMPACT_RESPONSES;
|
|
|
17
17
|
extern NSString* const FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES;
|
|
18
18
|
extern NSString* const FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY;
|
|
19
19
|
extern NSString* const FB_SETTING_MJPEG_SERVER_FRAMERATE;
|
|
20
|
+
extern NSString* const FB_SETTING_MJPEG_FIX_ORIENTATION;
|
|
20
21
|
extern NSString* const FB_SETTING_MJPEG_SCALING_FACTOR;
|
|
21
22
|
extern NSString* const FB_SETTING_SCREENSHOT_QUALITY;
|
|
22
23
|
extern NSString* const FB_SETTING_KEYBOARD_AUTOCORRECTION;
|
|
@@ -14,6 +14,7 @@ NSString* const FB_SETTING_ELEMENT_RESPONSE_ATTRIBUTES = @"elementResponseAttrib
|
|
|
14
14
|
NSString* const FB_SETTING_MJPEG_SERVER_SCREENSHOT_QUALITY = @"mjpegServerScreenshotQuality";
|
|
15
15
|
NSString* const FB_SETTING_MJPEG_SERVER_FRAMERATE = @"mjpegServerFramerate";
|
|
16
16
|
NSString* const FB_SETTING_MJPEG_SCALING_FACTOR = @"mjpegScalingFactor";
|
|
17
|
+
NSString* const FB_SETTING_MJPEG_FIX_ORIENTATION = @"mjpegFixOrientation";
|
|
17
18
|
NSString* const FB_SETTING_SCREENSHOT_QUALITY = @"screenshotQuality";
|
|
18
19
|
NSString* const FB_SETTING_KEYBOARD_AUTOCORRECTION = @"keyboardAutocorrection";
|
|
19
20
|
NSString* const FB_SETTING_KEYBOARD_PREDICTION = @"keyboardPrediction";
|
package/WebDriverAgentTests/IntegrationTests/{FBImageIOScalerTests.m → FBImageProcessorTests.m}
RENAMED
|
@@ -9,62 +9,62 @@
|
|
|
9
9
|
|
|
10
10
|
#import <XCTest/XCTest.h>
|
|
11
11
|
|
|
12
|
-
#import "
|
|
12
|
+
#import "FBImageProcessor.h"
|
|
13
13
|
#import "FBIntegrationTestCase.h"
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
@interface
|
|
16
|
+
@interface FBImageProcessorTests : FBIntegrationTestCase
|
|
17
17
|
|
|
18
18
|
@property (nonatomic) NSData *originalImage;
|
|
19
19
|
@property (nonatomic) CGSize originalSize;
|
|
20
20
|
|
|
21
21
|
@end
|
|
22
22
|
|
|
23
|
-
@implementation
|
|
23
|
+
@implementation FBImageProcessorTests
|
|
24
24
|
|
|
25
25
|
- (void)setUp {
|
|
26
26
|
XCUIApplication *app = [[XCUIApplication alloc] init];
|
|
27
27
|
[app launch];
|
|
28
28
|
XCUIScreenshot *screenshot = app.screenshot;
|
|
29
29
|
self.originalImage = UIImageJPEGRepresentation(screenshot.image, 1.0);
|
|
30
|
-
self.originalSize = [
|
|
30
|
+
self.originalSize = [FBImageProcessorTests scaledSizeFromImage:screenshot.image];
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
- (void)testScaling {
|
|
34
34
|
CGFloat halfScale = 0.5;
|
|
35
|
-
CGSize expectedHalfScaleSize = [
|
|
35
|
+
CGSize expectedHalfScaleSize = [FBImageProcessorTests sizeFromSize:self.originalSize scalingFactor:0.5];
|
|
36
36
|
[self scaleImageWithFactor:halfScale
|
|
37
37
|
expectedSize:expectedHalfScaleSize];
|
|
38
38
|
|
|
39
|
-
//
|
|
39
|
+
// 0 is the smalles scaling factor we accept
|
|
40
40
|
CGFloat minScale = 0.0;
|
|
41
|
-
CGSize expectedMinScaleSize = [
|
|
41
|
+
CGSize expectedMinScaleSize = [FBImageProcessorTests sizeFromSize:self.originalSize scalingFactor:0.01];
|
|
42
42
|
[self scaleImageWithFactor:minScale
|
|
43
43
|
expectedSize:expectedMinScaleSize];
|
|
44
44
|
|
|
45
45
|
// For scaling factors above 100 we don't perform any scaling and just return the unmodified image
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
[self scaleImageWithFactor:1.0
|
|
47
|
+
expectedSize:self.originalSize];
|
|
48
|
+
[self scaleImageWithFactor:2.0
|
|
48
49
|
expectedSize:self.originalSize];
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
- (void)scaleImageWithFactor:(CGFloat)scalingFactor expectedSize:(CGSize)excpectedSize {
|
|
52
|
-
|
|
53
|
+
FBImageProcessor *scaler = [[FBImageProcessor alloc] init];
|
|
53
54
|
|
|
54
55
|
id expScaled = [self expectationWithDescription:@"Receive scaled image"];
|
|
55
56
|
|
|
56
|
-
[scaler
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}];
|
|
57
|
+
[scaler submitImageData:self.originalImage
|
|
58
|
+
scalingFactor:scalingFactor
|
|
59
|
+
completionHandler:^(NSData *scaled) {
|
|
60
|
+
UIImage *scaledImage = [UIImage imageWithData:scaled];
|
|
61
|
+
CGSize scaledSize = [FBImageProcessorTests scaledSizeFromImage:scaledImage];
|
|
62
|
+
|
|
63
|
+
XCTAssertEqualWithAccuracy(scaledSize.width, excpectedSize.width, 1.0);
|
|
64
|
+
XCTAssertEqualWithAccuracy(scaledSize.height, excpectedSize.height, 1.0);
|
|
65
|
+
|
|
66
|
+
[expScaled fulfill];
|
|
67
|
+
}];
|
|
68
68
|
|
|
69
69
|
[self waitForExpectations:@[expScaled]
|
|
70
70
|
timeout:0.5];
|
|
@@ -78,14 +78,12 @@
|
|
|
78
78
|
XCTAssertTrue(screenshot.size.width > screenshot.size.height);
|
|
79
79
|
|
|
80
80
|
XCUIScreen *mainScreen = XCUIScreen.mainScreen;
|
|
81
|
-
// TODO: This screenshot rotation was not landscape in an iOS 16 beta simulator.
|
|
82
81
|
UIImage *screenshotExact = ((XCUIScreenshot *)mainScreen.screenshot).image;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
XCTAssertEqualWithAccuracy(
|
|
87
|
-
|
|
88
|
-
FLT_EPSILON);
|
|
82
|
+
CGSize realMainScreenSize = screenshotExact.size.height > screenshot.size.width
|
|
83
|
+
? CGSizeMake(screenshotExact.size.height * mainScreen.scale, screenshotExact.size.width * mainScreen.scale)
|
|
84
|
+
: CGSizeMake(screenshotExact.size.width * mainScreen.scale, screenshotExact.size.height * mainScreen.scale);
|
|
85
|
+
XCTAssertEqualWithAccuracy(realMainScreenSize.height, screenshot.size.height, FLT_EPSILON);
|
|
86
|
+
XCTAssertEqualWithAccuracy(realMainScreenSize.width, screenshot.size.width, FLT_EPSILON);
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
- (void)testWifiAddress
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-webdriveragent",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.15.2",
|
|
4
4
|
"description": "Package bundling WebDriverAgent",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"semantic-release": "^22.0.5",
|
|
91
91
|
"sinon": "^17.0.0",
|
|
92
92
|
"ts-node": "^10.9.1",
|
|
93
|
-
"typescript": "
|
|
93
|
+
"typescript": "~5.2"
|
|
94
94
|
},
|
|
95
95
|
"dependencies": {
|
|
96
96
|
"@appium/base-driver": "^9.0.0",
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) 2015-present, Facebook, Inc.
|
|
3
|
-
* All rights reserved.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the BSD-style license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree. An additional grant
|
|
7
|
-
* of patent rights can be found in the PATENTS file in the same directory.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
#import "FBImageIOScaler.h"
|
|
11
|
-
|
|
12
|
-
#import <ImageIO/ImageIO.h>
|
|
13
|
-
#import <UIKit/UIKit.h>
|
|
14
|
-
@import UniformTypeIdentifiers;
|
|
15
|
-
|
|
16
|
-
#import "FBConfiguration.h"
|
|
17
|
-
#import "FBErrorBuilder.h"
|
|
18
|
-
#import "FBLogger.h"
|
|
19
|
-
|
|
20
|
-
const CGFloat FBMinScalingFactor = 0.01f;
|
|
21
|
-
const CGFloat FBMaxScalingFactor = 1.0f;
|
|
22
|
-
const CGFloat FBMinCompressionQuality = 0.0f;
|
|
23
|
-
const CGFloat FBMaxCompressionQuality = 1.0f;
|
|
24
|
-
|
|
25
|
-
@interface FBImageIOScaler ()
|
|
26
|
-
|
|
27
|
-
@property (nonatomic) NSData *nextImage;
|
|
28
|
-
@property (nonatomic, readonly) NSLock *nextImageLock;
|
|
29
|
-
@property (nonatomic, readonly) dispatch_queue_t scalingQueue;
|
|
30
|
-
|
|
31
|
-
@end
|
|
32
|
-
|
|
33
|
-
@implementation FBImageIOScaler
|
|
34
|
-
|
|
35
|
-
- (id)init
|
|
36
|
-
{
|
|
37
|
-
self = [super init];
|
|
38
|
-
if (self) {
|
|
39
|
-
_nextImageLock = [[NSLock alloc] init];
|
|
40
|
-
_scalingQueue = dispatch_queue_create("image.scaling.queue", NULL);
|
|
41
|
-
}
|
|
42
|
-
return self;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
- (void)submitImage:(NSData *)image
|
|
46
|
-
scalingFactor:(CGFloat)scalingFactor
|
|
47
|
-
compressionQuality:(CGFloat)compressionQuality
|
|
48
|
-
completionHandler:(void (^)(NSData *))completionHandler
|
|
49
|
-
{
|
|
50
|
-
[self.nextImageLock lock];
|
|
51
|
-
if (self.nextImage != nil) {
|
|
52
|
-
[FBLogger verboseLog:@"Discarding screenshot"];
|
|
53
|
-
}
|
|
54
|
-
scalingFactor = MAX(FBMinScalingFactor, MIN(FBMaxScalingFactor, scalingFactor));
|
|
55
|
-
compressionQuality = MAX(FBMinCompressionQuality, MIN(FBMaxCompressionQuality, compressionQuality));
|
|
56
|
-
self.nextImage = image;
|
|
57
|
-
[self.nextImageLock unlock];
|
|
58
|
-
|
|
59
|
-
#pragma clang diagnostic push
|
|
60
|
-
#pragma clang diagnostic ignored "-Wcompletion-handler"
|
|
61
|
-
dispatch_async(self.scalingQueue, ^{
|
|
62
|
-
[self.nextImageLock lock];
|
|
63
|
-
NSData *next = self.nextImage;
|
|
64
|
-
self.nextImage = nil;
|
|
65
|
-
[self.nextImageLock unlock];
|
|
66
|
-
if (next == nil) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
NSError *error;
|
|
71
|
-
NSData *scaled = [self scaledJpegImageWithImage:next
|
|
72
|
-
scalingFactor:scalingFactor
|
|
73
|
-
compressionQuality:compressionQuality
|
|
74
|
-
error:&error];
|
|
75
|
-
if (scaled == nil) {
|
|
76
|
-
[FBLogger logFmt:@"%@", error.description];
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
completionHandler(scaled);
|
|
80
|
-
});
|
|
81
|
-
#pragma clang diagnostic pop
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// This method is more optimized for JPEG scaling
|
|
85
|
-
// and should be used in `submitImage` API, while the `scaledImageWithImage`
|
|
86
|
-
// one is more generic
|
|
87
|
-
- (nullable NSData *)scaledJpegImageWithImage:(NSData *)image
|
|
88
|
-
scalingFactor:(CGFloat)scalingFactor
|
|
89
|
-
compressionQuality:(CGFloat)compressionQuality
|
|
90
|
-
error:(NSError **)error
|
|
91
|
-
{
|
|
92
|
-
CGImageSourceRef imageData = CGImageSourceCreateWithData((CFDataRef)image, nil);
|
|
93
|
-
CGSize size = [self.class imageSizeWithImage:imageData];
|
|
94
|
-
CGFloat scaledMaxPixelSize = MAX(size.width, size.height) * scalingFactor;
|
|
95
|
-
CFDictionaryRef params = (__bridge CFDictionaryRef)@{
|
|
96
|
-
(const NSString *)kCGImageSourceCreateThumbnailWithTransform: @(YES),
|
|
97
|
-
(const NSString *)kCGImageSourceCreateThumbnailFromImageIfAbsent: @(YES),
|
|
98
|
-
(const NSString *)kCGImageSourceThumbnailMaxPixelSize: @(scaledMaxPixelSize)
|
|
99
|
-
};
|
|
100
|
-
CGImageRef scaled = CGImageSourceCreateThumbnailAtIndex(imageData, 0, params);
|
|
101
|
-
CFRelease(imageData);
|
|
102
|
-
if (nil == scaled) {
|
|
103
|
-
[[[FBErrorBuilder builder]
|
|
104
|
-
withDescriptionFormat:@"Failed to scale the image"]
|
|
105
|
-
buildError:error];
|
|
106
|
-
return nil;
|
|
107
|
-
}
|
|
108
|
-
NSData *resData = [self jpegDataWithImage:scaled
|
|
109
|
-
compressionQuality:compressionQuality];
|
|
110
|
-
if (nil == resData) {
|
|
111
|
-
[[[FBErrorBuilder builder]
|
|
112
|
-
withDescriptionFormat:@"Failed to compress the image to JPEG format"]
|
|
113
|
-
buildError:error];
|
|
114
|
-
}
|
|
115
|
-
CGImageRelease(scaled);
|
|
116
|
-
return resData;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
- (nullable NSData *)scaledImageWithImage:(NSData *)image
|
|
120
|
-
uti:(UTType *)uti
|
|
121
|
-
rect:(CGRect)rect
|
|
122
|
-
scalingFactor:(CGFloat)scalingFactor
|
|
123
|
-
compressionQuality:(CGFloat)compressionQuality
|
|
124
|
-
error:(NSError **)error
|
|
125
|
-
{
|
|
126
|
-
UIImage *uiImage = [UIImage imageWithData:image];
|
|
127
|
-
CGSize size = uiImage.size;
|
|
128
|
-
CGSize scaledSize = CGSizeMake(size.width * scalingFactor, size.height * scalingFactor);
|
|
129
|
-
UIGraphicsBeginImageContext(scaledSize);
|
|
130
|
-
UIImageOrientation orientation = uiImage.imageOrientation;
|
|
131
|
-
#if !TARGET_OS_TV
|
|
132
|
-
if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationPortrait) {
|
|
133
|
-
orientation = UIImageOrientationUp;
|
|
134
|
-
} else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationPortraitUpsideDown) {
|
|
135
|
-
orientation = UIImageOrientationDown;
|
|
136
|
-
} else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationLandscapeLeft) {
|
|
137
|
-
orientation = UIImageOrientationRight;
|
|
138
|
-
} else if (FBConfiguration.screenshotOrientation == UIInterfaceOrientationLandscapeRight) {
|
|
139
|
-
orientation = UIImageOrientationLeft;
|
|
140
|
-
}
|
|
141
|
-
#endif
|
|
142
|
-
uiImage = [UIImage imageWithCGImage:(CGImageRef)uiImage.CGImage
|
|
143
|
-
scale:uiImage.scale
|
|
144
|
-
orientation:orientation];
|
|
145
|
-
[uiImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
|
|
146
|
-
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
147
|
-
UIGraphicsEndImageContext();
|
|
148
|
-
|
|
149
|
-
if (!CGRectIsNull(rect)) {
|
|
150
|
-
UIGraphicsBeginImageContext(rect.size);
|
|
151
|
-
[resultImage drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
|
|
152
|
-
resultImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
153
|
-
UIGraphicsEndImageContext();
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return [uti conformsToType:UTTypePNG]
|
|
157
|
-
? UIImagePNGRepresentation(resultImage)
|
|
158
|
-
: UIImageJPEGRepresentation(resultImage, compressionQuality);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
- (nullable NSData *)jpegDataWithImage:(CGImageRef)imageRef
|
|
162
|
-
compressionQuality:(CGFloat)compressionQuality
|
|
163
|
-
{
|
|
164
|
-
NSMutableData *newImageData = [NSMutableData data];
|
|
165
|
-
CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData(
|
|
166
|
-
(__bridge CFMutableDataRef) newImageData,
|
|
167
|
-
(__bridge CFStringRef) UTTypeJPEG.identifier,
|
|
168
|
-
1,
|
|
169
|
-
NULL);
|
|
170
|
-
CFDictionaryRef compressionOptions = (__bridge CFDictionaryRef)@{
|
|
171
|
-
(const NSString *)kCGImageDestinationLossyCompressionQuality: @(compressionQuality)
|
|
172
|
-
};
|
|
173
|
-
CGImageDestinationAddImage(imageDestination, imageRef, compressionOptions);
|
|
174
|
-
if(!CGImageDestinationFinalize(imageDestination)) {
|
|
175
|
-
[FBLogger log:@"Failed to write the image"];
|
|
176
|
-
newImageData = nil;
|
|
177
|
-
}
|
|
178
|
-
CFRelease(imageDestination);
|
|
179
|
-
return newImageData;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
+ (CGSize)imageSizeWithImage:(CGImageSourceRef)imageSource
|
|
183
|
-
{
|
|
184
|
-
NSDictionary *options = @{
|
|
185
|
-
(const NSString *)kCGImageSourceShouldCache: @(NO)
|
|
186
|
-
};
|
|
187
|
-
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (CFDictionaryRef)options);
|
|
188
|
-
NSNumber *width = [(__bridge NSDictionary *)properties objectForKey:(const NSString *)kCGImagePropertyPixelWidth];
|
|
189
|
-
NSNumber *height = [(__bridge NSDictionary *)properties objectForKey:(const NSString *)kCGImagePropertyPixelHeight];
|
|
190
|
-
CGSize size = CGSizeMake([width floatValue], [height floatValue]);
|
|
191
|
-
CFRelease(properties);
|
|
192
|
-
return size;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
@end
|