From cc364f277e15a203244a70eb761edf303f671ea6 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 22 Jan 2023 20:39:10 +0000 Subject: [PATCH 01/94] Update due to recent changes in a previous version. --- .../java/net/sourceforge/opencamera/InstrumentedTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java index a7fd9bdd8..1e0ad8496 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java @@ -2058,7 +2058,7 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR49_output.jpg", false, -1, -1); //checkHistogramDetails(hdrHistogramDetails, 0, 75, 255); - checkHistogramDetails(hdrHistogramDetails, 0, 81, 254); + checkHistogramDetails(hdrHistogramDetails, 0, 89, 254); }); } -- GitLab From 3a7446a039f64e2f0c46c96d41e1cad3b79551cc Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 22 Jan 2023 20:58:32 +0000 Subject: [PATCH 02/94] Fix for HDR algorithm with even number of images - shouldn't allow negative response parameters. --- .../sourceforge/opencamera/InstrumentedTest.java | 14 +++++--------- .../net/sourceforge/opencamera/HDRProcessor.java | 5 +++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java index 1e0ad8496..5b7c9a11e 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java @@ -1006,8 +1006,7 @@ public class InstrumentedTest { int [] exp_offsets_y = {0, 0}; TestUtils.checkHDROffsets(activity, exp_offsets_x, exp_offsets_y); - //checkHistogramDetails(hdrHistogramDetails, 13, 72, 250); - checkHistogramDetails(hdrHistogramDetails, 24, 72, 250); + checkHistogramDetails(hdrHistogramDetails, 33, 78, 250); }); } @@ -1086,8 +1085,7 @@ public class InstrumentedTest { int [] exp_offsets_y = {0, 0, 0, 0}; TestUtils.checkHDROffsets(activity, exp_offsets_x, exp_offsets_y); - //checkHistogramDetails(hdrHistogramDetails, 15, 69, 254); - checkHistogramDetails(hdrHistogramDetails, 24, 70, 254); + checkHistogramDetails(hdrHistogramDetails, 31, 75, 254); }); } @@ -1146,8 +1144,7 @@ public class InstrumentedTest { int [] exp_offsets_y = {0, 0, 0, 0, 0, 0}; TestUtils.checkHDROffsets(activity, exp_offsets_x, exp_offsets_y); - //checkHistogramDetails(hdrHistogramDetails, 15, 70, 254); - checkHistogramDetails(hdrHistogramDetails, 25, 71, 254); + checkHistogramDetails(hdrHistogramDetails, 32, 76, 254); }); } @@ -2034,7 +2031,7 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR49_exp2_output.jpg", false, -1, -1); - checkHistogramDetails(hdrHistogramDetails, 0, 92, 250); + checkHistogramDetails(hdrHistogramDetails, 12, 120, 251); }); } @@ -2081,8 +2078,7 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR49_exp4_output.jpg", false, -1, -1); - //checkHistogramDetails(hdrHistogramDetails, 0, 100, 245); - checkHistogramDetails(hdrHistogramDetails, 0, 94, 244); + checkHistogramDetails(hdrHistogramDetails, 19, 109, 244); }); } diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index b483cf780..49d53162b 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -603,6 +603,11 @@ public class HDRProcessor { float this_B = response_functions[i].parameter_B; response_functions[i].parameter_A = this_A / a; response_functions[i].parameter_B = this_B - this_A * b / a; + if( response_functions[i].parameter_B < 1.0e-5f ) { + if( MyDebug.LOG ) + Log.e(TAG, "remapped parameter B too small or negative: " + response_functions[i].parameter_B); + response_functions[i].parameter_B = 1.0e-5f; + } if( MyDebug.LOG ) { Log.d(TAG, "remapped: " + i); Log.d(TAG, " A: " + this_A + " -> " + response_functions[i].parameter_A); -- GitLab From 72486aa87ac2cc81338fe148f76e6fe84bdcfd96 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Mon, 23 Jan 2023 21:45:33 +0000 Subject: [PATCH 03/94] Apply improvement for low light to hdr_n(). --- .../opencamera/InstrumentedTest.java | 30 ++++++++++++------- app/src/main/rs/process_hdr.rs | 16 +++++++++- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java index 5b7c9a11e..9a89625c5 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java @@ -450,7 +450,8 @@ public class InstrumentedTest { TestUtils.checkHDROffsets(activity, exp_offsets_x, exp_offsets_y); //checkHistogramDetails(hdrHistogramDetails, 3, 43, 251); - checkHistogramDetails(hdrHistogramDetails, 6, 42, 251); + //checkHistogramDetails(hdrHistogramDetails, 6, 42, 251); + checkHistogramDetails(hdrHistogramDetails, 6, 49, 252); }); } @@ -1006,7 +1007,8 @@ public class InstrumentedTest { int [] exp_offsets_y = {0, 0}; TestUtils.checkHDROffsets(activity, exp_offsets_x, exp_offsets_y); - checkHistogramDetails(hdrHistogramDetails, 33, 78, 250); + //checkHistogramDetails(hdrHistogramDetails, 33, 78, 250); + checkHistogramDetails(hdrHistogramDetails, 17, 75, 250); }); } @@ -1085,7 +1087,8 @@ public class InstrumentedTest { int [] exp_offsets_y = {0, 0, 0, 0}; TestUtils.checkHDROffsets(activity, exp_offsets_x, exp_offsets_y); - checkHistogramDetails(hdrHistogramDetails, 31, 75, 254); + //checkHistogramDetails(hdrHistogramDetails, 31, 75, 254); + checkHistogramDetails(hdrHistogramDetails, 23, 74, 254); }); } @@ -1115,7 +1118,8 @@ public class InstrumentedTest { //checkHistogramDetails(hdrHistogramDetails, 17, 81, 255); //checkHistogramDetails(hdrHistogramDetails, 28, 82, 255); - checkHistogramDetails(hdrHistogramDetails, 21, 74, 255); + //checkHistogramDetails(hdrHistogramDetails, 21, 74, 255); + checkHistogramDetails(hdrHistogramDetails, 17, 74, 255); }); } @@ -1144,7 +1148,8 @@ public class InstrumentedTest { int [] exp_offsets_y = {0, 0, 0, 0, 0, 0}; TestUtils.checkHDROffsets(activity, exp_offsets_x, exp_offsets_y); - checkHistogramDetails(hdrHistogramDetails, 32, 76, 254); + //checkHistogramDetails(hdrHistogramDetails, 32, 76, 254); + checkHistogramDetails(hdrHistogramDetails, 25, 75, 254); }); } @@ -1934,7 +1939,8 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR47_exp5_output.jpg", false, -1, -1); - checkHistogramDetails(hdrHistogramDetails, 1, 73, 255); + //checkHistogramDetails(hdrHistogramDetails, 1, 73, 255); + checkHistogramDetails(hdrHistogramDetails, 1, 80, 255); }); } @@ -1961,7 +1967,8 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR47_exp7_output.jpg", false, -1, -1); - checkHistogramDetails(hdrHistogramDetails, 1, 73, 255); + //checkHistogramDetails(hdrHistogramDetails, 1, 73, 255); + checkHistogramDetails(hdrHistogramDetails, 1, 80, 255); }); } @@ -2010,7 +2017,8 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR48_exp5_output.jpg", false, -1, -1); - checkHistogramDetails(hdrHistogramDetails, 0, 59, 241); + //checkHistogramDetails(hdrHistogramDetails, 0, 59, 241); + checkHistogramDetails(hdrHistogramDetails, 0, 67, 241); }); } @@ -2031,7 +2039,8 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR49_exp2_output.jpg", false, -1, -1); - checkHistogramDetails(hdrHistogramDetails, 12, 120, 251); + //checkHistogramDetails(hdrHistogramDetails, 12, 120, 251); + checkHistogramDetails(hdrHistogramDetails, 0, 122, 251); }); } @@ -2103,7 +2112,8 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR49_exp5_output.jpg", false, -1, -1); //checkHistogramDetails(hdrHistogramDetails, 0, 72, 244); - checkHistogramDetails(hdrHistogramDetails, 0, 78, 243); + //checkHistogramDetails(hdrHistogramDetails, 0, 78, 243); + checkHistogramDetails(hdrHistogramDetails, 0, 87, 243); }); } diff --git a/app/src/main/rs/process_hdr.rs b/app/src/main/rs/process_hdr.rs index ae3696c43..f1f278779 100644 --- a/app/src/main/rs/process_hdr.rs +++ b/app/src/main/rs/process_hdr.rs @@ -520,7 +520,18 @@ uchar4 __attribute__((kernel)) hdr_n(uchar4 in, uint32_t x, uint32_t y) { float avg = (rgb.r+rgb.g+rgb.b) / 3.0f; float diff = fabs( avg - 127.5f ); float weight = 1.0f; - if( diff > safe_range_c ) { + if( avg <= 127.5f ) { + // see comment for corresponding code in hdr() + const float range_low_c = 32.0f; + const float range_high_c = 48.0f; + if( avg <= range_low_c ) { + weight = 0.0f; + } + else if( avg <= range_high_c ) { + weight = (avg - range_low_c) / (range_high_c - range_low_c); + } + } + else if( diff > safe_range_c ) { // scaling chosen so that 0 and 255 map to a non-zero weight of 0.01 weight = 1.0f - 0.99f * (diff - safe_range_c) / (127.5f - safe_range_c); } @@ -569,6 +580,9 @@ uchar4 __attribute__((kernel)) hdr_n(uchar4 in, uint32_t x, uint32_t y) { // there will be at least one more adjacent image to look at avg = (rgb.r+rgb.g+rgb.b) / 3.0f; diff = fabs( avg - 127.5f ); + + // n.b., we don't have the codepath here for "if( avg <= 127.5f )" - causes problems + // for testHDR_exp5 (black blotches) if( diff > safe_range_c ) { // scaling chosen so that 0 and 255 map to a non-zero weight of 0.01 weight *= 1.0f - 0.99f * (diff - safe_range_c) / (127.5f - safe_range_c); -- GitLab From e273d93c1d386d9d9ac175267f85b61e5a17ac09 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 18 Feb 2023 13:11:19 +0000 Subject: [PATCH 04/94] Update. --- opencamera_source.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opencamera_source.txt b/opencamera_source.txt index 65e866c97..7dd5fe323 100644 --- a/opencamera_source.txt +++ b/opencamera_source.txt @@ -22,7 +22,7 @@ Application: You might prefer to use Open Camera in its entirety, as an Activity If you are using the Open Camera source code, there are some obvious things to change: * Package name * Online help / homepage (see MainActivity.getOnlineHelpUrl()) -* Privacy policy information, including name and email contact (see "preference_privacy_policy_text" in strings.xml) +* Licence and privacy policy information, including name and email contact (see "preference_licence_open_camera_text" and "preference_privacy_policy_text" in strings.xml) Android inspection warnings/errors ================================== @@ -57,4 +57,4 @@ Homepage: http://opencamera.org.uk/ Google Play download: https://play.google.com/store/apps/details?id=net.sourceforge.opencamera -Mark Harman 6 December 2022 +Mark Harman 18 February 2023 -- GitLab From 61defdd9822fb5ac06ec76890b981fc54fdd7c20 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 18 Feb 2023 13:17:24 +0000 Subject: [PATCH 05/94] Update copyright year. --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 932d1e097..d2de3caf8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1051,7 +1051,7 @@ Open Source licences Open Camera - Open Camera is © 2013–2022 Mark Harman, released under the GPL v3 or later. Tap here for full licence text and terms of service. + Open Camera is © 2013–2023 Mark Harman, released under the GPL v3 or later. Tap here for full licence text and terms of service. AndroidX libraries Open Camera uses the AndroidX/Jetpack libraries under the Apache license version 2.0. Tap here for full licence text. -- GitLab From 874c2b443aa159eb03ea707ceacf5ed0d5d3dbfa Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 18 Feb 2023 13:37:24 +0000 Subject: [PATCH 06/94] Update library versions. --- app/build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6408426d8..3204d2fc3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,19 +40,19 @@ android { } dependencies { - androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' // appcompat version must be 1.4.0 or later to satisfy emoji policy! - implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.appcompat:appcompat:1.4.2' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.exifinterface:exifinterface:1.3.3' + implementation 'androidx.exifinterface:exifinterface:1.3.6' - testImplementation 'junit:junit:4.13.1' + testImplementation 'junit:junit:4.13.2' // newer AndroidJUnit4 InstrumentedTest - androidTestImplementation "androidx.test:runner:1.4.0" - androidTestImplementation "androidx.test:rules:1.4.0" - androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0" + androidTestImplementation "androidx.test:runner:1.5.2" + androidTestImplementation "androidx.test:rules:1.5.0" + androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1" } -- GitLab From 208fbd07773aaa175f10760ef9751121c3d7ffdf Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 18 Mar 2023 17:55:58 +0000 Subject: [PATCH 07/94] Fix spacing. --- .../java/net/sourceforge/opencamera/InstrumentedTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java index 9a89625c5..df7efc5b8 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java @@ -6308,6 +6308,7 @@ public class InstrumentedTest { } }); } + /** Tests option to remove device exif info, but keeping datetime tags. */ @Category(PhotoTests.class) -- GitLab From b98cfb6437dedeb8c17c535693c8846efd51e941 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 18 Mar 2023 17:57:11 +0000 Subject: [PATCH 08/94] Refactor test_camera2 to TestUtils. --- .../opencamera/InstrumentedTest.java | 6 ++---- .../net/sourceforge/opencamera/TestUtils.java | 5 ++++- .../opencamera/test/MainActivityTest.java | 4 +--- .../opencamera/test/MainTests.java | 20 ++++++++++--------- .../opencamera/test/PhotoTests.java | 18 +++++++++-------- .../opencamera/test/VideoTests.java | 20 ++++++++++--------- 6 files changed, 39 insertions(+), 34 deletions(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java index df7efc5b8..32fe2587f 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java @@ -53,8 +53,6 @@ interface PanoramaTests {} public class InstrumentedTest { private static final String TAG = "InstrumentedTest"; - public static final boolean test_camera2 = false; - //public static final boolean test_camera2 = true; static final Intent intent; static { @@ -64,7 +62,7 @@ public class InstrumentedTest { intent.putExtra("test_project_junit4", true); // need to run this here, not in before(), so it's run before activity is created (otherwise Camera2 won't be enabled) - TestUtils.initTest(ApplicationProvider.getApplicationContext(), test_camera2); + TestUtils.initTest(ApplicationProvider.getApplicationContext()); } @Rule @@ -100,7 +98,7 @@ public class InstrumentedTest { // state after running tests. mActivityRule.getScenario().onActivity(activity -> { Log.d(TAG, "after: init"); - TestUtils.initTest(activity, test_camera2); + TestUtils.initTest(activity); }); Log.d(TAG, "after done"); diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java index 92c980f52..533d09359 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java @@ -47,6 +47,9 @@ import java.util.Locale; public class TestUtils { private static final String TAG = "TestUtils"; + public static final boolean test_camera2 = false; + //public static final boolean test_camera2 = true; + final private static String images_base_path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath(); final public static String hdr_images_path = images_base_path + "/testOpenCamera/testdata/hdrsamples/"; final public static String avg_images_path = images_base_path + "/testOpenCamera/testdata/avgsamples/"; @@ -59,7 +62,7 @@ public class TestUtils { /** Code to call before running each test. */ - public static void initTest(Context context, boolean test_camera2) { + public static void initTest(Context context) { Log.d(TAG, "initTest: " + test_camera2); // initialise test statics (to avoid the persisting between tests in a test suite run!) MainActivity.test_preview_want_no_limits = false; diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java index e78bc0753..68f7adc1f 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java @@ -68,8 +68,6 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2 Date: Sat, 18 Mar 2023 18:03:03 +0000 Subject: [PATCH 09/94] Fix typo. --- app/src/main/java/net/sourceforge/opencamera/ImageSaver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index 9e100effc..18b03b150 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -3973,7 +3973,7 @@ public class ImageSaver extends Thread { } } - /** This fixes a problem when we save from a bitmap - we need to set extra exiftags. + /** This fixes a problem when we save from a bitmap - we need to set extra exif tags. * Exiftool shows these tags as "Date/Time Original" and "Create Date". * Without these tags, Windows properties for the image doesn't show anything for * Origin/"Date taken". -- GitLab From 121b135937f0720fad8e9ed8cb07025fc47897bb Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 18 Mar 2023 18:13:53 +0000 Subject: [PATCH 10/94] Should no longer need to use setDateTimeExif() for resaving from bitmap. --- .../sourceforge/opencamera/ImageSaver.java | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index 18b03b150..f34591186 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -3477,9 +3477,6 @@ public class ImageSaver extends Thread { } modifyExif(exif_new, request.remove_device_exif, request.type == Request.Type.JPEG, request.using_camera2, request.using_camera_extensions, request.current_date, request.store_location, request.location, request.store_geo_direction, request.geo_direction, request.custom_tag_artist, request.custom_tag_copyright, request.level_angle, request.pitch_angle, request.store_ypr); - if( request.remove_device_exif == Request.RemoveDeviceExif.OFF || request.remove_device_exif == Request.RemoveDeviceExif.KEEP_DATETIME ) { - setDateTimeExif(exif_new); - } removeExifTags(exif_new, request); // must be last, before saving attributes exif_new.saveAttributes(); @@ -3973,32 +3970,6 @@ public class ImageSaver extends Thread { } } - /** This fixes a problem when we save from a bitmap - we need to set extra exif tags. - * Exiftool shows these tags as "Date/Time Original" and "Create Date". - * Without these tags, Windows properties for the image doesn't show anything for - * Origin/"Date taken". - * N.B., this is probably redundant on Android 7+, where we'll have transferred these tags - * across from the original JPEG in setExif(). - */ - private void setDateTimeExif(ExifInterface exif) { - if( MyDebug.LOG ) - Log.d(TAG, "setDateTimeExif"); - String exif_datetime = exif.getAttribute(ExifInterface.TAG_DATETIME); - if( exif_datetime != null ) { - if( MyDebug.LOG ) { - Log.d(TAG, "write datetime tags: " + exif_datetime); - Log.d(TAG, "TAG_DATETIME_ORIGINAL was: " + exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)); - Log.d(TAG, "TAG_DATETIME_DIGITIZED was: " + exif.getAttribute(ExifInterface.TAG_DATETIME_DIGITIZED)); - } - exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, exif_datetime); - exif.setAttribute(ExifInterface.TAG_DATETIME_DIGITIZED, exif_datetime); - if( MyDebug.LOG ) { - Log.d(TAG, "TAG_DATETIME_ORIGINAL is now: " + exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)); - Log.d(TAG, "TAG_DATETIME_DIGITIZED is now: " + exif.getAttribute(ExifInterface.TAG_DATETIME_DIGITIZED)); - } - } - } - /** Adds exif tags for datetime from the supplied date, if not present. Needed for camera vendor * extensions which (at least on Galaxy S10e) don't seem to have these tags set at all! */ -- GitLab From 7b18183d8bab5eadb253aeef03b7637ea49b281e Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 18 Mar 2023 21:14:27 +0000 Subject: [PATCH 11/94] Shouldn't display zebra stripes, focus peaking or histogram when showing last photo. --- _docs/history.html | 5 +++++ .../main/java/net/sourceforge/opencamera/ui/DrawPreview.java | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index 0a9bde2b3..4629b06d1 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -47,6 +47,11 @@

< Main Page.

+Version 1.52 (Working in progress)
+
+FIXED   Don't show zebra stripes, focus peaking or histogram, when displaying resultant photo for
+        "Pause after taking photo" option.
+
 Version 1.51.1 (2023/01/02)
 
 FIXED   Fix crashes for Camera2 API.
diff --git a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java
index c8c7273dc..0376bf818 100644
--- a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java
+++ b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java
@@ -1586,7 +1586,7 @@ public class DrawPreview {
             }
         }
 
-        if( camera_controller != null ) {
+        if( camera_controller != null && !show_last_image ) {
             // draw histogram
             if( preview.isPreviewBitmapEnabled() ) {
                 int [] histogram = preview.getHistogram();
@@ -2877,7 +2877,7 @@ public class DrawPreview {
             p.setAlpha(255);
         }
 
-        if( preview.isPreviewBitmapEnabled() ) {
+        if( preview.isPreviewBitmapEnabled() && !show_last_image ) {
             // draw additional real-time effects
 
             // draw zebra stripes
-- 
GitLab


From 33ce979c8678cbe80fd18614f5b240d70f610e0f Mon Sep 17 00:00:00 2001
From: Mark Harman 
Date: Sat, 18 Mar 2023 22:09:14 +0000
Subject: [PATCH 12/94] Prefer _HDR etc to base images, for most recent media
 image.

---
 _docs/history.html                            |  2 +
 .../sourceforge/opencamera/ImageSaver.java    | 10 ++-
 .../sourceforge/opencamera/StorageUtils.java  | 90 +++++++++++++++++++
 3 files changed, 99 insertions(+), 3 deletions(-)

diff --git a/_docs/history.html b/_docs/history.html
index 4629b06d1..b7204079a 100644
--- a/_docs/history.html
+++ b/_docs/history.html
@@ -51,6 +51,8 @@ Version 1.52 (Working in progress)
 
 FIXED   Don't show zebra stripes, focus peaking or histogram, when displaying resultant photo for
         "Pause after taking photo" option.
+FIXED   Problem where clicking on gallery icon would sometimes go to a "base" image instead of HDR
+        image, when saving HDR photos with base images (for Android 10+).
 
 Version 1.51.1 (2023/01/02)
 
diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java
index f34591186..68a849661 100644
--- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java
+++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java
@@ -63,6 +63,10 @@ import org.xmlpull.v1.XmlSerializer;
 public class ImageSaver extends Thread {
     private static final String TAG = "ImageSaver";
 
+    static final String hdr_suffix = "_HDR";
+    static final String nr_suffix = "_NR";
+    static final String pano_suffix = "_PANO";
+
     private final Paint p = new Paint();
 
     private final MainActivity main_activity;
@@ -1541,7 +1545,7 @@ public class ImageSaver extends Thread {
 
             if( MyDebug.LOG )
                 Log.d(TAG, "save NR image");
-            String suffix = "_NR";
+            String suffix = nr_suffix;
             success = saveSingleImageNow(request, request.jpeg_images.get(0), nr_bitmap, suffix, true, true, true, false);
             if( MyDebug.LOG && !success )
                 Log.e(TAG, "saveSingleImageNow failed for nr image");
@@ -1636,7 +1640,7 @@ public class ImageSaver extends Thread {
             int base_image_id = ((request.jpeg_images.size()-1)/2);
             if( MyDebug.LOG )
                 Log.d(TAG, "base_image_id: " + base_image_id);
-            String suffix = request.jpeg_images.size() == 1 ? "_DRO" : "_HDR";
+            String suffix = request.jpeg_images.size() == 1 ? "_DRO" : hdr_suffix;
             success = saveSingleImageNow(request, request.jpeg_images.get(base_image_id), hdr_bitmap, suffix, true, true, true, false);
             if( MyDebug.LOG && !success )
                 Log.e(TAG, "saveSingleImageNow failed for hdr image");
@@ -1805,7 +1809,7 @@ public class ImageSaver extends Thread {
 
             if( MyDebug.LOG )
                 Log.d(TAG, "save panorama image");
-            String suffix = "_PANO";
+            String suffix = pano_suffix;
             success = saveSingleImageNow(request, request.jpeg_images.get(0), panorama, suffix, true, true, true, true);
             if( MyDebug.LOG && !success )
                 Log.e(TAG, "saveSingleImageNow failed for panorama image");
diff --git a/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java b/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java
index 7855e1ea1..e5d95f084 100644
--- a/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java
+++ b/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java
@@ -938,6 +938,23 @@ public class StorageUtils {
         return filename_without_ext;
     }
 
+    /** If the filename is for a "special" type HDR, NR or PANO, then return the filename with the
+     *  part of the filename e.g. "_HDR" onwards; else return null.
+     *  Received filename should not include an extension.
+     */
+    private static String filenameIsSpecial(String filename_without_ext) {
+        if( filename_without_ext.endsWith(ImageSaver.hdr_suffix) ) {
+            return filename_without_ext.substring(0, filename_without_ext.length()-ImageSaver.hdr_suffix.length());
+        }
+        if( filename_without_ext.endsWith(ImageSaver.nr_suffix) ) {
+            return filename_without_ext.substring(0, filename_without_ext.length()-ImageSaver.nr_suffix.length());
+        }
+        if( filename_without_ext.endsWith(ImageSaver.pano_suffix) ) {
+            return filename_without_ext.substring(0, filename_without_ext.length()-ImageSaver.pano_suffix.length());
+        }
+        return null;
+    }
+
     private enum UriType {
         MEDIASTORE_IMAGES,
         MEDIASTORE_VIDEOS
@@ -1125,6 +1142,79 @@ public class StorageUtils {
                             cursor.moveToPosition(dng_pos);
                         }
                     }
+                    else if( filename != null ) {
+                        // in cases where a HDR/NR/PANO photo was saved with base images, we should prefer the HDR image
+                        String filename_without_ext = filenameWithoutExtension(filename).toUpperCase(Locale.US);
+                        if( MyDebug.LOG )
+                            Log.d(TAG, "filename_without_ext: " + filename_without_ext);
+                        String filename_special_base = filenameIsSpecial(filename_without_ext);
+                        if( MyDebug.LOG )
+                            Log.d(TAG, "filename_special_base: " + filename_special_base);
+                        if( filename_special_base == null ) {
+                            String filename_base = null;
+                            // assume that base saved images are at most _XX
+                            if( filename_without_ext.length() >= 3 && filename_without_ext.charAt(filename_without_ext.length()-2) == '_' ) {
+                                filename_base = filename_without_ext.substring(0, filename_without_ext.length()-2);
+                            }
+                            else if( filename_without_ext.length() >= 4 && filename_without_ext.charAt(filename_without_ext.length()-3) == '_' ) {
+                                filename_base = filename_without_ext.substring(0, filename_without_ext.length()-3);
+                            }
+                            if( MyDebug.LOG )
+                                Log.d(TAG, "filename_base: " + filename_base);
+                            if( filename_base != null ) {
+                                int last_pos = cursor.getPosition();
+                                boolean found_special = false;
+                                int scan_count = 0;
+                                while( cursor.moveToNext() ) {
+                                    String next_filename = cursor.getString(column_name_c);
+                                    if( MyDebug.LOG )
+                                        Log.d(TAG, "next_filename: " + next_filename);
+                                    if( next_filename == null ) {
+                                        if( MyDebug.LOG )
+                                            Log.d(TAG, "done scanning, couldn't find filename");
+                                        break;
+                                    }
+                                    String next_filename_without_ext = filenameWithoutExtension(next_filename).toUpperCase(Locale.US);
+                                    if( MyDebug.LOG )
+                                        Log.d(TAG, "next_filename_without_ext: " + next_filename_without_ext);
+                                    String next_filename_special_base = filenameIsSpecial(next_filename_without_ext);
+                                    if( MyDebug.LOG )
+                                        Log.d(TAG, "next_filename_special_base: " + next_filename_special_base);
+                                    if( next_filename_special_base != null ) {
+                                        // found a special filename - is it the same base?
+                                        if( filename_base.equals(next_filename_special_base) ) {
+                                            // found a match
+                                            if( MyDebug.LOG )
+                                                Log.d(TAG, "found equivalent special");
+                                            found_special = true;
+                                            break;
+                                        }
+                                        else {
+                                            // found special, but doesn't match, so no point scanning further
+                                            if( MyDebug.LOG )
+                                                Log.d(TAG, "found another special");
+                                            break;
+                                        }
+                                    }
+                                    else if( !next_filename_without_ext.startsWith(filename_base) ) {
+                                        if( MyDebug.LOG )
+                                            Log.d(TAG, "no longer matches filename_base");
+                                        break;
+                                    }
+                                    else if( scan_count++ > 10 ) {
+                                        if( MyDebug.LOG )
+                                            Log.d(TAG, "give up scanning");
+                                        break;
+                                    }
+                                }
+                                if( !found_special ) {
+                                    if( MyDebug.LOG )
+                                        Log.d(TAG, "can't find equivalent non-special");
+                                    cursor.moveToPosition(last_pos);
+                                }
+                            }
+                        }
+                    }
                 }
 
                 long id = cursor.getLong(column_id_c);
-- 
GitLab


From 5b00000c0427fc6635cc20ac524f898b8eb32ae2 Mon Sep 17 00:00:00 2001
From: Mark Harman 
Date: Sun, 19 Mar 2023 13:28:17 +0000
Subject: [PATCH 13/94] Update http links to https.

---
 _docs/credits.html    |  2 +-
 _docs/devices.html    |  2 +-
 _docs/help.html       | 10 +++++-----
 _docs/history.html    |  2 +-
 _docs/index.html      |  8 ++++----
 _docs/info.html       |  2 +-
 _docs/privacy_oc.html |  2 +-
 7 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/_docs/credits.html b/_docs/credits.html
index 261f47924..09c6526c6 100644
--- a/_docs/credits.html
+++ b/_docs/credits.html
@@ -87,7 +87,7 @@
 

Open Camera Privacy Policy.

This website uses icons from third party sources, see licences.

-

Open Camera on Sourceforge.

+

Open Camera on Sourceforge.


diff --git a/_docs/devices.html b/_docs/devices.html index bc99d5cfd..c4e34b6f6 100644 --- a/_docs/devices.html +++ b/_docs/devices.html @@ -229,7 +229,7 @@ in general this is likely available for the flagship S devices running Android 1

Open Camera Privacy Policy.

This website uses icons from third party sources, see licences.

-

Open Camera on Sourceforge.

+

Open Camera on Sourceforge.


diff --git a/_docs/help.html b/_docs/help.html index 6677eb0df..237e515c5 100644 --- a/_docs/help.html +++ b/_docs/help.html @@ -160,7 +160,7 @@ a panel with various controls:

    Exposure compensation (slider) - A higher value increases the exposure, so that pictures come out brighter in low light; a lower value makes pictures darker. One unit of EV changes the brightness of the captured image by a factor of two. +1 EV doubles the image brightness, while -1 EV halves the image brightness. Set to 0 for the default exposure. - See Exposure compensation. + See Exposure compensation. (Only available if the camera supports control of the exposure.)
  • @@ -704,11 +704,11 @@ choose the colours of the stripes.

    Show a grid - Whether to display one of a choice of grids on the camera preview. Grids are useful in photography to help compose your image. Options are:

      -
    • 3x3 - helps with applying the rule of thirds.
    • +
    • 3x3 - helps with applying the rule of thirds.
    • Phi 3x3 - 3x3 grid with ratios 1:0.618:1.
    • 4x2
    • Crosshair
    • -
    • Golden - displays a Golden spiral (or technically, +
    • Golden - displays a Golden spiral (or technically, a Fibonacci spiral). You can use this to improve your photography.
    • Golden Triangles
    • Diagonals
    • @@ -1500,7 +1500,7 @@ It is helpful to supply the "About" information - please go to Settings/About, t can paste the information into your web browser, email or whatever.

      For more general questions or things like feature suggestions, please use the -forums. +forums. For some enquiries you may prefer to use email. Please contact me at mark.harman.apps@gmail.com.


      -Open Camera Blog ~ -Discussion Forums ~ +Open Camera Blog ~ +Discussion Forums ~ Code Repository (Git)

      @@ -198,7 +198,7 @@ your wedding etc :)

      -

      Open Camera is released under the GPL v3 or later. The source code is +

      Open Camera is released under the GPL v3 or later. The source code is available from https://sourceforge.net/​projects/opencamera/files/ . @@ -307,7 +307,7 @@ of old versions; but no need to mention CC0 media which doesn't require attribut


      Open Camera Privacy Policy.

      This website uses icons from third party sources, see licences.

      -

      Open Camera on Sourceforge.

      +

      Open Camera on Sourceforge.


      diff --git a/_docs/info.html b/_docs/info.html index 91c2124f9..64fd5214b 100644 --- a/_docs/info.html +++ b/_docs/info.html @@ -76,7 +76,7 @@

      Open Camera Privacy Policy.

      This website uses icons from third party sources, see licences.

      -

      Open Camera on Sourceforge.

      +

      Open Camera on Sourceforge.


      diff --git a/_docs/privacy_oc.html b/_docs/privacy_oc.html index 48728d312..76973cc0b 100644 --- a/_docs/privacy_oc.html +++ b/_docs/privacy_oc.html @@ -120,7 +120,7 @@ that use our services".


      Open Camera Privacy Policy.

      This website uses icons from third party sources, see licences.

      -

      Open Camera on Sourceforge.

      +

      Open Camera on Sourceforge.


      -- GitLab From ef0f00a89f018ed2255cd4d0eaee8c7b3da88e40 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 19 Mar 2023 13:30:58 +0000 Subject: [PATCH 14/94] Remove/update some old info. --- _docs/devices.html | 8 -------- _docs/help.html | 19 +++++++------------ 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/_docs/devices.html b/_docs/devices.html index c4e34b6f6..73f7d679e 100644 --- a/_docs/devices.html +++ b/_docs/devices.html @@ -109,14 +109,6 @@

      I've also had reports of RAW/DNG images being saved with red/blue swapped. See here for details.

      -

      Lenovo Vibe S1

      - -

      A special prize goes to this device - several people have told me they can't even install Open Camera (Google Play - gives error 504) on at least some versions of this device. This appears to be due to a pre-installed app "Stereo Verify" - that clashes with Open Camera's package name (net.sourceforge.opencamera), which prevents installation of Open Camera. - Also see - here for further discussion.

      -

      Nokia

      I've tested Open Camera with the Nokia 8. Everything seems to work as far as I can tell, including Camera2 API with full diff --git a/_docs/help.html b/_docs/help.html index 237e515c5..fad18f4a9 100644 --- a/_docs/help.html +++ b/_docs/help.html @@ -150,8 +150,7 @@ a panel with various controls:

      • ISO (top row) - (Not supported on all devices.) A higher ISO setting means the camera is more sensitive to light, though may also result in more noise. This mimics the film speed on traditional film cameras. Select "AUTO" to switch back to - automatic ISO mode. See - here for more details on ISO. + automatic ISO mode. If Camera2 API is used, then selecting a non-auto ISO will bring up sliders allowing direct control over the ISO and exposure time (in place of the exposure compensation slider). You can also select "M" to switch straight to manual mode, keeping to the current ISO value. @@ -263,9 +262,8 @@ Settings/Timer.
      • interval between each repeated photo can be set under Settings/"Repeat mode interval".
      • Grid - Whether to display one of a choice of grids on the camera preview. Also available under Settings/Camera preview/"Show a grid".
      • -
      • White balance - Choose a method to control how the white balance is set. See -here for an -explanation of white balance. (Only available if the camera supports different white balance +
      • White balance - Choose a method to control how the white balance is set. +(Only available if the camera supports different white balance settings.) If Camera2 API is enabled, then you can also set "manual". In this mode, manual control over the white balance temperature is available from the exposure compensation icon Exposure Compensation icon.
      • @@ -451,8 +449,7 @@ captured.

        Apply a scene mode - Choose a scene mode to apply. (Only available if the camera supports scene modes.)

        -

        Set the white balance - Choose a method to control how the white balance is set. See -here for an explanation of white balance. (Only +

        Set the white balance - Choose a method to control how the white balance is set. (Only available if the camera supports different white balance settings.)

        --> @@ -1250,9 +1246,8 @@ inside /storage/ (press "Parent Folder" until you're in "/storage/") or of the folders in there - note that confusingly it won't be "sdcard", but will be named something else, e.g., "extSdCard".
      • Android 4.4 - Unfortunately it is not possible for 3rd party apps to write to external SD cards. -This is not a bug or missing feature in Open Camera, rather that -Google -have blocked write access to external SD cards in Android 4.4.
      • +This is not a bug or missing feature in Open Camera, rather that access to external SD cards was +blocked in Android 4.4, and new methods to access them did not appear until Android 5.
      • Android 5.0 onwards - The restrictions on SD cards introduced in Android 4.4 still apply, however instead you can enable Settings/More camera controls/"Storage Access Framework", and this should allow you to save to external SD cards. If when choosing a folder, you only see "Recent", you may need to click on the -- GitLab From 1e6dd72f078e1efb75aa789222f4a18eb441e883 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 19 Mar 2023 13:36:40 +0000 Subject: [PATCH 15/94] Make URL smaller show page isn't too wide on mobile devices. --- _docs/privacy_oc.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_docs/privacy_oc.html b/_docs/privacy_oc.html index 76973cc0b..14c3082b3 100644 --- a/_docs/privacy_oc.html +++ b/_docs/privacy_oc.html @@ -81,7 +81,7 @@ of taking photos and recording videos, to fulfil its purpose as a camera. Microp When enabled, audio data is likely to be sent to remote servers by Android to perform speech recognition. This is subject to the Data Processing Addendum for Products where Google is a Data Processor, located at - https://privacy.google.com/businesses/gdprprocessorterms/ , as updated from time to time. + https://privacy.google.com/businesses/gdprprocessorterms/ , as updated from time to time. This option is no longer available in version 1.50 onwards.
      • For versions 1.49.2 or earlier: The "addresses" option for photo stamp or video subtitles used the Android -- GitLab From 78a43be44eb2b3eef2aca1477263dc5fd19bea50 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 30 Mar 2023 00:11:46 +0100 Subject: [PATCH 16/94] Fix warning about infinite loop in ImageSaver thread. --- .../sourceforge/opencamera/ImageSaver.java | 53 +++++++++++++++++-- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index 68a849661..c57bd1e40 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -80,7 +80,7 @@ public class ImageSaver extends Thread { * Also note, main_activity.imageQueueChanged() should be called on UI thread after n_images_to_save increases or * decreases. * Access to n_images_to_save should always be synchronized to this (i.e., the ImageSaver class). - * n_real_images_to_save excludes "Dummy" requests, and should also be synchronized, and modified + * n_real_images_to_save excludes "Dummy" or "on_destroy" requests, and should also be synchronized, and modified * at the same time as n_images_to_save. */ private int n_images_to_save = 0; @@ -107,7 +107,8 @@ public class ImageSaver extends Thread { enum Type { JPEG, // also covers WEBP RAW, - DUMMY + DUMMY, + ON_DESTROY // indicate that application is being destroyed, so should exit thread } final Type type; enum ProcessType { @@ -489,6 +490,38 @@ public class ImageSaver extends Thread { void onDestroy() { if( MyDebug.LOG ) Log.d(TAG, "onDestroy"); + { + // a request so that the imagesaver thread will complete + Request request = new Request(Request.Type.ON_DESTROY, + Request.ProcessType.NORMAL, + false, + 0, + Request.SaveBase.SAVEBASE_NONE, + null, + null, + false, null, + false, false, + Request.ImageFormat.STD, 0, + false, 0.0, null, + false, + false, + null, + HDRProcessor.default_tonemapping_algorithm_c, + null, + 0, + 0, + 1.0f, + null, null, 0, 0, null, null, null, null, + //null, + null, + false, Request.RemoveDeviceExif.OFF, false, null, false, 0.0, + 0.0, false, + null, null, + 1); + if( MyDebug.LOG ) + Log.d(TAG, "add on_destroy request"); + addRequest(request, 1); + } if( panoramaProcessor != null ) { panoramaProcessor.onDestroy(); } @@ -511,6 +544,7 @@ public class ImageSaver extends Thread { if( MyDebug.LOG ) Log.d(TAG, "ImageSaver thread found new request from queue, size is now: " + queue.size()); boolean success; + boolean on_destroy = false; switch (request.type) { case RAW: if (MyDebug.LOG) @@ -527,6 +561,12 @@ public class ImageSaver extends Thread { Log.d(TAG, "request is dummy"); success = true; break; + case ON_DESTROY: + if( MyDebug.LOG ) + Log.d(TAG, "request is on_destroy"); + success = true; + on_destroy = true; + break; default: if (MyDebug.LOG) Log.e(TAG, "request is unknown type!"); @@ -546,7 +586,7 @@ public class ImageSaver extends Thread { } synchronized( this ) { n_images_to_save--; - if( request.type != Request.Type.DUMMY ) + if( request.type != Request.Type.DUMMY && request.type != Request.Type.ON_DESTROY ) n_real_images_to_save--; if( MyDebug.LOG ) Log.d(TAG, "ImageSaver thread processed new request from queue, images to save is now: " + n_images_to_save); @@ -566,6 +606,9 @@ public class ImageSaver extends Thread { } }); } + if( on_destroy ) { + break; + } } catch(InterruptedException e) { e.printStackTrace(); @@ -573,6 +616,8 @@ public class ImageSaver extends Thread { Log.e(TAG, "interrupted while trying to read from ImageSaver queue"); } } + if( MyDebug.LOG ) + Log.d(TAG, "stopping ImageSaver thread..."); } /** Saves a photo. @@ -914,7 +959,7 @@ public class ImageSaver extends Thread { // but we synchronize modification to avoid risk of problems related to compiler optimisation (local caching or reordering) // also see FindBugs warning due to inconsistent synchronisation n_images_to_save++; // increment before adding to the queue, just to make sure the main thread doesn't think we're all done - if( request.type != Request.Type.DUMMY ) + if( request.type != Request.Type.DUMMY && request.type != Request.Type.ON_DESTROY ) n_real_images_to_save++; main_activity.runOnUiThread(new Runnable() { -- GitLab From 589cfd8dcfb87cba63ca55b742e1021998951bd4 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 22 Apr 2023 21:49:25 +0100 Subject: [PATCH 17/94] Update logging. --- .../opencamera/cameracontroller/CameraController2.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java index 1c65ba29b..478942fe0 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java @@ -2082,7 +2082,7 @@ public class CameraController2 extends CameraController { Log.d(TAG, "callback done, about to notify"); open_camera_lock.notifyAll(); if( MyDebug.LOG ) - Log.d(TAG, "callback done, notification done"); + Log.d(TAG, "callback done, notify done"); } } } @@ -2119,7 +2119,7 @@ public class CameraController2 extends CameraController { Log.d(TAG, "callback done, about to notify"); open_camera_lock.notifyAll(); if( MyDebug.LOG ) - Log.d(TAG, "callback done, notification done"); + Log.d(TAG, "callback done, notify done"); } } } @@ -2145,7 +2145,7 @@ public class CameraController2 extends CameraController { Log.d(TAG, "callback done, about to notify"); open_camera_lock.notifyAll(); if( MyDebug.LOG ) - Log.d(TAG, "callback done, notification done"); + Log.d(TAG, "callback done, notify done"); } } } -- GitLab From 2d9fe65a8d16af91294feb87a906cc9b96652aa5 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Mon, 24 Apr 2023 21:42:23 +0100 Subject: [PATCH 18/94] Fix warnings. --- .../main/java/net/sourceforge/opencamera/ImageSaver.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index c57bd1e40..99cc4db79 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -1590,8 +1590,7 @@ public class ImageSaver extends Thread { if( MyDebug.LOG ) Log.d(TAG, "save NR image"); - String suffix = nr_suffix; - success = saveSingleImageNow(request, request.jpeg_images.get(0), nr_bitmap, suffix, true, true, true, false); + success = saveSingleImageNow(request, request.jpeg_images.get(0), nr_bitmap, nr_suffix, true, true, true, false); if( MyDebug.LOG && !success ) Log.e(TAG, "saveSingleImageNow failed for nr image"); nr_bitmap.recycle(); @@ -1854,8 +1853,7 @@ public class ImageSaver extends Thread { if( MyDebug.LOG ) Log.d(TAG, "save panorama image"); - String suffix = pano_suffix; - success = saveSingleImageNow(request, request.jpeg_images.get(0), panorama, suffix, true, true, true, true); + success = saveSingleImageNow(request, request.jpeg_images.get(0), panorama, pano_suffix, true, true, true, true); if( MyDebug.LOG && !success ) Log.e(TAG, "saveSingleImageNow failed for panorama image"); panorama.recycle(); -- GitLab From db1cd0d3b1f35a4829205ca71f55fa82e076b22a Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 25 Apr 2023 22:31:53 +0100 Subject: [PATCH 19/94] Fix tabs to be spaces. --- .../sourceforge/opencamera/ImageSaver.java | 140 +++--- .../opencamera/PanoramaProcessor.java | 418 +++++++++--------- 2 files changed, 279 insertions(+), 279 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index 99cc4db79..04ae4c7cf 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -140,7 +140,7 @@ public class ImageSaver extends Thread { final boolean using_camera2; final boolean using_camera_extensions; /* image_format allows converting the standard JPEG image into another file format. -# */ +# */ enum ImageFormat { STD, // leave unchanged from the standard JPEG format WEBP, @@ -1418,36 +1418,36 @@ public class ImageSaver extends Thread { saveBaseImages(request, "_"); main_activity.savingImage(true); - /*List bitmaps = loadBitmaps(request.jpeg_images, 0); - if (bitmaps == null) { - if (MyDebug.LOG) - Log.e(TAG, "failed to load bitmaps"); - main_activity.savingImage(false); - return false; - }*/ - /*Bitmap nr_bitmap = loadBitmap(request.jpeg_images.get(0), true); - - if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) { - try { - for(int i = 1; i < request.jpeg_images.size(); i++) { - Log.d(TAG, "processAvg for image: " + i); - Bitmap new_bitmap = loadBitmap(request.jpeg_images.get(i), false); - float avg_factor = (float) i; - hdrProcessor.processAvg(nr_bitmap, new_bitmap, avg_factor, true); - // processAvg recycles new_bitmap - } - //hdrProcessor.processAvgMulti(bitmaps, hdr_strength, 4); - //hdrProcessor.avgBrighten(nr_bitmap); - } - catch(HDRProcessorException e) { - e.printStackTrace(); - throw new RuntimeException(); - } - } - else { - Log.e(TAG, "shouldn't have offered NoiseReduction as an option if not on Android 5"); - throw new RuntimeException(); - }*/ + /*List bitmaps = loadBitmaps(request.jpeg_images, 0); + if (bitmaps == null) { + if (MyDebug.LOG) + Log.e(TAG, "failed to load bitmaps"); + main_activity.savingImage(false); + return false; + }*/ + /*Bitmap nr_bitmap = loadBitmap(request.jpeg_images.get(0), true); + + if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) { + try { + for(int i = 1; i < request.jpeg_images.size(); i++) { + Log.d(TAG, "processAvg for image: " + i); + Bitmap new_bitmap = loadBitmap(request.jpeg_images.get(i), false); + float avg_factor = (float) i; + hdrProcessor.processAvg(nr_bitmap, new_bitmap, avg_factor, true); + // processAvg recycles new_bitmap + } + //hdrProcessor.processAvgMulti(bitmaps, hdr_strength, 4); + //hdrProcessor.avgBrighten(nr_bitmap); + } + catch(HDRProcessorException e) { + e.printStackTrace(); + throw new RuntimeException(); + } + } + else { + Log.e(TAG, "shouldn't have offered NoiseReduction as an option if not on Android 5"); + throw new RuntimeException(); + }*/ Bitmap nr_bitmap; if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) { try { @@ -1466,12 +1466,12 @@ public class ImageSaver extends Thread { List bitmaps = null; Bitmap bitmap0, bitmap1; if( use_smp ) { - /*List sub_jpeg_list = new ArrayList<>(); - sub_jpeg_list.add(request.jpeg_images.get(0)); - sub_jpeg_list.add(request.jpeg_images.get(1)); - bitmaps = loadBitmaps(sub_jpeg_list, -1, inSampleSize); - bitmap0 = bitmaps.get(0); - bitmap1 = bitmaps.get(1);*/ + /*List sub_jpeg_list = new ArrayList<>(); + sub_jpeg_list.add(request.jpeg_images.get(0)); + sub_jpeg_list.add(request.jpeg_images.get(1)); + bitmaps = loadBitmaps(sub_jpeg_list, -1, inSampleSize); + bitmap0 = bitmaps.get(0); + bitmap1 = bitmaps.get(1);*/ int n_remaining = request.jpeg_images.size(); int n_load = Math.min(n_smp_images, n_remaining); if( MyDebug.LOG ) { @@ -1700,28 +1700,28 @@ public class ImageSaver extends Thread { // save text file with gyro info if( !request.image_capture_intent && request.save_base == Request.SaveBase.SAVEBASE_ALL_PLUS_DEBUG ) { - /*final StringBuilder gyro_text = new StringBuilder(); - gyro_text.append("Panorama gyro debug info\n"); - gyro_text.append("n images: " + request.gyro_rotation_matrix.size() + ":\n"); + /*final StringBuilder gyro_text = new StringBuilder(); + gyro_text.append("Panorama gyro debug info\n"); + gyro_text.append("n images: " + request.gyro_rotation_matrix.size() + ":\n"); - float [] inVector = new float[3]; - float [] outVector = new float[3]; - for(int i=0;i pyramid = new ArrayList<>(); for(int i=0;i that.distance ) - return 1; - else if( this.distance < that.distance ) - return -1; - else - return 0;*/ + /*if( this.distance > that.distance ) + return 1; + else if( this.distance < that.distance ) + return -1; + else + return 0;*/ return Float.compare(this.distance, that.distance); } @@ -785,21 +785,21 @@ public class PanoramaProcessor { for(int indx=st_indx;indx max_value ) - max_value = bytes[j]; - } - if( MyDebug.LOG ) - Log.d(TAG, "strength max_value: " + max_value); - for(int j=0;j 255 ) - c = 255; - else if( c < 0 ) - c = 0; - pixels[j] = Color.argb(255, c, c, c); - } - Bitmap bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); - File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/corner_strength_bitmap" + debug_index + "_" + i + ".jpg"); - try { - OutputStream outputStream = new FileOutputStream(file); - bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); - outputStream.close(); - MainActivity mActivity = (MainActivity) context; - mActivity.getStorageUtils().broadcastFile(file, true, false, true); - } - catch(IOException e) { - e.printStackTrace(); - } - bitmap.recycle(); - }*/ + /*if( MyDebug.LOG ) { + // debugging + float [] bytes = new float[width*height]; + strength_allocation.copyTo(bytes); + int [] pixels = new int[width*height]; + float max_value = 0.0f; + for(int j=0;j max_value ) + max_value = bytes[j]; + } + if( MyDebug.LOG ) + Log.d(TAG, "strength max_value: " + max_value); + for(int j=0;j 255 ) + c = 255; + else if( c < 0 ) + c = 0; + pixels[j] = Color.argb(255, c, c, c); + } + Bitmap bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); + File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/corner_strength_bitmap" + debug_index + "_" + i + ".jpg"); + try { + OutputStream outputStream = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); + outputStream.close(); + MainActivity mActivity = (MainActivity) context; + mActivity.getStorageUtils().broadcastFile(file, true, false, true); + } + catch(IOException e) { + e.printStackTrace(); + } + bitmap.recycle(); + }*/ ix_allocation.destroy(); //noinspection UnusedAssignment @@ -1057,26 +1057,26 @@ public class PanoramaProcessor { //noinspection UnusedAssignment gs_allocation = null; - /*featureDetectorScript.set_corner_threshold(100000000.0f); - featureDetectorScript.set_bitmap(strength_allocation); - featureDetectorScript.forEach_local_maximum(strength_allocation, local_max_features_allocation); - // collect points - byte [] bytes = new byte[width*height]; - local_max_features_allocation.copyTo(bytes); - // find points - List points = new ArrayList<>(); - for(int y=feature_descriptor_radius;y points = new ArrayList<>(); + for(int y=feature_descriptor_radius;y min_rotation_dist && Math.abs(dy1) > min_rotation_dist ) { - y_scale = dy1 / dy0; - if( y_scale <= max_y_scale && y_scale >= 1.0f/max_y_scale ) { - found_y_scale = true; - } - else { - y_scale = 1.0f; - } - dy0 *= y_scale; - }*/ + /*float y_scale = 1.0f; + boolean found_y_scale = false; + if( estimate_y_scale && Math.abs(dy0) > min_rotation_dist && Math.abs(dy1) > min_rotation_dist ) { + y_scale = dy1 / dy0; + if( y_scale <= max_y_scale && y_scale >= 1.0f/max_y_scale ) { + found_y_scale = true; + } + else { + y_scale = 1.0f; + } + dy0 *= y_scale; + }*/ float angle = (float)(Math.atan2(dy1, dx1) - Math.atan2(dy0, dx0)); if( angle < -Math.PI ) @@ -1578,17 +1578,17 @@ public class PanoramaProcessor { // reject too large angles continue; } - /*if( MyDebug.LOG ) { - Log.d(TAG, "ransac: " + i + " , " + j + ": "); - Log.d(TAG, " match 0: " + points_arrays[0][match.index0].x + " , " + points_arrays[0][match.index0].y); - Log.d(TAG, " match 1: " + points_arrays[1][match.index1].x + " , " + points_arrays[1][match.index1].y); - Log.d(TAG, " match2 0: " + points_arrays[0][match2.index0].x + " , " + points_arrays[0][match2.index0].y); - Log.d(TAG, " match2 1: " + points_arrays[1][match2.index1].x + " , " + points_arrays[1][match2.index1].y); - Log.d(TAG, " y_scale: " + y_scale); - Log.d(TAG, " angle: " + angle); - Log.d(TAG, " mag0: " + Math.sqrt(mag_sq0)); - Log.d(TAG, " mag1: " + Math.sqrt(mag_sq1)); - }*/ + /*if( MyDebug.LOG ) { + Log.d(TAG, "ransac: " + i + " , " + j + ": "); + Log.d(TAG, " match 0: " + points_arrays[0][match.index0].x + " , " + points_arrays[0][match.index0].y); + Log.d(TAG, " match 1: " + points_arrays[1][match.index1].x + " , " + points_arrays[1][match.index1].y); + Log.d(TAG, " match2 0: " + points_arrays[0][match2.index0].x + " , " + points_arrays[0][match2.index0].y); + Log.d(TAG, " match2 1: " + points_arrays[1][match2.index1].x + " , " + points_arrays[1][match2.index1].y); + Log.d(TAG, " y_scale: " + y_scale); + Log.d(TAG, " angle: " + angle); + Log.d(TAG, " mag0: " + Math.sqrt(mag_sq0)); + Log.d(TAG, " mag1: " + Math.sqrt(mag_sq1)); + }*/ float y_scale = 1.0f; boolean found_y_scale = false; @@ -1624,12 +1624,12 @@ public class PanoramaProcessor { float dx = transformed_x0 - x1; float dy = transformed_y0 - y1; - /*if( MyDebug.LOG ) { - if( other_match == match ) - Log.d(TAG, " ransac on match: " + i + " , " + j + " : " + dx + " , " + dy); - else if( other_match == match2 ) - Log.d(TAG, " ransac on match2: " + i + " , " + j + " : " + dx + " , " + dy); - }*/ + /*if( MyDebug.LOG ) { + if( other_match == match ) + Log.d(TAG, " ransac on match: " + i + " , " + j + " : " + dx + " , " + dy); + else if( other_match == match2 ) + Log.d(TAG, " ransac on match2: " + i + " , " + j + " : " + dx + " , " + dy); + }*/ float error2 = dx*dx + dy*dy; if( error2 + 1.0e-5 <= max_inlier_dist2 ) { inliers.add(other_match); @@ -1707,28 +1707,28 @@ public class PanoramaProcessor { float y_scale = 1.0f; if( estimate_rotation && use_rotation ) { - /*if( true ) - throw new RuntimeException(); // test*/ + /*if( true ) + throw new RuntimeException(); // test*/ // first compute an ideal y_scale - /*if( estimate_y_scale && use_y_scale ) { - float y_scale_sum = 0.0f; - int n_y_scale = 0; - for(FeatureMatch match : actual_matches) { - float d0_y = points_arrays[0][match.index0].y - centres[0].y; - float d1_y = points_arrays[1][match.index1].y - centres[1].y; - if( Math.abs(d0_y) > min_rotation_dist && Math.abs(d1_y) > min_rotation_dist ) { - float this_y_scale = d1_y / d0_y; - y_scale_sum += this_y_scale; - n_y_scale++; - if( MyDebug.LOG ) - Log.d(TAG, " match has scale: " + this_y_scale); - } - } - if( n_y_scale > 0 ) { - y_scale = y_scale_sum / n_y_scale; - } - }*/ + /*if( estimate_y_scale && use_y_scale ) { + float y_scale_sum = 0.0f; + int n_y_scale = 0; + for(FeatureMatch match : actual_matches) { + float d0_y = points_arrays[0][match.index0].y - centres[0].y; + float d1_y = points_arrays[1][match.index1].y - centres[1].y; + if( Math.abs(d0_y) > min_rotation_dist && Math.abs(d1_y) > min_rotation_dist ) { + float this_y_scale = d1_y / d0_y; + y_scale_sum += this_y_scale; + n_y_scale++; + if( MyDebug.LOG ) + Log.d(TAG, " match has scale: " + this_y_scale); + } + } + if( n_y_scale > 0 ) { + y_scale = y_scale_sum / n_y_scale; + } + }*/ // compute an ideal rotation for a transformation where we rotate about centres[0], and then translate float angle_sum = 0.0f; @@ -1826,10 +1826,10 @@ public class PanoramaProcessor { Log.d(TAG, " distance: " + match.distance); } } - /*if( Math.abs(rotation) > 30.0f*Math.PI/180.0f ) { - // test - throw new RuntimeException(); - }*/ + /*if( Math.abs(rotation) > 30.0f*Math.PI/180.0f ) { + // test + throw new RuntimeException(); + }*/ if( false && MyDebug.LOG ) { // debug: @@ -1848,21 +1848,21 @@ public class PanoramaProcessor { int off_x = (i==0) ? 0 : width; boolean was_matched; if( i == 0 ) { - /*if( MyDebug.LOG ) - Log.d(TAG, "### has_matched0[" + j + "]: " + has_matched0[j]);*/ + /*if( MyDebug.LOG ) + Log.d(TAG, "### has_matched0[" + j + "]: " + has_matched0[j]);*/ was_matched = has_matched0[j]; } else { - /*if( MyDebug.LOG ) - Log.d(TAG, "### has_matched1[" + j + "]: " + has_matched1[j]);*/ + /*if( MyDebug.LOG ) + Log.d(TAG, "### has_matched1[" + j + "]: " + has_matched1[j]);*/ was_matched = has_matched1[j]; } - /*if( !was_matched ) { - continue; - }*/ - if( i == 0 && rejected0[j] ) + /*if( !was_matched ) { + continue; + }*/ + if( i == 0 && rejected0[j] ) p.setColor(Color.CYAN); - else + else p.setColor(was_matched ? Color.YELLOW : Color.RED); //canvas.drawCircle(points_arrays[i][j].x + off_x, points_arrays[i][j].y, 5.0f, p); canvas.drawRect(points_arrays[i][j].x + off_x - feature_descriptor_radius - 1, points_arrays[i][j].y - feature_descriptor_radius - 1, points_arrays[i][j].x + off_x + feature_descriptor_radius + 1, points_arrays[i][j].y + feature_descriptor_radius + 1, p); @@ -1929,9 +1929,9 @@ public class PanoramaProcessor { for(int j=0;j Date: Wed, 26 Apr 2023 23:16:38 +0100 Subject: [PATCH 20/94] Fix failing tests - should have been updated for code changes in 1.51 release. --- .../java/net/sourceforge/opencamera/InstrumentedTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java index 32fe2587f..53219e3fb 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/InstrumentedTest.java @@ -702,7 +702,7 @@ public class InstrumentedTest { //checkHistogramDetails(hdrHistogramDetails, 0, 48, 255); //checkHistogramDetails(hdrHistogramDetails, 0, 65, 255); - checkHistogramDetails(hdrHistogramDetails, 0, 62, 254); + checkHistogramDetails(hdrHistogramDetails, 0, 72, 255); }); } @@ -1058,7 +1058,7 @@ public class InstrumentedTest { //checkHistogramDetails(hdrHistogramDetails, 17, 81, 255); //checkHistogramDetails(hdrHistogramDetails, 32, 74, 255); - checkHistogramDetails(hdrHistogramDetails, 29, 68, 255); + checkHistogramDetails(hdrHistogramDetails, 23, 71, 255); }); } @@ -2200,7 +2200,7 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR53_output.jpg", false, 103, 1000000000L/5381); //checkHistogramDetails(hdrHistogramDetails, 0, 55, 254); - checkHistogramDetails(hdrHistogramDetails, 0, 64, 255); + checkHistogramDetails(hdrHistogramDetails, 0, 72, 255); }); } @@ -2379,7 +2379,7 @@ public class InstrumentedTest { TestUtils.HistogramDetails hdrHistogramDetails = TestUtils.subTestHDR(activity, inputs, "testHDR61_output.jpg", false, 50, 1000000000L/5025); - checkHistogramDetails(hdrHistogramDetails, 0, 86, 254); + checkHistogramDetails(hdrHistogramDetails, 0, 93, 255); int [] exp_offsets_x = {0, 0, 1}; int [] exp_offsets_y = {0, 0, -2}; -- GitLab From 34d8645b875c2abb113831f492e384ed7a885b15 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 27 Apr 2023 21:50:27 +0100 Subject: [PATCH 21/94] Drop support for notifications for background saving, due to Android 13 permissions faff. --- _docs/history.html | 1 + .../opencamera/test/MainActivityTest.java | 12 +++---- .../sourceforge/opencamera/MainActivity.java | 35 ++++++++++--------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index 334c5e5ea..6760d6477 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -53,6 +53,7 @@ FIXED Don't show zebra stripes, focus peaking or histogram, when displaying re "Pause after taking photo" option. FIXED Problem where clicking on gallery icon would sometimes go to a "base" image instead of HDR image, when saving HDR photos with base images (for Android 10+). +UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. Version 1.51.1 (2023/01/02) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java index 68f7adc1f..b646344c3 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java @@ -2401,8 +2401,8 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2= Build.VERSION_CODES.O ) { + /*if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { assertTrue(mActivity.testHasNotification()); - } + }*/ mActivity.waitUntilImageQueueEmpty(); this.getInstrumentation().waitForIdleSync(); - assertFalse(mActivity.testHasNotification()); + //assertFalse(mActivity.testHasNotification()); int n_new_files = getNFiles() - n_files; Log.d(TAG, "n_new_files: " + n_new_files); diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java index 2942b034f..215f9f553 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java @@ -26,9 +26,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import android.Manifest; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; import android.content.pm.PackageInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -199,9 +196,10 @@ public class MainActivity extends AppCompatActivity { public static boolean test_force_supports_camera2; // okay to be static, as this is set for an entire test suite public volatile String test_save_settings_file; - private boolean has_notification; - private final String CHANNEL_ID = "open_camera_channel"; - private final int image_saving_notification_id = 1; + // update: notifications now removed due to needing permissions on Android 13+ + //private boolean has_notification; + //private final String CHANNEL_ID = "open_camera_channel"; + //private final int image_saving_notification_id = 1; private static final float WATER_DENSITY_FRESHWATER = 1.0f; private static final float WATER_DENSITY_SALTWATER = 1.03f; @@ -652,7 +650,8 @@ public class MainActivity extends AppCompatActivity { }).start(); // create notification channel - only needed on Android 8+ - if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { + // update: notifications now removed due to needing permissions on Android 13+ + /*if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { CharSequence name = "Open Camera Image Saving"; String description = "Notification channel for processing and saving images in the background"; int importance = NotificationManager.IMPORTANCE_LOW; @@ -662,7 +661,7 @@ public class MainActivity extends AppCompatActivity { // or other notification behaviors after this NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); - } + }*/ if( MyDebug.LOG ) Log.d(TAG, "onCreate: total time for Activity startup: " + (System.currentTimeMillis() - debug_time)); @@ -4074,21 +4073,22 @@ public class MainActivity extends AppCompatActivity { Log.d(TAG, "imageQueueChanged"); applicationInterface.getDrawPreview().setImageQueueFull( !applicationInterface.canTakeNewPhoto() ); - if( applicationInterface.getImageSaver().getNImagesToSave() == 0) { + /*if( applicationInterface.getImageSaver().getNImagesToSave() == 0) { cancelImageSavingNotification(); } - else if( has_notification) { + else if( has_notification ) { // call again to update the text of remaining images createImageSavingNotification(); - } + }*/ } /** Creates a notification to indicate still saving images (or updates an existing one). + * Update: notifications now removed due to needing permissions on Android 13+. */ private void createImageSavingNotification() { if( MyDebug.LOG ) Log.d(TAG, "createImageSavingNotification"); - if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { + /*if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { int n_images_to_save = applicationInterface.getImageSaver().getNRealImagesToSave(); Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_stat_notify_take_photo) @@ -4101,19 +4101,20 @@ public class MainActivity extends AppCompatActivity { NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.notify(image_saving_notification_id, builder.build()); has_notification = true; - } + }*/ } /** Cancels the notification for saving images. + * Update: notifications now removed due to needing permissions on Android 13+. */ private void cancelImageSavingNotification() { if( MyDebug.LOG ) Log.d(TAG, "cancelImageSavingNotification"); - if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { + /*if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) { NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.cancel(image_saving_notification_id); has_notification = false; - } + }*/ } public void clickedGallery(View view) { @@ -6063,7 +6064,7 @@ public class MainActivity extends AppCompatActivity { return this.applicationInterface.hasThumbnailAnimation(); } - public boolean testHasNotification() { + /*public boolean testHasNotification() { return has_notification; - } + }*/ } -- GitLab From 012d4af9f0fceb17371f9b941d529e4fa4bc5dc8 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 27 Apr 2023 22:56:48 +0100 Subject: [PATCH 22/94] Make test more tolerant. --- .../androidTest/java/net/sourceforge/opencamera/TestUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java index 533d09359..afdab103c 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java @@ -1212,8 +1212,9 @@ public class TestUtils { Log.d(TAG, "expected name1: " + expected_filename1); assertTrue(expected_filename.equals(saved_image_file.getName()) || expected_filename1.equals(saved_image_file.getName()));*/ // allow for possibility that the time has passed since taking the photo + // but start from -1, as time may have passed in the Test code since saving the file boolean matched = false; - for(int i=0;i<=max_time_s && !matched;i++) { + for(int i=-1;i<=max_time_s && !matched;i++) { Date test_date = new Date(date.getTime() - 1000L *i); String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(test_date); String expected_filename = "IMG_" + timeStamp + suffix + ".jpg"; -- GitLab From 297131c966d0a4044539f0fa789d2cdf3ee920b0 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 27 Apr 2023 22:58:15 +0100 Subject: [PATCH 23/94] Target Android 13; upgrade gradle. --- app/build.gradle | 7 ++++--- app/src/main/AndroidManifest.xml | 1 - .../java/net/sourceforge/opencamera/MainActivity.java | 8 ++++---- .../sourceforge/opencamera/MyApplicationInterface.java | 2 +- .../net/sourceforge/opencamera/preview/Preview.java | 10 +++++----- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3204d2fc3..d8985b3f6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 31 + compileSdkVersion 33 compileOptions.encoding = 'UTF-8' defaultConfig { applicationId "net.sourceforge.opencamera" minSdkVersion 15 - targetSdkVersion 31 + targetSdkVersion 33 //compileSdkVersion 31 // needed to support appcompat:1.4.0 (which we need for emoji policy support, and not yet ready to target SDK 30) renderscriptTargetApi 21 @@ -36,6 +36,7 @@ android { abortOnError false checkReleaseBuilds false } + namespace 'net.sourceforge.opencamera' //useLibrary 'android.test.mock' } @@ -43,7 +44,7 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.5' // appcompat version must be 1.4.0 or later to satisfy emoji policy! - implementation 'androidx.appcompat:appcompat:1.4.2' + implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.legacy:legacy-support-v4:1.0.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a9c8e3dd2..ed54d2639 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ Date: Fri, 28 Apr 2023 21:40:09 +0100 Subject: [PATCH 24/94] Remove unused old code. --- .../sourceforge/opencamera/HDRProcessor.java | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 49d53162b..12683417d 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2384,74 +2384,6 @@ public class HDRProcessor { void adjustHistogram(Allocation allocation_in, Allocation allocation_out, int width, int height, float hdr_alpha, int n_tiles, boolean ce_preserve_blacks, long time_s) { if( MyDebug.LOG ) Log.d(TAG, "adjustHistogram"); - final boolean adjust_histogram = false; - //final boolean adjust_histogram = true; - - if( adjust_histogram ) { - // create histogram - int [] histogram = new int[256]; - if( MyDebug.LOG ) - Log.d(TAG, "time before creating histogram: " + (System.currentTimeMillis() - time_s)); - Allocation histogramAllocation = computeHistogramAllocation(allocation_in, false, false, time_s); - if( MyDebug.LOG ) - Log.d(TAG, "time after creating histogram: " + (System.currentTimeMillis() - time_s)); - histogramAllocation.copyTo(histogram); - - /*if( MyDebug.LOG ) { - // compare/adjust - allocations[0].copyTo(bm); - int [] debug_histogram = new int[256]; - for(int i=0;i<256;i++) { - debug_histogram[i] = 0; - } - int [] debug_buffer = new int[width]; - for(int y=0;y> 16); - float g = (float)((color & 0xFF00) >> 8); - float b = (float)(color & 0xFF); - //float value = 0.299f*r + 0.587f*g + 0.114f*b; // matches ScriptIntrinsicHistogram default behaviour - float value = Math.max(r, g); - value = Math.max(value, b); - int i_value = (int)value; - i_value = Math.min(255, i_value); // just in case - debug_histogram[i_value]++; - } - } - for(int x=0;x<256;x++) { - Log.d(TAG, "histogram[" + x + "] = " + histogram[x] + " debug_histogram: " + debug_histogram[x]); - //histogram[x] = debug_histogram[x]; - } - }*/ - - int [] c_histogram = new int[256]; - c_histogram[0] = histogram[0]; - for(int x=1;x<256;x++) { - c_histogram[x] = c_histogram[x-1] + histogram[x]; - } - /*if( MyDebug.LOG ) { - for(int x=0;x<256;x++) { - Log.d(TAG, "histogram[" + x + "] = " + histogram[x] + " cumulative: " + c_histogram[x]); - } - }*/ - histogramAllocation.copyFrom(c_histogram); - - /*if( histogramAdjustScript == null ) { - histogramAdjustScript = new ScriptC_histogram_adjust(rs); - }*/ - ScriptC_histogram_adjust histogramAdjustScript = new ScriptC_histogram_adjust(rs); - histogramAdjustScript.set_c_histogram(histogramAllocation); - histogramAdjustScript.set_hdr_alpha(hdr_alpha); - - if( MyDebug.LOG ) - Log.d(TAG, "call histogramAdjustScript"); - histogramAdjustScript.forEach_histogram_adjust(allocation_in, allocation_out); - if( MyDebug.LOG ) - Log.d(TAG, "time after histogramAdjustScript: " + (System.currentTimeMillis() - time_s)); - histogramAllocation.destroy(); - } //final boolean adjust_histogram_local = false; final boolean adjust_histogram_local = true; -- GitLab From df803ce84b7230f23e1323f9dbfacf0a5fe799e4 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 28 Apr 2023 22:14:21 +0100 Subject: [PATCH 25/94] Refactor code to new clipHistogram(). --- .../sourceforge/opencamera/HDRProcessor.java | 233 +++++++++--------- 1 file changed, 121 insertions(+), 112 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 12683417d..38a493edd 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2380,6 +2380,126 @@ public class HDRProcessor { return new LuminanceInfo(min_value, 127, hi_value, true); } + /** Clips the histogram, for Contrast Limited AHE algorithm. + * @param histogram Histogram to modify (length 256). + * @param temp_c_histogram Temporary workspace (length 256). + * @param sub_width Width of the region being processed. + * @param sub_height Height of the region being processed. + */ + void clipHistogram(int [] histogram, int [] temp_c_histogram, int sub_width, int sub_height, boolean ce_preserve_blacks) { + int n_pixels = sub_width * sub_height; + int clip_limit = (5 * n_pixels) / 256; + /*if( MyDebug.LOG ) { + Log.d(TAG, "clip_limit: " + clip_limit); + Log.d(TAG, " relative clip limit: " + clip_limit*256.0f/n_pixels); + }*/ + { + // find real clip limit + int bottom = 0, top = clip_limit; + while( top - bottom > 1 ) { + int middle = (top + bottom)/2; + int sum = 0; + for(int x=0;x<256;x++) { + if( histogram[x] > middle ) { + sum += (histogram[x] - clip_limit); + } + } + if( sum > (clip_limit - middle) * 256 ) + top = middle; + else + bottom = middle; + } + clip_limit = (top + bottom)/2; + /*if( MyDebug.LOG ) { + Log.d(TAG, "updated clip_limit: " + clip_limit); + Log.d(TAG, " relative updated clip limit: " + clip_limit*256.0f/n_pixels); + }*/ + } + int n_clipped = 0; + for(int x=0;x<256;x++) { + if( histogram[x] > clip_limit ) { + /*if( MyDebug.LOG ) { + Log.d(TAG, " " + x + " : " + histogram[x] + " : " + (histogram[x]*256.0f/n_pixels)); + }*/ + n_clipped += (histogram[x] - clip_limit); + histogram[x] = clip_limit; + } + } + int n_clipped_per_bucket = n_clipped / 256; + /*if( MyDebug.LOG ) { + Log.d(TAG, "n_clipped: " + n_clipped); + Log.d(TAG, "n_clipped_per_bucket: " + n_clipped_per_bucket); + }*/ + for(int x=0;x<256;x++) { + histogram[x] += n_clipped_per_bucket; + } + + if( ce_preserve_blacks ) { + // This helps tests such as testHDR52, testHDR57, testAvg26, testAvg30 + // The basic idea is that we want to avoid making darker pixels darker (by too + // much). We do this by adjusting the histogram: + // * We can set a minimum value of each histogram value. E.g., if we set all + // pixels up to a certain brightness to a value equal to n_pixels/256, then + // we prevent those pixels from being made darker. In practice, we choose + // a tapered minimum, starting at (n_pixels/256) for black pixels, linearly + // interpolating to no minimum at brightness 128 (dark_threshold_c). + // * For any adjusted value of the histogram, we redistribute, by reducing + // the histogram values of brighter pixels with values larger than (n_pixels/256), + // reducing them to a minimum of (n_pixels/256). + // * Lastly, we only modify a given histogram value if pixels of that brightness + // would be made darker by the CLAHE algorithm. We can do this by looking at + // the cumulative histogram (as computed before modifying any values). + /*if( MyDebug.LOG ) { + for(int x=0;x<256;x++) { + Log.d(TAG, "pre-brighten histogram[" + x + "] = " + histogram[x]); + } + }*/ + + temp_c_histogram[0] = histogram[0]; + for(int x=1;x<256;x++) { + temp_c_histogram[x] = temp_c_histogram[x-1] + histogram[x]; + } + + // avoid making pixels too dark + int equal_limit = n_pixels / 256; + if( MyDebug.LOG ) + Log.d(TAG, "equal_limit: " + equal_limit); + //final int dark_threshold_c = 64; + final int dark_threshold_c = 128; + //final int dark_threshold_c = 256; + for(int x=0;x= c_equal_limit ) { + continue; + } + float alpha = 1.0f - ((float)x)/((float)dark_threshold_c); + //float alpha = 1.0f - ((float)x)/256.0f; + int limit = (int)(alpha * equal_limit); + //int limit = equal_limit; + if( MyDebug.LOG ) + Log.d(TAG, "x: " + x + " ; limit: " + limit); + /*histogram[x] = Math.max(histogram[x], limit); + if( MyDebug.LOG ) + Log.d(TAG, " histogram pulled up to: " + histogram[x]);*/ + if( histogram[x] < limit ) { + // top up by redistributing later values + for(int y=x+1;y<256 && histogram[x] < limit;y++) { + if( histogram[y] > equal_limit ) { + int move = histogram[y] - equal_limit; + move = Math.min(move, limit - histogram[x]); + histogram[x] += move; + histogram[y] -= move; + } + } + if( MyDebug.LOG ) + Log.d(TAG, " histogram pulled up to: " + histogram[x]); + /*if( temp_c_histogram[x] >= c_equal_limit ) + throw new RuntimeException(); // test*/ + } + } + } + } + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) void adjustHistogram(Allocation allocation_in, Allocation allocation_out, int width, int height, float hdr_alpha, int n_tiles, boolean ce_preserve_blacks, long time_s) { if( MyDebug.LOG ) @@ -2474,118 +2594,7 @@ public class HDRProcessor { } }*/ - // clip histogram, for Contrast Limited AHE algorithm - int n_pixels = (stop_x - start_x) * (stop_y - start_y); - int clip_limit = (5 * n_pixels) / 256; - /*if( MyDebug.LOG ) { - Log.d(TAG, "clip_limit: " + clip_limit); - Log.d(TAG, " relative clip limit: " + clip_limit*256.0f/n_pixels); - }*/ - { - // find real clip limit - int bottom = 0, top = clip_limit; - while( top - bottom > 1 ) { - int middle = (top + bottom)/2; - int sum = 0; - for(int x=0;x<256;x++) { - if( histogram[x] > middle ) { - sum += (histogram[x] - clip_limit); - } - } - if( sum > (clip_limit - middle) * 256 ) - top = middle; - else - bottom = middle; - } - clip_limit = (top + bottom)/2; - /*if( MyDebug.LOG ) { - Log.d(TAG, "updated clip_limit: " + clip_limit); - Log.d(TAG, " relative updated clip limit: " + clip_limit*256.0f/n_pixels); - }*/ - } - int n_clipped = 0; - for(int x=0;x<256;x++) { - if( histogram[x] > clip_limit ) { - /*if( MyDebug.LOG ) { - Log.d(TAG, " " + x + " : " + histogram[x] + " : " + (histogram[x]*256.0f/n_pixels)); - }*/ - n_clipped += (histogram[x] - clip_limit); - histogram[x] = clip_limit; - } - } - int n_clipped_per_bucket = n_clipped / 256; - /*if( MyDebug.LOG ) { - Log.d(TAG, "n_clipped: " + n_clipped); - Log.d(TAG, "n_clipped_per_bucket: " + n_clipped_per_bucket); - }*/ - for(int x=0;x<256;x++) { - histogram[x] += n_clipped_per_bucket; - } - - if( ce_preserve_blacks ) { - // This helps tests such as testHDR52, testHDR57, testAvg26, testAvg30 - // The basic idea is that we want to avoid making darker pixels darker (by too - // much). We do this by adjusting the histogram: - // * We can set a minimum value of each histogram value. E.g., if we set all - // pixels up to a certain brightness to a value equal to n_pixels/256, then - // we prevent those pixels from being made darker. In practice, we choose - // a tapered minimum, starting at (n_pixels/256) for black pixels, linearly - // interpolating to no minimum at brightness 128 (dark_threshold_c). - // * For any adjusted value of the histogram, we redistribute, by reducing - // the histogram values of brighter pixels with values larger than (n_pixels/256), - // reducing them to a minimum of (n_pixels/256). - // * Lastly, we only modify a given histogram value if pixels of that brightness - // would be made darker by the CLAHE algorithm. We can do this by looking at - // the cumulative histogram (as computed before modifying any values). - /*if( MyDebug.LOG ) { - for(int x=0;x<256;x++) { - Log.d(TAG, "pre-brighten histogram[" + x + "] = " + histogram[x]); - } - }*/ - - temp_c_histogram[0] = histogram[0]; - for(int x=1;x<256;x++) { - temp_c_histogram[x] = temp_c_histogram[x-1] + histogram[x]; - } - - // avoid making pixels too dark - int equal_limit = n_pixels / 256; - if( MyDebug.LOG ) - Log.d(TAG, "equal_limit: " + equal_limit); - //final int dark_threshold_c = 64; - final int dark_threshold_c = 128; - //final int dark_threshold_c = 256; - for(int x=0;x= c_equal_limit ) { - continue; - } - float alpha = 1.0f - ((float)x)/((float)dark_threshold_c); - //float alpha = 1.0f - ((float)x)/256.0f; - int limit = (int)(alpha * equal_limit); - //int limit = equal_limit; - if( MyDebug.LOG ) - Log.d(TAG, "x: " + x + " ; limit: " + limit); - /*histogram[x] = Math.max(histogram[x], limit); - if( MyDebug.LOG ) - Log.d(TAG, " histogram pulled up to: " + histogram[x]);*/ - if( histogram[x] < limit ) { - // top up by redistributing later values - for(int y=x+1;y<256 && histogram[x] < limit;y++) { - if( histogram[y] > equal_limit ) { - int move = histogram[y] - equal_limit; - move = Math.min(move, limit - histogram[x]); - histogram[x] += move; - histogram[y] -= move; - } - } - if( MyDebug.LOG ) - Log.d(TAG, " histogram pulled up to: " + histogram[x]); - /*if( temp_c_histogram[x] >= c_equal_limit ) - throw new RuntimeException(); // test*/ - } - } - } + clipHistogram(histogram, temp_c_histogram, (stop_x - start_x), (stop_y - start_y), ce_preserve_blacks); // compute cumulative histogram int histogram_offset = 256*(i*n_tiles+j); -- GitLab From ea6093653f54ea775ed31a07768f3455da0798d7 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 28 Apr 2023 23:27:39 +0100 Subject: [PATCH 26/94] Add logging. --- app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 38a493edd..4c5fbaae2 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2965,6 +2965,9 @@ public class HDRProcessor { Log.d(TAG, "median brightness: " + histogramInfo.median_brightness); Log.d(TAG, "mean brightness: " + histogramInfo.mean_brightness); Log.d(TAG, "max brightness: " + max_brightness); + for(int i=0;i<256;i++) { + Log.d(TAG, "histogram[" + i + "]: " + histo[i]); + } } BrightenFactors brighten_factors = computeBrightenFactors(true, iso, exposure_time, brightness, max_brightness); -- GitLab From 811bf8450e0432b4e3ba2e40f953b39a40eaed6b Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 7 May 2023 11:52:31 +0100 Subject: [PATCH 27/94] Update logging. --- .../sourceforge/opencamera/HDRProcessor.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 4c5fbaae2..3a73fa6a5 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1034,7 +1034,7 @@ public class HDRProcessor { output_allocation.copyTo(output_bitmap); } if( MyDebug.LOG ) - Log.d(TAG, "time after copying to bitmap: " + (System.currentTimeMillis() - time_s)); + Log.d(TAG, "### processSingleImage: time after copying to bitmap: " + (System.currentTimeMillis() - time_s)); if( free_output_allocation ) allocation.destroy(); @@ -1042,7 +1042,7 @@ public class HDRProcessor { freeScripts(); if( MyDebug.LOG ) - Log.d(TAG, "time for processSingleImage: " + (System.currentTimeMillis() - time_s)); + Log.d(TAG, "### time for processSingleImage: " + (System.currentTimeMillis() - time_s)); } void brightenImage(Bitmap bitmap, int brightness, int max_brightness, int brightness_target) { @@ -2516,6 +2516,8 @@ public class HDRProcessor { // Pizer, Amburn, Austin, Cromartie, Geselowitz, Greer, ter Haar Romeny, Zimmerman, Zuiderveld (1987). // Also note that if ce_preserve_blacks is true, we apply a modification to this algorithm, see below. + if( MyDebug.LOG ) + Log.d(TAG, "time before creating histograms: " + (System.currentTimeMillis() - time_s)); // create histograms Allocation histogramAllocation = Allocation.createSized(rs, Element.I32(rs), 256); /*if( histogramScript == null ) { @@ -2611,7 +2613,7 @@ public class HDRProcessor { } if( MyDebug.LOG ) - Log.d(TAG, "time after creating histograms: " + (System.currentTimeMillis() - time_s)); + Log.d(TAG, "adjustHistogram: time after creating histograms: " + (System.currentTimeMillis() - time_s)); Allocation c_histogramAllocation = Allocation.createSized(rs, Element.I32(rs), n_tiles*n_tiles*256); c_histogramAllocation.copyFrom(c_histogram); @@ -2699,8 +2701,10 @@ public class HDRProcessor { */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public int [] computeHistogram(Bitmap bitmap, boolean avg) { - if( MyDebug.LOG ) - Log.d(TAG, "computeHistogram"); + if( MyDebug.LOG ) { + Log.d(TAG, "computeHistogram [bitmap]"); + Log.d(TAG, "avg: " + avg); + } long time_s = System.currentTimeMillis(); initRenderscript(); Allocation allocation_in = Allocation.createFromBitmap(rs, bitmap); @@ -2709,6 +2713,10 @@ public class HDRProcessor { int [] histogram = computeHistogram(allocation_in, avg, false); allocation_in.destroy(); freeScripts(); + if( MyDebug.LOG ) { + Log.d(TAG, "image size: " + bitmap.getWidth() + " x " + bitmap.getHeight()); + Log.d(TAG, "### time to compute histogram: " + (System.currentTimeMillis() - time_s)); + } return histogram; } @@ -2721,6 +2729,10 @@ public class HDRProcessor { Allocation histogramAllocation = computeHistogramAllocation(allocation, avg, floating_point, time_s); histogramAllocation.copyTo(histogram); histogramAllocation.destroy(); + if( MyDebug.LOG ) { + Log.d(TAG, "allocation size: " + width + " x " + height); + Log.d(TAG, "### time to compute histogram: " + (System.currentTimeMillis() - time_s)); + } return histogram; } -- GitLab From d68480302d92bd3029114fe71f60d3264b7584d7 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 7 May 2023 11:55:07 +0100 Subject: [PATCH 28/94] Refactor to move renderscript specific code later. --- .../sourceforge/opencamera/HDRProcessor.java | 56 +++---------------- 1 file changed, 8 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 3a73fa6a5..926af7e54 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -3007,11 +3007,6 @@ public class HDRProcessor { } }*/ - /*if( avgBrightenScript == null ) { - avgBrightenScript = new ScriptC_avg_brighten(rs); - }*/ - ScriptC_avg_brighten avgBrightenScript = new ScriptC_avg_brighten(rs); - avgBrightenScript.set_bitmap(input); float black_level = 0.0f; { // quick and dirty dehaze algorithm @@ -3036,56 +3031,21 @@ public class HDRProcessor { Log.d(TAG, "black_level is now: " + black_level); } } - avgBrightenScript.invoke_setBlackLevel(black_level); // use a lower medial filter strength for pixel binned images, so that we don't blur testAvg46 so much (especially sign text) float median_filter_strength = (cached_avg_sample_size >= 2) ? 0.5f : 1.0f; if( MyDebug.LOG ) Log.d(TAG, "median_filter_strength: " + median_filter_strength); - avgBrightenScript.set_median_filter_strength(median_filter_strength); - avgBrightenScript.invoke_setBrightenParameters(gain, gamma, low_x, mid_x, max_brightness); - /*float tonemap_scale_c = 255.0f; - if( MyDebug.LOG ) - Log.d(TAG, "tonemap_scale_c: " + tonemap_scale_c); - avgBrightenScript.set_tonemap_scale(tonemap_scale_c); - - float max_possible_value = gain*max_brightness; - if( MyDebug.LOG ) - Log.d(TAG, "max_possible_value: " + max_possible_value); - if( max_possible_value < 255.0f ) { - max_possible_value = 255.0f; // don't make dark images too bright - if( MyDebug.LOG ) - Log.d(TAG, "clamp max_possible_value to: " + max_possible_value); - } - float linear_scale = (max_possible_value + tonemap_scale_c) / max_possible_value; - if( MyDebug.LOG ) - Log.d(TAG, "linear_scale: " + linear_scale); - avgBrightenScript.set_linear_scale(linear_scale);*/ - - /*{ - max_possible_value = max_brightness; - float tonemap_scale_c = 255.0f; - if( 255.0f / max_possible_value < ((float)brightness_target)/(float)brightness + brightness_target / 255.0f - 1.0f ) { - final float tonemap_denom = ((float)brightness_target)/(float)brightness - (255.0f / max_possible_value); - if( MyDebug.LOG ) - Log.d(TAG, "tonemap_denom: " + tonemap_denom); - if( tonemap_denom != 0.0f ) // just in case - tonemap_scale_c = (255.0f - brightness_target) / tonemap_denom; - //throw new RuntimeException(); // test - } - // Higher tonemap_scale_c values means darker results from the Reinhard tonemapping. - // Colours brighter than 255-tonemap_scale_c will be made darker, colours darker than 255-tonemap_scale_c will be made brighter - // (tonemap_scale_c==255 means therefore that colours will only be made darker). - if( MyDebug.LOG ) - Log.d(TAG, "tonemap_scale_c: " + tonemap_scale_c); - avgBrightenScript.set_tonemap_scale(tonemap_scale_c); - - float linear_scale = (max_possible_value + tonemap_scale_c) / max_possible_value; - if( MyDebug.LOG ) - Log.d(TAG, "linear_scale: " + linear_scale); - avgBrightenScript.set_linear_scale(linear_scale); + /*if( avgBrightenScript == null ) { + avgBrightenScript = new ScriptC_avg_brighten(rs); }*/ + ScriptC_avg_brighten avgBrightenScript = new ScriptC_avg_brighten(rs); + avgBrightenScript.set_bitmap(input); + avgBrightenScript.invoke_setBlackLevel(black_level); + + avgBrightenScript.set_median_filter_strength(median_filter_strength); + avgBrightenScript.invoke_setBrightenParameters(gain, gamma, low_x, mid_x, max_brightness); Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Allocation allocation_out = Allocation.createFromBitmap(rs, bitmap); -- GitLab From 7728db2e3a7d8b9ef7d91ae48520733650137920 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 7 May 2023 12:07:28 +0100 Subject: [PATCH 29/94] Collapse notification panel when launching from a quick settings tile. --- _docs/history.html | 1 + app/src/main/java/net/sourceforge/opencamera/MyTileService.java | 2 +- .../net/sourceforge/opencamera/MyTileServiceFrontCamera.java | 2 +- .../java/net/sourceforge/opencamera/MyTileServiceVideo.java | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index 6760d6477..34fee3ad4 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -53,6 +53,7 @@ FIXED Don't show zebra stripes, focus peaking or histogram, when displaying re "Pause after taking photo" option. FIXED Problem where clicking on gallery icon would sometimes go to a "base" image instead of HDR image, when saving HDR photos with base images (for Android 10+). +FIXED Collapse notification panel when launching from a quick settings tile. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. Version 1.51.1 (2023/01/02) diff --git a/app/src/main/java/net/sourceforge/opencamera/MyTileService.java b/app/src/main/java/net/sourceforge/opencamera/MyTileService.java index e781b5c13..45f9c1115 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyTileService.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyTileService.java @@ -46,6 +46,6 @@ public class MyTileService extends TileService { Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setAction(TILE_ID); - startActivity(intent); + startActivityAndCollapse(intent); // use this instead of startActivity() so that the notification panel doesn't remain pulled down } } diff --git a/app/src/main/java/net/sourceforge/opencamera/MyTileServiceFrontCamera.java b/app/src/main/java/net/sourceforge/opencamera/MyTileServiceFrontCamera.java index 101ae4f2e..fe1765381 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyTileServiceFrontCamera.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyTileServiceFrontCamera.java @@ -46,6 +46,6 @@ public class MyTileServiceFrontCamera extends TileService { Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setAction(TILE_ID); - startActivity(intent); + startActivityAndCollapse(intent); // use this instead of startActivity() so that the notification panel doesn't remain pulled down } } diff --git a/app/src/main/java/net/sourceforge/opencamera/MyTileServiceVideo.java b/app/src/main/java/net/sourceforge/opencamera/MyTileServiceVideo.java index 50c3f265c..f00a55a16 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyTileServiceVideo.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyTileServiceVideo.java @@ -46,6 +46,6 @@ public class MyTileServiceVideo extends TileService { Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setAction(TILE_ID); - startActivity(intent); + startActivityAndCollapse(intent); // use this instead of startActivity() so that the notification panel doesn't remain pulled down } } -- GitLab From 21ecb98a948de7cc9918a5c24595be2513c384f8 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 7 May 2023 12:33:47 +0100 Subject: [PATCH 30/94] Make use of setRecentsScreenshotEnabled() on Android 13. --- _docs/history.html | 2 ++ .../net/sourceforge/opencamera/MainActivity.java | 12 ++++++++++++ .../cameracontroller/CameraController.java | 2 ++ 3 files changed, 16 insertions(+) diff --git a/_docs/history.html b/_docs/history.html index 34fee3ad4..f2425bb57 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -55,6 +55,8 @@ FIXED Problem where clicking on gallery icon would sometimes go to a "base" im image, when saving HDR photos with base images (for Android 10+). FIXED Collapse notification panel when launching from a quick settings tile. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. +UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for + Android 13+). Version 1.51.1 (2023/01/02) diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java index 23718ff3f..1b5ec856b 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java @@ -3493,6 +3493,12 @@ public class MainActivity extends AppCompatActivity { }*/ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ) { + // we set this to prevent what's on the preview being used to show under the "recent apps" view - potentially useful + // for privacy reasons + setRecentsScreenshotEnabled(false); + } + if( lock_to_landscape ) { // force to landscape mode setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); @@ -3586,6 +3592,12 @@ public class MainActivity extends AppCompatActivity { public void setWindowFlagsForSettings(boolean set_lock_protect) { if( MyDebug.LOG ) Log.d(TAG, "setWindowFlagsForSettings: " + set_lock_protect); + + if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ) { + // in settings mode, okay to revert to default behaviour for using a screenshot for "recent apps" view + setRecentsScreenshotEnabled(true); + } + // allow screen rotation setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java index 01d0eaa93..043613f1b 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java @@ -354,6 +354,8 @@ public abstract class CameraController { * Otherwise there is a risk when opening the camera that the textureview still shows an image from when * the camera was previously opened (e.g., from pausing and resuming the application). This returns false (for CameraController2) * when the camera has received its first frame. + * Update: on more recent Android versions this didn't work very well, possibly due to a screenshot being used for "recent apps" + * view; on Android 13+, the activity can make use of shouldCoverPreview(false) for this. */ public boolean shouldCoverPreview() { return false; -- GitLab From 2f76773bf7d5c7bb7d1a9689a9521f8f6485652f Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Mon, 8 May 2023 22:56:24 +0100 Subject: [PATCH 31/94] Update logging. --- .../main/java/net/sourceforge/opencamera/HDRProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 926af7e54..b6712f7ac 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -986,7 +986,7 @@ public class HDRProcessor { int brightness = histogramInfo.median_brightness; int max_brightness = histogramInfo.max_brightness; if( MyDebug.LOG ) - Log.d(TAG, "### time after computeHistogram: " + (System.currentTimeMillis() - time_s)); + Log.d(TAG, "### processSingleImage: time after computeHistogram: " + (System.currentTimeMillis() - time_s)); if( MyDebug.LOG ) { Log.d(TAG, "median brightness: " + brightness); Log.d(TAG, "max brightness: " + max_brightness); @@ -1021,7 +1021,7 @@ public class HDRProcessor { } allocation = output_allocation; if( MyDebug.LOG ) - Log.d(TAG, "### time after dro_brighten: " + (System.currentTimeMillis() - time_s)); + Log.d(TAG, "### processSingleImage: time after dro_brighten: " + (System.currentTimeMillis() - time_s)); } } -- GitLab From 31aaad8ddd86a58a5c198939245ea7be387762ac Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Mon, 8 May 2023 22:57:15 +0100 Subject: [PATCH 32/94] Refactor code to new computeBlackLevel(). --- .../sourceforge/opencamera/HDRProcessor.java | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index b6712f7ac..4c5b89fa0 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2946,6 +2946,34 @@ public class HDRProcessor { return new BrightenFactors(gain, low_x, mid_x, gamma); } + private float computeBlackLevel(HistogramInfo histogramInfo, int [] histo, int iso) { + float black_level = 0.0f; + { + // quick and dirty dehaze algorithm + // helps (among others): testAvg1 to testAvg10, testAvg27, testAvg30, testAvg31, testAvg39, testAvg40 + int total = histogramInfo.total; + int percentile = (int)(total*0.001f); + int count = 0; + int darkest_brightness = -1; + for(int i = 0; i < histo.length; i++) { + count += histo[i]; + if( count >= percentile && darkest_brightness == -1 ) { + darkest_brightness = i; + } + } + black_level = Math.max(black_level, darkest_brightness); + // don't allow black_level too high for "dark" images, as this can cause problems due to exaggerating noise (e.g., + // see testAvg38) + black_level = Math.min(black_level, iso <= 700 ? 18 : 4); + if( MyDebug.LOG ) { + Log.d(TAG, "percentile: " + percentile); + Log.d(TAG, "darkest_brightness: " + darkest_brightness); + Log.d(TAG, "black_level is now: " + black_level); + } + } + return black_level; + } + /** Final stage of the noise reduction algorithm. * Note that the returned bitmap will be scaled up by the factor returned by getAvgSampleSize(). * @param input The allocation in floating point format. @@ -3007,30 +3035,7 @@ public class HDRProcessor { } }*/ - float black_level = 0.0f; - { - // quick and dirty dehaze algorithm - // helps (among others): testAvg1 to testAvg10, testAvg27, testAvg30, testAvg31, testAvg39, testAvg40 - int total = histogramInfo.total; - int percentile = (int)(total*0.001f); - int count = 0; - int darkest_brightness = -1; - for(int i = 0; i < histo.length; i++) { - count += histo[i]; - if( count >= percentile && darkest_brightness == -1 ) { - darkest_brightness = i; - } - } - black_level = Math.max(black_level, darkest_brightness); - // don't allow black_level too high for "dark" images, as this can cause problems due to exaggerating noise (e.g., - // see testAvg38) - black_level = Math.min(black_level, iso <= 700 ? 18 : 4); - if( MyDebug.LOG ) { - Log.d(TAG, "percentile: " + percentile); - Log.d(TAG, "darkest_brightness: " + darkest_brightness); - Log.d(TAG, "black_level is now: " + black_level); - } - } + float black_level = computeBlackLevel(histogramInfo, histo, iso); // use a lower medial filter strength for pixel binned images, so that we don't blur testAvg46 so much (especially sign text) float median_filter_strength = (cached_avg_sample_size >= 2) ? 0.5f : 1.0f; -- GitLab From 6f9b2d9644dc50e2aa2320d9a5312f593a709c53 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Mon, 8 May 2023 23:01:15 +0100 Subject: [PATCH 33/94] Remove out of date comments. --- .../net/sourceforge/opencamera/HDRProcessor.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 4c5b89fa0..ef03a0a1a 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2975,7 +2975,6 @@ public class HDRProcessor { } /** Final stage of the noise reduction algorithm. - * Note that the returned bitmap will be scaled up by the factor returned by getAvgSampleSize(). * @param input The allocation in floating point format. * @param width Width of the input. * @param height Height of the input. @@ -3093,17 +3092,6 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "### time after copying to bitmap: " + (System.currentTimeMillis() - time_s)); - /*int sample_size = getAvgSampleSize(); - if( MyDebug.LOG ) - Log.d(TAG, "sample_size: " + sample_size); - if( sample_size > 1 ) { - Matrix matrix = new Matrix(); - matrix.postScale(sample_size, sample_size); - Bitmap new_bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); - bitmap.recycle(); - bitmap = new_bitmap; - }*/ - freeScripts(); if( MyDebug.LOG ) Log.d(TAG, "### total time for avgBrighten: " + (System.currentTimeMillis() - time_s)); -- GitLab From 6910bc0a5458ccf99ecf14cde31d455af584a287 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Mon, 8 May 2023 23:13:30 +0100 Subject: [PATCH 34/94] Refactor to use local variable hdrProcessor. --- .../java/net/sourceforge/opencamera/TestUtils.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java index afdab103c..accc399f1 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java @@ -562,9 +562,10 @@ public class TestUtils { Bitmap nr_bitmap; try { + HDRProcessor hdrProcessor = activity.getApplicationInterface().getHDRProcessor(); // initialise allocation from first two bitmaps - //int inSampleSize = activity.getApplicationInterface().getHDRProcessor().getAvgSampleSize(inputs.size()); - int inSampleSize = activity.getApplicationInterface().getHDRProcessor().getAvgSampleSize(iso, exposure_time); + //int inSampleSize = hdrProcessor.getAvgSampleSize(inputs.size()); + int inSampleSize = hdrProcessor.getAvgSampleSize(iso, exposure_time); Bitmap bitmap0 = getBitmapFromFile(activity, inputs.get(0), inSampleSize); Bitmap bitmap1 = getBitmapFromFile(activity, inputs.get(1), inSampleSize); int width = bitmap0.getWidth(); @@ -573,7 +574,7 @@ public class TestUtils { float avg_factor = 1.0f; List times = new ArrayList<>(); long time_s = System.currentTimeMillis(); - HDRProcessor.AvgData avg_data = activity.getApplicationInterface().getHDRProcessor().processAvg(bitmap0, bitmap1, avg_factor, iso, exposure_time, zoom_factor); + HDRProcessor.AvgData avg_data = hdrProcessor.processAvg(bitmap0, bitmap1, avg_factor, iso, exposure_time, zoom_factor); Allocation allocation = avg_data.allocation_out; times.add(System.currentTimeMillis() - time_s); // processAvg recycles both bitmaps @@ -587,7 +588,7 @@ public class TestUtils { Bitmap new_bitmap = getBitmapFromFile(activity, inputs.get(i), inSampleSize); avg_factor = (float)i; time_s = System.currentTimeMillis(); - activity.getApplicationInterface().getHDRProcessor().updateAvg(avg_data, width, height, new_bitmap, avg_factor, iso, exposure_time, zoom_factor); + hdrProcessor.updateAvg(avg_data, width, height, new_bitmap, avg_factor, iso, exposure_time, zoom_factor); times.add(System.currentTimeMillis() - time_s); // updateAvg recycles new_bitmap if( cb != null ) { @@ -596,7 +597,7 @@ public class TestUtils { } time_s = System.currentTimeMillis(); - nr_bitmap = activity.getApplicationInterface().getHDRProcessor().avgBrighten(allocation, width, height, iso, exposure_time); + nr_bitmap = hdrProcessor.avgBrighten(allocation, width, height, iso, exposure_time); avg_data.destroy(); //noinspection UnusedAssignment avg_data = null; -- GitLab From 2c3cba9b7cbe46d86d07d1291007da9cdcd3f117 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 9 May 2023 23:07:08 +0100 Subject: [PATCH 35/94] Update comments. --- app/src/main/rs/avg_brighten.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/rs/avg_brighten.rs b/app/src/main/rs/avg_brighten.rs index 824d41df4..479f5a30d 100644 --- a/app/src/main/rs/avg_brighten.rs +++ b/app/src/main/rs/avg_brighten.rs @@ -151,11 +151,12 @@ uchar4 __attribute__((kernel)) avg_brighten_f(float3 rgb, uint32_t x, uint32_t y // if making canges to this (especially radius, C), run AvgTests - in particular, pay close // attention to: // testAvg6: don't want to make the postcard too blurry - // testAvg8: zoom in to 60%, ensure still appears reasonably sharp + // testAvg8: zoom in to 600%, ensure still appears reasonably sharp // testAvg23: ensure we do reduce the noise, e.g., view around "vicks", without making the // text blurry // testAvg24: want to reduce the colour noise near the wall, but don't blur out detail, e.g. // at the flowers + // testAvg31 // Also need to be careful of performance. /*float old_value = fmax(rgb.r, rgb.g); old_value = fmax(old_value, rgb.b);*/ -- GitLab From f35ad0c0a07b0c32541ef0ffa000cd71090bc4e1 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 10 May 2023 22:26:49 +0100 Subject: [PATCH 36/94] Can now allow non-fake toasts when resuming from background. --- _docs/history.html | 2 ++ .../opencamera/preview/Preview.java | 20 ++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index f2425bb57..f8b9fc62f 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -54,6 +54,8 @@ FIXED Don't show zebra stripes, focus peaking or histogram, when displaying re FIXED Problem where clicking on gallery icon would sometimes go to a "base" image instead of HDR image, when saving HDR photos with base images (for Android 10+). FIXED Collapse notification panel when launching from a quick settings tile. +FIXED Some info toasts weren't showing (e.g., when cancelling SAF dialog, or denying location + permission). UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for Android 13+). diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index a113c164d..712242511 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -7746,14 +7746,20 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu return; } - if( MyDebug.LOG ) + if( MyDebug.LOG ) { Log.d(TAG, "showToast: " + message); + Log.d(TAG, "use_fake_toast: " + use_fake_toast); + } - if( this.app_is_paused ) { + if( this.app_is_paused && use_fake_toast ) { if( MyDebug.LOG ) - Log.e(TAG, "don't show toast as application is paused: " + message); - // when targeting Android 11+, toasts with custom views won't be shown in background anyway - in theory we - // shouldn't be making toasts when in background, but check just in case + Log.e(TAG, "don't show fake toast as application is paused: " + message); + // When targeting Android 11+, toasts with custom views won't be shown in background anyway - in theory we + // shouldn't be making toasts when in background, but check just in case. + // However we no longer use custom views when use_fake_toast==false, so fine to allow those - and indeed this + // is useful for cases where the toast is created shortly before Open Camera resumes, e.g., cancelling SAF + // (see toast in MainActivity.onActivityResult()), or denying location permission (see toast from + // PermissionHandler.onRequestPermissionsResult()). return; } @@ -7763,9 +7769,9 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu // Also for the use_fake_toast code, running the creation code, and the postDelayed code (and the code in clearActiveFakeToast()), on the UI thread avoids threading issues activity.runOnUiThread(new Runnable() { public void run() { - if( Preview.this.app_is_paused ) { + if( Preview.this.app_is_paused && use_fake_toast ) { if( MyDebug.LOG ) - Log.e(TAG, "don't show toast as application is paused: " + message); + Log.e(TAG, "don't show fake toast as application is paused: " + message); // see note above return; } -- GitLab From b24461b886e145873d846b0471e3f530bf63672a Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 10 May 2023 22:58:21 +0100 Subject: [PATCH 37/94] Tweak avgbrighten script for performance. --- _docs/history.html | 1 + app/src/main/rs/avg_brighten.rs | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index f8b9fc62f..d7fd9e1a4 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -56,6 +56,7 @@ FIXED Problem where clicking on gallery icon would sometimes go to a "base" im FIXED Collapse notification panel when launching from a quick settings tile. FIXED Some info toasts weren't showing (e.g., when cancelling SAF dialog, or denying location permission). +UPDATED Improved performance for NR photo mode. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for Android 13+). diff --git a/app/src/main/rs/avg_brighten.rs b/app/src/main/rs/avg_brighten.rs index 479f5a30d..04eaa659f 100644 --- a/app/src/main/rs/avg_brighten.rs +++ b/app/src/main/rs/avg_brighten.rs @@ -237,10 +237,12 @@ uchar4 __attribute__((kernel)) avg_brighten_f(float3 rgb, uint32_t x, uint32_t y { // spatial noise reduction filter, colour only // if changing this, see list of tests under standard spatial noise reduction above - float old_value = fmax(rgb.r, rgb.g); - old_value = fmax(old_value, rgb.b); + //float old_value = fmax(rgb.r, rgb.g); + //old_value = fmax(old_value, rgb.b); + float old_value = rgb.g; // use only green component for performance float3 sum = 0.0; - int radius = 3; + //int radius = 3; + int radius = 2; int count = 0; int sx = (x >= radius) ? x-radius : 0; int ex = (x < width-radius) ? x+radius : width-1; @@ -252,14 +254,16 @@ uchar4 __attribute__((kernel)) avg_brighten_f(float3 rgb, uint32_t x, uint32_t y { float3 this_pixel = rsGetElementAt_float3(bitmap, cx, cy); { - float this_value = fmax(this_pixel.r, this_pixel.g); - this_value = fmax(this_value, this_pixel.b); + //float this_value = fmax(this_pixel.r, this_pixel.g); + //this_value = fmax(this_value, this_pixel.b); + float this_value = this_pixel.g; // use only green component for performance if( this_value > 0.5f ) this_pixel *= old_value/this_value; // use a wiener filter, so that more similar pixels have greater contribution // smaller value of C means stronger filter (i.e., less averaging) // for now set at same value as standard spatial filter above - const float C = 64.0f*64.0f/8.0f; + //const float C = 64.0f*64.0f/8.0f; + const float C = 16.0f*16.0f/8.0f; float3 diff = rgb - this_pixel; float L = dot(diff, diff); //L = 0.0f; // test no wiener filter -- GitLab From 7649c4b29476f9c3ea1939a35d2d3a8dc238c503 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 10 May 2023 23:26:39 +0100 Subject: [PATCH 38/94] Simplify inputs to processAvgCore, don't need allocation_avg. --- .../sourceforge/opencamera/HDRProcessor.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index ef03a0a1a..ec740d9bd 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1243,7 +1243,7 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "median: " + luminanceInfo.median_value);*/ - AvgData avg_data = processAvgCore(null, null, bitmap_avg, bitmap_new, width, height, avg_factor, iso, exposure_time, zoom_factor, null, null, null, time_s); + AvgData avg_data = processAvgCore(null, bitmap_avg, bitmap_new, width, height, avg_factor, iso, exposure_time, zoom_factor, null, null, null, time_s); //allocation_avg.copyTo(bitmap_avg); @@ -1284,19 +1284,19 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "### time after creating allocations from bitmaps: " + (System.currentTimeMillis() - time_s));*/ - processAvgCore(avg_data.allocation_out, avg_data.allocation_out, null, bitmap_new, width, height, avg_factor, iso, exposure_time, zoom_factor, avg_data.allocation_avg_align, avg_data.bitmap_avg_align, avg_data.allocation_orig, time_s); + processAvgCore(avg_data.allocation_out, null, bitmap_new, width, height, avg_factor, iso, exposure_time, zoom_factor, avg_data.allocation_avg_align, avg_data.bitmap_avg_align, avg_data.allocation_orig, time_s); if( MyDebug.LOG ) Log.d(TAG, "### time for updateAvg: " + (System.currentTimeMillis() - time_s)); } /** Core algorithm for Noise Reduction algorithm. - * @param allocation_out If non-null, this will be used for the output allocation, otherwise a - * new one will be created. - * @param allocation_avg If non-null, an allocation for the averaged image so far. If null, the - * first bitmap should be supplied as bitmap_avg. + * @param allocation_out If non-null, this is an allocation of the averaged image so far, and it + * will also be used for the output allocation. If null, the first bitmap + * should be supplied as bitmap_avg, and a new allocation will be created + * for the output. * @param bitmap_avg If non-null, the first bitmap (which will be recycled when the returned - * AvgData is destroyed). If null, an allocation_avg should be supplied. + * AvgData is destroyed). If null, an allocation_out should be supplied. * @param bitmap_new The new bitmap to combined. The bitmap will be recycled. * @param width The width of the bitmaps. * @param height The height of the bitmaps. @@ -1311,7 +1311,7 @@ public class HDRProcessor { * @param time_s Time, for debugging. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - private AvgData processAvgCore(Allocation allocation_out, Allocation allocation_avg, Bitmap bitmap_avg, Bitmap bitmap_new, int width, int height, float avg_factor, int iso, long exposure_time, float zoom_factor, Allocation allocation_avg_align, Bitmap bitmap_avg_align, Allocation allocation_orig, long time_s) { + private AvgData processAvgCore(Allocation allocation_out, Bitmap bitmap_avg, Bitmap bitmap_new, int width, int height, float avg_factor, int iso, long exposure_time, float zoom_factor, Allocation allocation_avg_align, Bitmap bitmap_avg_align, Allocation allocation_orig, long time_s) { if( MyDebug.LOG ) { Log.d(TAG, "processAvgCore"); Log.d(TAG, "iso: " + iso); @@ -1319,6 +1319,7 @@ public class HDRProcessor { } Allocation allocation_new = null; + Allocation allocation_avg = allocation_out; boolean free_allocation_avg = false; //Allocation allocation_diffs = null; -- GitLab From 9359fbb098c5cb17f3f0bef1c741f301a73644f8 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 10 May 2023 23:35:12 +0100 Subject: [PATCH 39/94] Simplify code. --- .../sourceforge/opencamera/HDRProcessor.java | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index ec740d9bd..24951411c 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -880,12 +880,14 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "call processHDRScript"); - Allocation output_allocation; + final Allocation output_allocation; boolean free_output_allocation = false; if( release_bitmaps ) { // must use allocations[base_bitmap] as the output, as that's the image guaranteed to have no offset (otherwise we'll have // problems due to the output being equal to one of the inputs) output_allocation = allocations[base_bitmap]; + // similarly must be the base_bitmap we copy to + output_bitmap = bitmaps.get(base_bitmap); } else { output_allocation = Allocation.createFromBitmap(rs, output_bitmap); @@ -923,23 +925,17 @@ public class HDRProcessor { Log.d(TAG, "### time after adjustHistogram: " + (System.currentTimeMillis() - time_s)); } - if( release_bitmaps ) { - // must be the base_bitmap we copy to - see note above about using allocations[base_bitmap] as the output - allocations[base_bitmap].copyTo(bitmaps.get(base_bitmap)); - if( MyDebug.LOG ) - Log.d(TAG, "### time after copying to bitmap: " + (System.currentTimeMillis() - time_s)); + output_allocation.copyTo(output_bitmap); + if( MyDebug.LOG ) + Log.d(TAG, "### time after copying to bitmap: " + (System.currentTimeMillis() - time_s)); + if( release_bitmaps ) { // make it so that we store the output bitmap as first in the list - bitmaps.set(0, bitmaps.get(base_bitmap)); + bitmaps.set(0, output_bitmap); for(int i=1;i Date: Wed, 10 May 2023 23:35:36 +0100 Subject: [PATCH 40/94] Update logging. --- app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 24951411c..0858ad917 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -960,7 +960,7 @@ public class HDRProcessor { initRenderscript(); if( MyDebug.LOG ) - Log.d(TAG, "### time after creating renderscript: " + (System.currentTimeMillis() - time_s)); + Log.d(TAG, "### processSingleImage: time after creating renderscript: " + (System.currentTimeMillis() - time_s)); // create allocation Allocation allocation = Allocation.createFromBitmap(rs, bitmaps.get(0)); -- GitLab From a5d8e1c8a2748d845d7be54ce537043f312b9c66 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 11 May 2023 22:30:01 +0100 Subject: [PATCH 41/94] Refactor - reorder, and remove some unused code. --- .../sourceforge/opencamera/HDRProcessor.java | 116 ++++++++---------- 1 file changed, 49 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 0858ad917..31c52616b 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1310,7 +1310,6 @@ public class HDRProcessor { Log.d(TAG, "zoom_factor: " + zoom_factor); } - Allocation allocation_new = null; Allocation allocation_avg = allocation_out; boolean free_allocation_avg = false; @@ -1320,19 +1319,17 @@ public class HDRProcessor { offsets_y = new int[2]; boolean floating_point = bitmap_avg == null; { - boolean floating_point_align = floating_point; + boolean floating_point_align; // perform auto-alignment List align_bitmaps = new ArrayList<>(); Allocation [] allocations = new Allocation[2]; - Bitmap bitmap_new_align = null; - Allocation allocation_new_align = null; - int alignment_width = width; - int alignment_height = height; + Bitmap bitmap_new_align; + Allocation allocation_new_align; + int alignment_width; + int alignment_height; int full_alignment_width = width; int full_alignment_height = height; - //final boolean scale_align = false; - final boolean scale_align = true; //final int scale_align_size = 2; //final int scale_align_size = 4; //final int scale_align_size = Math.max(4 / this.cached_avg_sample_size, 1); @@ -1342,7 +1339,7 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "scale_align_size: " + scale_align_size); boolean crop_to_centre = true; - if( scale_align ) { + { // use scaled down and/or cropped bitmaps for alignment if( MyDebug.LOG ) Log.d(TAG, "### time before creating allocations for autoalignment: " + (System.currentTimeMillis() - time_s)); @@ -1390,19 +1387,6 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "### time after creating allocations for autoalignment: " + (System.currentTimeMillis() - time_s)); } - else { - if( allocation_avg == null ) { - allocation_avg = Allocation.createFromBitmap(rs, bitmap_avg); - free_allocation_avg = true; - } - allocation_new = Allocation.createFromBitmap(rs, bitmap_new); - if( MyDebug.LOG ) - Log.d(TAG, "### time after creating allocations from bitmaps: " + (System.currentTimeMillis() - time_s)); - align_bitmaps.add(bitmap_avg); - align_bitmaps.add(bitmap_new); - allocations[0] = allocation_avg; - allocations[1] = allocation_new; - } // misalignment more likely in "dark" images with more images and/or longer exposures // using max_align_scale=2 needed to prevent misalignment in testAvg51; also helps testAvg14 @@ -1426,7 +1410,7 @@ public class HDRProcessor { processAvgScript.set_allocation_diffs(allocation_diffs); */ - if( scale_align ) { + { for(int i=0;i= 700 ) { + // helps reduce speckles in testAvg17, testAvg23, testAvg33, testAvg36, testAvg38 + // using this level for testAvg31 (ISO 609) would increase ghosting + //limited_iso = 500; + limited_iso = 800; + if( iso >= 1100 ) { + // helps further reduce speckles in testAvg17, testAvg38 + // but don't do for iso >= 700 as makes "vicks" text in testAvg23 slightly more blurred + wiener_cutoff_factor = 8.0f; + } + } + limited_iso = Math.max(limited_iso, 100); + float wiener_C = 10.0f * limited_iso; + //float wiener_C = 1000.0f; + //float wiener_C = 4000.0f; + + // Tapering the wiener scale means that we do more averaging for earlier images in the stack, the + // logic being we'll have more chance of ghosting or misalignment with later images. + // This helps: testAvg31, testAvg33. + // Also slightly helps testAvg17, testAvg23 (slightly less white speckle on tv), testAvg28 + // (one less white speckle on face). + // Note that too much tapering risks increasing ghosting in testAvg26, testAvg39. + float tapered_wiener_scale = 1.0f - (float)Math.pow(0.5, avg_factor); + if( MyDebug.LOG ) { + Log.d(TAG, "avg_factor: " + avg_factor); + Log.d(TAG, "tapered_wiener_scale: " + tapered_wiener_scale); + } + wiener_C /= tapered_wiener_scale; + + float wiener_C_cutoff = wiener_cutoff_factor * wiener_C; + if( MyDebug.LOG ) { + Log.d(TAG, "wiener_C: " + wiener_C); + Log.d(TAG, "wiener_cutoff_factor: " + wiener_cutoff_factor); + } + // create RenderScript if( processAvgScript == null ) { processAvgScript = new ScriptC_process_avg(rs); @@ -1492,11 +1515,9 @@ public class HDRProcessor { first = false; }*/ - if( allocation_new == null ) { - allocation_new = Allocation.createFromBitmap(rs, bitmap_new); - if( MyDebug.LOG ) - Log.d(TAG, "### time after creating allocation_new from bitmap: " + (System.currentTimeMillis() - time_s)); - } + Allocation allocation_new = Allocation.createFromBitmap(rs, bitmap_new); + if( MyDebug.LOG ) + Log.d(TAG, "### time after creating allocation_new from bitmap: " + (System.currentTimeMillis() - time_s)); // set allocations @@ -1523,45 +1544,6 @@ public class HDRProcessor { // set globals processAvgScript.set_avg_factor(avg_factor); - - // higher wiener_C (and higher wiener_cutoff_factor) means more averaging (but more risk of ghosting) - // if changing this, pay close attention to tests testAvg6, testAvg8, testAvg17, testAvg23 - float limited_iso = Math.min(iso, 400); - float wiener_cutoff_factor = 1.0f; - if( iso >= 700 ) { - // helps reduce speckles in testAvg17, testAvg23, testAvg33, testAvg36, testAvg38 - // using this level for testAvg31 (ISO 609) would increase ghosting - //limited_iso = 500; - limited_iso = 800; - if( iso >= 1100 ) { - // helps further reduce speckles in testAvg17, testAvg38 - // but don't do for iso >= 700 as makes "vicks" text in testAvg23 slightly more blurred - wiener_cutoff_factor = 8.0f; - } - } - limited_iso = Math.max(limited_iso, 100); - float wiener_C = 10.0f * limited_iso; - //float wiener_C = 1000.0f; - //float wiener_C = 4000.0f; - - // Tapering the wiener scale means that we do more averaging for earlier images in the stack, the - // logic being we'll have more chance of ghosting or misalignment with later images. - // This helps: testAvg31, testAvg33. - // Also slightly helps testAvg17, testAvg23 (slightly less white speckle on tv), testAvg28 - // (one less white speckle on face). - // Note that too much tapering risks increasing ghosting in testAvg26, testAvg39. - float tapered_wiener_scale = 1.0f - (float)Math.pow(0.5, avg_factor); - if( MyDebug.LOG ) { - Log.d(TAG, "avg_factor: " + avg_factor); - Log.d(TAG, "tapered_wiener_scale: " + tapered_wiener_scale); - } - wiener_C /= tapered_wiener_scale; - - float wiener_C_cutoff = wiener_cutoff_factor * wiener_C; - if( MyDebug.LOG ) { - Log.d(TAG, "wiener_C: " + wiener_C); - Log.d(TAG, "wiener_cutoff_factor: " + wiener_cutoff_factor); - } processAvgScript.set_wiener_C(wiener_C); processAvgScript.set_wiener_C_cutoff(wiener_C_cutoff); -- GitLab From b0fbfc2f088fdd6b15bbde1d3f9c3e0749459e6e Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 11 May 2023 22:41:17 +0100 Subject: [PATCH 42/94] More refactoring/reordering, also add exception for incorrect inputs of bitmap_avg or allocation_out. --- .../sourceforge/opencamera/HDRProcessor.java | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 31c52616b..79373dc11 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1310,14 +1310,25 @@ public class HDRProcessor { Log.d(TAG, "zoom_factor: " + zoom_factor); } - Allocation allocation_avg = allocation_out; - boolean free_allocation_avg = false; - //Allocation allocation_diffs = null; offsets_x = new int[2]; offsets_y = new int[2]; - boolean floating_point = bitmap_avg == null; + boolean floating_point; + if( bitmap_avg != null && allocation_out == null ) { + if( MyDebug.LOG ) + Log.d(TAG, "process first bitmap"); + floating_point = false; + } + else if( bitmap_avg == null && allocation_out != null ) { + floating_point = true; + if( MyDebug.LOG ) + Log.d(TAG, "processing existing allocation"); + } + else { + throw new RuntimeException("only one of bitmap_avg or allocation_out should be supplied"); + } + { boolean floating_point_align; // perform auto-alignment @@ -1435,20 +1446,6 @@ public class HDRProcessor { } } - if( allocation_out == null ) { - if( MyDebug.LOG ) - Log.d(TAG, "need to create allocation_out"); - allocation_out = Allocation.createTyped(rs, Type.createXY(rs, Element.F32_3(rs), width, height)); - if( MyDebug.LOG ) - Log.d(TAG, "### time after create allocation_out: " + (System.currentTimeMillis() - time_s)); - } - if( allocation_avg == null ) { - allocation_avg = Allocation.createFromBitmap(rs, bitmap_avg); - free_allocation_avg = true; - if( MyDebug.LOG ) - Log.d(TAG, "### time after creating allocation_avg from bitmap: " + (System.currentTimeMillis() - time_s)); - } - // write new avg image // higher wiener_C (and higher wiener_cutoff_factor) means more averaging (but more risk of ghosting) @@ -1490,6 +1487,24 @@ public class HDRProcessor { Log.d(TAG, "wiener_cutoff_factor: " + wiener_cutoff_factor); } + Allocation allocation_avg = allocation_out; + boolean free_allocation_avg = false; + + if( allocation_out == null ) { + if( MyDebug.LOG ) + Log.d(TAG, "need to create allocation_out"); + allocation_out = Allocation.createTyped(rs, Type.createXY(rs, Element.F32_3(rs), width, height)); + if( MyDebug.LOG ) + Log.d(TAG, "### time after create allocation_out: " + (System.currentTimeMillis() - time_s)); + } + + if( allocation_avg == null ) { + allocation_avg = Allocation.createFromBitmap(rs, bitmap_avg); + free_allocation_avg = true; + if( MyDebug.LOG ) + Log.d(TAG, "### time after creating allocation_avg from bitmap: " + (System.currentTimeMillis() - time_s)); + } + // create RenderScript if( processAvgScript == null ) { processAvgScript = new ScriptC_process_avg(rs); -- GitLab From 227ee95c1854eea6960883b5972fc8ebc5f4e256 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 12 May 2023 23:44:05 +0100 Subject: [PATCH 43/94] Fix typo bug. --- app/src/main/rs/process_avg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/rs/process_avg.rs b/app/src/main/rs/process_avg.rs index 38dd07798..2ece2d3b8 100644 --- a/app/src/main/rs/process_avg.rs +++ b/app/src/main/rs/process_avg.rs @@ -86,7 +86,7 @@ float3 __attribute__((kernel)) avg_f(float3 pixel_avg_f, uint32_t x, uint32_t y) L += dot(diff, diff); diff = convert_float3(rsGetElementAt_uchar4(bitmap_orig, ix-2, iy+2).rgb) - convert_float3(rsGetElementAt_uchar4(bitmap_new, ox-2, oy+2).rgb); L += dot(diff, diff); - diff = convert_float3(rsGetElementAt_uchar4(bitmap_orig, ix-2, iy+2).rgb) - convert_float3(rsGetElementAt_uchar4(bitmap_new, ox+2, oy+2).rgb); + diff = convert_float3(rsGetElementAt_uchar4(bitmap_orig, ix+2, iy+2).rgb) - convert_float3(rsGetElementAt_uchar4(bitmap_new, ox+2, oy+2).rgb); L += dot(diff, diff); L /= n_pixels; -- GitLab From ea283423d1e1f11dcbdd23f95c76117c181ac8d4 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 16 May 2023 23:01:05 +0100 Subject: [PATCH 44/94] Add names for threads. --- app/src/main/java/net/sourceforge/opencamera/ImageSaver.java | 2 ++ .../main/java/net/sourceforge/opencamera/PanoramaProcessor.java | 1 + 2 files changed, 3 insertions(+) diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index 04ae4c7cf..614601b57 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -300,6 +300,7 @@ public class ImageSaver extends Thread { } ImageSaver(MainActivity main_activity) { + super("ImageSaver"); if( MyDebug.LOG ) Log.d(TAG, "ImageSaver"); this.main_activity = main_activity; @@ -1104,6 +1105,7 @@ public class ImageSaver extends Thread { final BitmapFactory.Options options; final byte [] jpeg; LoadBitmapThread(BitmapFactory.Options options, byte [] jpeg) { + super("LoadBitmapThread"); this.options = options; this.jpeg = jpeg; } diff --git a/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java b/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java index b42cdc57e..5e2050ee4 100644 --- a/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java @@ -867,6 +867,7 @@ public class PanoramaProcessor { private final int [] pixels1; ComputeDistancesBetweenMatchesThread(List matches, int st_indx, int nd_indx, int feature_descriptor_radius, List bitmaps, int [] pixels0, int [] pixels1) { + super("ComputeDistancesBetweenMatchesThread"); this.matches = matches; this.st_indx = st_indx; this.nd_indx = nd_indx; -- GitLab From 2d7b6b296c4a2e663b280858003c060fb332d771 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 18 May 2023 21:48:46 +0100 Subject: [PATCH 45/94] Refactor to pass AvgData to processAvgCore(). --- .../sourceforge/opencamera/HDRProcessor.java | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 79373dc11..bf79dbc8d 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1235,7 +1235,7 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "median: " + luminanceInfo.median_value);*/ - AvgData avg_data = processAvgCore(null, bitmap_avg, bitmap_new, width, height, avg_factor, iso, exposure_time, zoom_factor, null, null, null, time_s); + AvgData avg_data = processAvgCore(null, bitmap_avg, bitmap_new, width, height, avg_factor, iso, exposure_time, zoom_factor, time_s); //allocation_avg.copyTo(bitmap_avg); @@ -1276,40 +1276,49 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "### time after creating allocations from bitmaps: " + (System.currentTimeMillis() - time_s));*/ - processAvgCore(avg_data.allocation_out, null, bitmap_new, width, height, avg_factor, iso, exposure_time, zoom_factor, avg_data.allocation_avg_align, avg_data.bitmap_avg_align, avg_data.allocation_orig, time_s); + processAvgCore(avg_data, null, bitmap_new, width, height, avg_factor, iso, exposure_time, zoom_factor, time_s); if( MyDebug.LOG ) Log.d(TAG, "### time for updateAvg: " + (System.currentTimeMillis() - time_s)); } /** Core algorithm for Noise Reduction algorithm. - * @param allocation_out If non-null, this is an allocation of the averaged image so far, and it - * will also be used for the output allocation. If null, the first bitmap - * should be supplied as bitmap_avg, and a new allocation will be created - * for the output. + * @param avg_data Should be null for first call, and non-null for subsequent calls. This should + * be the AvgData returned by the first call. * @param bitmap_avg If non-null, the first bitmap (which will be recycled when the returned - * AvgData is destroyed). If null, an allocation_out should be supplied. + * AvgData is destroyed). If null, an avg_data should be supplied. * @param bitmap_new The new bitmap to combined. The bitmap will be recycled. * @param width The width of the bitmaps. * @param height The height of the bitmaps. * @param avg_factor The averaging factor. * @param iso The ISO used for the photos. * @param zoom_factor The digital zoom factor used to take the photos. - * @param allocation_avg_align If non-null, use this allocation for alignment for averaged image. - * @param bitmap_avg_align Should be supplied if allocation_avg_align is non-null, and stores - * the bitmap corresponding to the allocation_avg_align. - * @param allocation_orig - * If non-null, this is an allocation representing the first image. * @param time_s Time, for debugging. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - private AvgData processAvgCore(Allocation allocation_out, Bitmap bitmap_avg, Bitmap bitmap_new, int width, int height, float avg_factor, int iso, long exposure_time, float zoom_factor, Allocation allocation_avg_align, Bitmap bitmap_avg_align, Allocation allocation_orig, long time_s) { + private AvgData processAvgCore(AvgData avg_data, Bitmap bitmap_avg, Bitmap bitmap_new, int width, int height, float avg_factor, int iso, long exposure_time, float zoom_factor, long time_s) { if( MyDebug.LOG ) { Log.d(TAG, "processAvgCore"); Log.d(TAG, "iso: " + iso); Log.d(TAG, "zoom_factor: " + zoom_factor); } + // If non-null, allocation_out is an allocation of the averaged image so far, and it will + // also be used for the output allocation. If null, the first bitmap should be supplied as + // bitmap_avg, and a new allocation will be created for the output. + Allocation allocation_out = null; + Bitmap bitmap_avg_align = null; // if non-null, use this bitmap for alignment for averaged image. + Allocation allocation_avg_align = null; // allocation corresponding to bitmap_avg_align + Bitmap bitmap_orig = null; // if non-null, this is a bitmap representing the first image. + Allocation allocation_orig = null; // allocation corresponding to bitmap_orig + if( avg_data != null ) { + allocation_out = avg_data.allocation_out; + bitmap_avg_align = avg_data.bitmap_avg_align; + allocation_avg_align = avg_data.allocation_avg_align; + bitmap_orig = avg_data.bitmap_orig; + allocation_orig = avg_data.allocation_orig; + } + //Allocation allocation_diffs = null; offsets_x = new int[2]; -- GitLab From ff8c4843431b7027052109c0dd7d63d58e0e3a44 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 18 May 2023 22:17:22 +0100 Subject: [PATCH 46/94] Refactor avgBrighten() to receive AvgData instead of allocation. --- .../net/sourceforge/opencamera/TestUtils.java | 3 +-- .../sourceforge/opencamera/HDRProcessor.java | 17 +++++++++++++++-- .../net/sourceforge/opencamera/ImageSaver.java | 3 +-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java index accc399f1..8de859ab0 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java @@ -575,7 +575,6 @@ public class TestUtils { List times = new ArrayList<>(); long time_s = System.currentTimeMillis(); HDRProcessor.AvgData avg_data = hdrProcessor.processAvg(bitmap0, bitmap1, avg_factor, iso, exposure_time, zoom_factor); - Allocation allocation = avg_data.allocation_out; times.add(System.currentTimeMillis() - time_s); // processAvg recycles both bitmaps if( cb != null ) { @@ -597,7 +596,7 @@ public class TestUtils { } time_s = System.currentTimeMillis(); - nr_bitmap = hdrProcessor.avgBrighten(allocation, width, height, iso, exposure_time); + nr_bitmap = hdrProcessor.avgBrighten(avg_data, width, height, iso, exposure_time); avg_data.destroy(); //noinspection UnusedAssignment avg_data = null; diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index bf79dbc8d..e641391a9 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2982,9 +2982,9 @@ public class HDRProcessor { * @return Resultant bitmap. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public Bitmap avgBrighten(Allocation input, int width, int height, int iso, long exposure_time) { + private Bitmap avgBrightenRS(Allocation input, int width, int height, int iso, long exposure_time) { if( MyDebug.LOG ) { - Log.d(TAG, "avgBrighten"); + Log.d(TAG, "avgBrightenRS"); Log.d(TAG, "iso: " + iso); Log.d(TAG, "exposure_time: " + exposure_time); } @@ -3097,6 +3097,19 @@ public class HDRProcessor { return bitmap; } + /** Final stage of the noise reduction algorithm. + * @param avg_data AvgData returned from call to processAvg(). + * @param width Width of the input. + * @param height Height of the input. + * @param iso ISO used for the original images. + * @param exposure_time Exposure time used for the original images. + * @return Resultant bitmap. + */ + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public Bitmap avgBrighten(AvgData avg_data, int width, int height, int iso, long exposure_time) { + return avgBrightenRS(avg_data.allocation_out, width, height, iso, exposure_time); + } + /** * Computes a value for how sharp the image is perceived to be. The higher the value, the * sharper the image. diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index 614601b57..b89f9a576 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -1509,7 +1509,6 @@ public class ImageSaver extends Thread { if( MyDebug.LOG ) { Log.d(TAG, "*** time for processing first two bitmaps: " + (System.currentTimeMillis() - this_time_s)); } - Allocation allocation = avg_data.allocation_out; for(int i=2;i Date: Fri, 19 May 2023 23:10:37 +0100 Subject: [PATCH 47/94] Refactoring for autoAlignment(). --- .../sourceforge/opencamera/HDRProcessor.java | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index e641391a9..89e300987 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1770,7 +1770,7 @@ public class HDRProcessor { /** * - * @param bitmaps Only required if use_mtb is true, otherwise may be null. + * @param bitmaps Bitmaps to align. * @param base_bitmap Index of bitmap in bitmaps that should be kept fixed; the other bitmaps * will be aligned relative to this. * @param assume_sorted If assume_sorted if false, and use_mtb is true, this function will also @@ -1796,16 +1796,23 @@ public class HDRProcessor { } } + int n_images = bitmaps.size(); + if( bitmaps.size() != allocations.length ) { + throw new RuntimeException("unequal bitmaps and allocations lengths"); + } + else if( bitmaps.size() != offsets_x.length ) { + throw new RuntimeException("unequal bitmaps and offsets_x lengths"); + } + else if( bitmaps.size() != offsets_y.length ) { + throw new RuntimeException("unequal bitmaps and offsets_y lengths"); + } + // initialise - for(int i=0;i 255-(min_diff_c+1) ) { @@ -1959,6 +1968,8 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, i + ": median_value is now: " + median_value); + mtb_allocations[i] = Allocation.createTyped(rs, Type.createXY(rs, Element.U8(rs), mtb_width, mtb_height)); + // set parameters if( use_mtb ) createMTBScript.set_median_value(median_value); @@ -2058,7 +2069,7 @@ public class HDRProcessor { alignMTBScript.set_bitmap0(mtb_allocations[base_bitmap]); // bitmap1 set below - for(int i=0;i Date: Sat, 20 May 2023 23:52:44 +0100 Subject: [PATCH 48/94] Reorder code. --- .../sourceforge/opencamera/HDRProcessor.java | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 89e300987..413d684cc 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2100,6 +2100,22 @@ public class HDRProcessor { final boolean use_pyramid = false; //final boolean use_pyramid = true; + + int stop_x, stop_y; + if( use_pyramid ) { + stop_x = mtb_width; + stop_y = mtb_height; + } + else { + // see note inside align_mtb.rs/align_mtb() for why we sample over a subset of the image + stop_x = mtb_width/pixel_step_size; + stop_y = mtb_height/pixel_step_size; + } + if( MyDebug.LOG ) { + Log.d(TAG, "stop_x: " + stop_x); + Log.d(TAG, "stop_y: " + stop_y); + } + /*if( use_pyramid ) { // downscale by step_size Allocation [] scaled_allocations = new Allocation[2]; @@ -2141,19 +2157,11 @@ public class HDRProcessor { alignMTBScript.invoke_init_errors(); Script.LaunchOptions launch_options = new Script.LaunchOptions(); - if( !use_pyramid ) { - // see note inside align_mtb.rs/align_mtb() for why we sample over a subset of the image - int stop_x = mtb_width/pixel_step_size; - int stop_y = mtb_height/pixel_step_size; - if( MyDebug.LOG ) { - Log.d(TAG, "stop_x: " + stop_x); - Log.d(TAG, "stop_y: " + stop_y); - } - //launch_options.setX((int)(stop_x*0.25), (int)(stop_x*0.75)); - //launch_options.setY((int)(stop_y*0.25), (int)(stop_y*0.75)); - launch_options.setX(0, stop_x); - launch_options.setY(0, stop_y); - } + //launch_options.setX((int)(stop_x*0.25), (int)(stop_x*0.75)); + //launch_options.setY((int)(stop_y*0.25), (int)(stop_y*0.75)); + launch_options.setX(0, stop_x); + launch_options.setY(0, stop_y); + long this_time_s = System.currentTimeMillis(); if( use_mtb ) alignMTBScript.forEach_align_mtb(mtb_allocations[base_bitmap], launch_options); -- GitLab From 16247533dd0dce085a1d98329cc1f21d6a516a54 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 21 May 2023 00:08:28 +0100 Subject: [PATCH 49/94] More reordering. --- .../main/java/net/sourceforge/opencamera/HDRProcessor.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 413d684cc..0c84e067e 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2116,6 +2116,8 @@ public class HDRProcessor { Log.d(TAG, "stop_y: " + stop_y); } + int [] errors = new int[9]; + /*if( use_pyramid ) { // downscale by step_size Allocation [] scaled_allocations = new Allocation[2]; @@ -2171,12 +2173,11 @@ public class HDRProcessor { Log.d(TAG, "time for alignMTBScript: " + (System.currentTimeMillis() - this_time_s)); Log.d(TAG, "time after alignMTBScript: " + (System.currentTimeMillis() - time_s)); } + errorsAllocation.copyTo(errors); + errorsAllocation.destroy(); int best_error = -1; int best_id = -1; - int [] errors = new int[9]; - errorsAllocation.copyTo(errors); - errorsAllocation.destroy(); for(int j=0;j<9;j++) { int this_error = errors[j]; if( MyDebug.LOG ) -- GitLab From b9dec6a508c4bd834f7176b08346ebe57d6fcdc1 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 23 May 2023 23:28:41 +0100 Subject: [PATCH 50/94] Reorder code. --- .../sourceforge/opencamera/HDRProcessor.java | 135 +++++++++--------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 0c84e067e..635d148d5 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -650,6 +650,74 @@ public class HDRProcessor { // write new hdr image + float max_possible_value = response_functions[0].parameter_A * 255 + response_functions[0].parameter_B; + //float max_possible_value = response_functions[base_bitmap - 1].parameter_A * 255 + response_functions[base_bitmap - 1].parameter_B; + if( MyDebug.LOG ) + Log.d(TAG, "max_possible_value: " + max_possible_value); + if( max_possible_value < 255.0f ) { + max_possible_value = 255.0f; // don't make dark images too bright, see below about linear_scale for more details + if( MyDebug.LOG ) + Log.d(TAG, "clamp max_possible_value to: " + max_possible_value); + } + + //hdr_alpha = 0.0f; // test + //final float tonemap_scale_c = avg_luminance / 0.8f; // lower values tend to result in too dark pictures; higher values risk over exposed bright areas + //final float tonemap_scale_c = 255.0f; + //final float tonemap_scale_c = 255.0f - median_brightness; + float tonemap_scale_c = 255.0f; + + int median_target = getBrightnessTarget(median_brightness, 2, 119); + + if( MyDebug.LOG ) { + Log.d(TAG, "median_target: " + median_target); + Log.d(TAG, "compare: " + 255.0f / max_possible_value); + Log.d(TAG, "to: " + (((float)median_target)/(float)median_brightness + median_target / 255.0f - 1.0f)); + } + if( 255.0f / max_possible_value < ((float)median_target)/(float)median_brightness + median_target / 255.0f - 1.0f ) { + // For Reinhard tonemapping: + // As noted below, we have f(V) = V.S / (V+C), where V is the HDR value, C is tonemap_scale_c + // and S = (Vmax + C)/Vmax (see below) + // Ideally we try to choose C such that we map median value M to target T: + // f(M) = T + // => T = M . (Vmax + C) / (Vmax . (M + C)) + // => (T/M).(M + C) = (Vmax + C) / Vmax = 1 + C/Vmax + // => C . ( T/M - 1/Vmax ) = 1 - T + // => C = (1-T) / (T/M - 1/Vmax) + // Since we want C <= 1, we must have: + // 1-T <= T/M - 1/Vmax + // => 1/Vmax <= T/M + T - 1 + // If this isn't the case, we set C to 1 (to preserve the median as close as possible). + // Note that if we weren't doing the linear scaling below, this would reduce to choosing + // C = M(1-T)/T. We also tend to that as max_possible_value tends to infinity. So even though + // we only sometimes enter this case, it's important for cases where max_possible_value + // might be estimated too large (also consider that if we ever support more than 3 images, + // we'd risk having too large values). + // If T=M, then this simplifies to C = 1-M. + // I've tested that using "C = 1-M" always (and no linear scaling) also gives good results: + // much better compared to Open Camera 1.39, though not quite as good as doing both this + // and linear scaling (testHDR18, testHDR26, testHDR32 look too grey and/or bright). + final float tonemap_denom = ((float)median_target)/(float)median_brightness - (255.0f / max_possible_value); + if( MyDebug.LOG ) + Log.d(TAG, "tonemap_denom: " + tonemap_denom); + if( tonemap_denom != 0.0f ) { // just in case + tonemap_scale_c = (255.0f - median_target) / tonemap_denom; + if( MyDebug.LOG ) + Log.d(TAG, "tonemap_scale_c (before setting min): " + tonemap_scale_c); + /*if( tonemap_scale_c < 0.5f*255.0f ) { + throw new RuntimeException("tonemap_scale_c: " + tonemap_scale_c); + }*/ + // important to set a min value, see testHDR58, testHDR59, testHDR60 - at least 0.25, but 0.5 works better: + //tonemap_scale_c = Math.max(tonemap_scale_c, 0.25f*255.0f); + tonemap_scale_c = Math.max(tonemap_scale_c, 0.5f*255.0f); + } + //throw new RuntimeException(); // test + } + // Higher tonemap_scale_c values means darker results from the Reinhard tonemapping. + // Colours brighter than 255-tonemap_scale_c will be made darker, colours darker than 255-tonemap_scale_c will be made brighter + // (tonemap_scale_c==255 means therefore that colours will only be made darker). + if( MyDebug.LOG ) + Log.d(TAG, "tonemap_scale_c: " + tonemap_scale_c); + // create RenderScript /*if( processHDRScript == null ) { processHDRScript = new ScriptC_process_hdr(rs); @@ -752,73 +820,6 @@ public class HDRProcessor { break; } - float max_possible_value = response_functions[0].parameter_A * 255 + response_functions[0].parameter_B; - //float max_possible_value = response_functions[base_bitmap - 1].parameter_A * 255 + response_functions[base_bitmap - 1].parameter_B; - if( MyDebug.LOG ) - Log.d(TAG, "max_possible_value: " + max_possible_value); - if( max_possible_value < 255.0f ) { - max_possible_value = 255.0f; // don't make dark images too bright, see below about linear_scale for more details - if( MyDebug.LOG ) - Log.d(TAG, "clamp max_possible_value to: " + max_possible_value); - } - - //hdr_alpha = 0.0f; // test - //final float tonemap_scale_c = avg_luminance / 0.8f; // lower values tend to result in too dark pictures; higher values risk over exposed bright areas - //final float tonemap_scale_c = 255.0f; - //final float tonemap_scale_c = 255.0f - median_brightness; - float tonemap_scale_c = 255.0f; - - int median_target = getBrightnessTarget(median_brightness, 2, 119); - - if( MyDebug.LOG ) { - Log.d(TAG, "median_target: " + median_target); - Log.d(TAG, "compare: " + 255.0f / max_possible_value); - Log.d(TAG, "to: " + (((float)median_target)/(float)median_brightness + median_target / 255.0f - 1.0f)); - } - if( 255.0f / max_possible_value < ((float)median_target)/(float)median_brightness + median_target / 255.0f - 1.0f ) { - // For Reinhard tonemapping: - // As noted below, we have f(V) = V.S / (V+C), where V is the HDR value, C is tonemap_scale_c - // and S = (Vmax + C)/Vmax (see below) - // Ideally we try to choose C such that we map median value M to target T: - // f(M) = T - // => T = M . (Vmax + C) / (Vmax . (M + C)) - // => (T/M).(M + C) = (Vmax + C) / Vmax = 1 + C/Vmax - // => C . ( T/M - 1/Vmax ) = 1 - T - // => C = (1-T) / (T/M - 1/Vmax) - // Since we want C <= 1, we must have: - // 1-T <= T/M - 1/Vmax - // => 1/Vmax <= T/M + T - 1 - // If this isn't the case, we set C to 1 (to preserve the median as close as possible). - // Note that if we weren't doing the linear scaling below, this would reduce to choosing - // C = M(1-T)/T. We also tend to that as max_possible_value tends to infinity. So even though - // we only sometimes enter this case, it's important for cases where max_possible_value - // might be estimated too large (also consider that if we ever support more than 3 images, - // we'd risk having too large values). - // If T=M, then this simplifies to C = 1-M. - // I've tested that using "C = 1-M" always (and no linear scaling) also gives good results: - // much better compared to Open Camera 1.39, though not quite as good as doing both this - // and linear scaling (testHDR18, testHDR26, testHDR32 look too grey and/or bright). - final float tonemap_denom = ((float)median_target)/(float)median_brightness - (255.0f / max_possible_value); - if( MyDebug.LOG ) - Log.d(TAG, "tonemap_denom: " + tonemap_denom); - if( tonemap_denom != 0.0f ) { // just in case - tonemap_scale_c = (255.0f - median_target) / tonemap_denom; - if( MyDebug.LOG ) - Log.d(TAG, "tonemap_scale_c (before setting min): " + tonemap_scale_c); - /*if( tonemap_scale_c < 0.5f*255.0f ) { - throw new RuntimeException("tonemap_scale_c: " + tonemap_scale_c); - }*/ - // important to set a min value, see testHDR58, testHDR59, testHDR60 - at least 0.25, but 0.5 works better: - //tonemap_scale_c = Math.max(tonemap_scale_c, 0.25f*255.0f); - tonemap_scale_c = Math.max(tonemap_scale_c, 0.5f*255.0f); - } - //throw new RuntimeException(); // test - } - // Higher tonemap_scale_c values means darker results from the Reinhard tonemapping. - // Colours brighter than 255-tonemap_scale_c will be made darker, colours darker than 255-tonemap_scale_c will be made brighter - // (tonemap_scale_c==255 means therefore that colours will only be made darker). - if( MyDebug.LOG ) - Log.d(TAG, "tonemap_scale_c: " + tonemap_scale_c); processHDRScript.set_tonemap_scale(tonemap_scale_c); // algorithm specific parameters -- GitLab From 183aba6734453a1a0d212da919edb05992af6f5e Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 23 May 2023 23:51:25 +0100 Subject: [PATCH 51/94] More reordering/refactoring. --- .../sourceforge/opencamera/HDRProcessor.java | 104 ++++++++++-------- app/src/main/rs/process_hdr.rs | 4 +- 2 files changed, 60 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 635d148d5..8f8a609b3 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -718,6 +718,62 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "tonemap_scale_c: " + tonemap_scale_c); + // algorithm specific parameters + float linear_scale = 0.0f; + float W = 0.0f; + switch( tonemapping_algorithm ) { + case TONEMAPALGORITHM_EXPONENTIAL: + { + // The basic algorithm is f(V) = 1 - exp( - E * V ), where V is the HDR value, E is a + // constant. This maps [0, infinity] to [0, 1]. However we have an estimate of the maximum + // possible value, Vmax, so we can set a linear scaling S so that [0, Vmax] maps to [0, 1] + // f(V) = S . (1 - exp( - E * V )) + // so 1 = S . (1 - exp( - E * Vmax )) + // => S = 1 / (1 - exp( - E * Vmax )) + // Note that Vmax should be set to a minimum of 255, else we'll make darker images brighter. + final float exposure = 1.2f; // should match setting in process_hdr.rs + linear_scale = (float)(1.0 / (1.0 - Math.exp(-exposure * max_possible_value / 255.0))); + if( MyDebug.LOG ) + Log.d(TAG, "linear_scale: " + linear_scale); + break; + } + case TONEMAPALGORITHM_REINHARD: { + // The basic algorithm is f(V) = V / (V+C), where V is the HDR value, C is tonemap_scale_c + // This was used until Open Camera 1.39, but has the problem of making images too dark: it + // maps [0, infinity] to [0, 1], but since in practice we never have very large V values, we + // won't use the full [0, 1] range. So we apply a linear scale S: + // f(V) = V.S / (V+C) + // S is chosen such that the maximum possible value, Vmax, maps to 1. So: + // 1 = Vmax . S / (Vmax + C) + // => S = (Vmax + C)/Vmax + // Note that we don't actually know the maximum HDR value, but instead we estimate it with + // max_possible_value, which gives the maximum value we'd have if even the darkest image was + // 255.0. + // Note that if max_possible_value was less than 255, we'd end up scaling a max value less than + // 1, to [0, 1], i.e., making dark images brighter, which we don't want, which is why above we + // set max_possible_value to a minimum of 255. In practice, this is unlikely to ever happen + // since max_possible_value is calculated as a maximum possible based on the response functions + // (as opposed to the real brightest HDR value), so even for dark photos we'd expect to have + // max_possible_value >= 255. + // Note that the original Reinhard tonemapping paper describes a non-linear scaling by (1 + CV/Vmax^2), + // though this is poorer performance (in terms of calculation time). + linear_scale = (max_possible_value + tonemap_scale_c) / max_possible_value; + if( MyDebug.LOG ) + Log.d(TAG, "linear_scale: " + linear_scale); + break; + } + case TONEMAPALGORITHM_FU2: + { + // For FU2, we have f(V) = U(EV) / U(W), where V is the HDR value, U is a function. + // We want f(Vmax) = 1, so EVmax = W + final float fu2_exposure_bias = 2.0f / 255.0f; // should match setting in process_hdr.rs + W = fu2_exposure_bias * max_possible_value; + if( MyDebug.LOG ) + Log.d(TAG, "fu2 W: " + W); + break; + } + } + // create RenderScript /*if( processHDRScript == null ) { processHDRScript = new ScriptC_process_hdr(rs); @@ -822,58 +878,14 @@ public class HDRProcessor { processHDRScript.set_tonemap_scale(tonemap_scale_c); - // algorithm specific parameters + // set algorithm specific parameters switch( tonemapping_algorithm ) { case TONEMAPALGORITHM_EXPONENTIAL: - { - // The basic algorithm is f(V) = 1 - exp( - E * V ), where V is the HDR value, E is a - // constant. This maps [0, infinity] to [0, 1]. However we have an estimate of the maximum - // possible value, Vmax, so we can set a linear scaling S so that [0, Vmax] maps to [0, 1] - // f(V) = S . (1 - exp( - E * V )) - // so 1 = S . (1 - exp( - E * Vmax )) - // => S = 1 / (1 - exp( - E * Vmax )) - // Note that Vmax should be set to a minimum of 255, else we'll make darker images brighter. - float E = processHDRScript.get_exposure(); - float linear_scale = (float)(1.0 / (1.0 - Math.exp(-E * max_possible_value / 255.0))); - if( MyDebug.LOG ) - Log.d(TAG, "linear_scale: " + linear_scale); - processHDRScript.set_linear_scale(linear_scale); - break; - } - case TONEMAPALGORITHM_REINHARD: { - // The basic algorithm is f(V) = V / (V+C), where V is the HDR value, C is tonemap_scale_c - // This was used until Open Camera 1.39, but has the problem of making images too dark: it - // maps [0, infinity] to [0, 1], but since in practice we never have very large V values, we - // won't use the full [0, 1] range. So we apply a linear scale S: - // f(V) = V.S / (V+C) - // S is chosen such that the maximum possible value, Vmax, maps to 1. So: - // 1 = Vmax . S / (Vmax + C) - // => S = (Vmax + C)/Vmax - // Note that we don't actually know the maximum HDR value, but instead we estimate it with - // max_possible_value, which gives the maximum value we'd have if even the darkest image was - // 255.0. - // Note that if max_possible_value was less than 255, we'd end up scaling a max value less than - // 1, to [0, 1], i.e., making dark images brighter, which we don't want, which is why above we - // set max_possible_value to a minimum of 255. In practice, this is unlikely to ever happen - // since max_possible_value is calculated as a maximum possible based on the response functions - // (as opposed to the real brightest HDR value), so even for dark photos we'd expect to have - // max_possible_value >= 255. - // Note that the original Reinhard tonemapping paper describes a non-linear scaling by (1 + CV/Vmax^2), - // though this is poorer performance (in terms of calculation time). - float linear_scale = (max_possible_value + tonemap_scale_c) / max_possible_value; - if( MyDebug.LOG ) - Log.d(TAG, "linear_scale: " + linear_scale); + case TONEMAPALGORITHM_REINHARD: processHDRScript.set_linear_scale(linear_scale); break; - } case TONEMAPALGORITHM_FU2: { - // For FU2, we have f(V) = U(EV) / U(W), where V is the HDR value, U is a function. - // We want f(Vmax) = 1, so EVmax = W - float E = processHDRScript.get_fu2_exposure_bias(); - float W = E * max_possible_value; - if( MyDebug.LOG ) - Log.d(TAG, "fu2 W: " + W); processHDRScript.set_W(W); break; } diff --git a/app/src/main/rs/process_hdr.rs b/app/src/main/rs/process_hdr.rs index f1f278779..877ce1ecb 100644 --- a/app/src/main/rs/process_hdr.rs +++ b/app/src/main/rs/process_hdr.rs @@ -43,13 +43,13 @@ const int tonemap_algorithm_aces_c = 4; int tonemap_algorithm = tonemap_algorithm_reinhard_c; -// for Exponential: +// for Exponential; should match setting in HDRProcessor.java: const float exposure = 1.2f; // for Reinhard: float tonemap_scale = 1.0f; -// for FU2: +// for FU2; should match setting in HDRProcessor.java: const float fu2_exposure_bias = 2.0f / 255.0f; float W = 11.2f; -- GitLab From 4b26c9fbc95489e187d50665efb7d45de5c713d2 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 28 May 2023 22:03:01 +0100 Subject: [PATCH 52/94] Refactor to use string constants. --- .../net/sourceforge/opencamera/MyPreferenceFragment.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java index 048442bb0..2765eb6d8 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java @@ -195,7 +195,7 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared Log.d(TAG, "supports_face_detection: " + supports_face_detection); if( !supports_face_detection ) { - Preference pref = findPreference("preference_face_detection"); + Preference pref = findPreference(PreferenceKeys.FaceDetectionPreferenceKey); PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_category_camera_controls"); pg.removePreference(pref); @@ -732,11 +732,11 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared Log.d(TAG, "supports_tonemap_curve: " + supports_tonemap_curve); } if( !supports_tonemap_curve ) { - Preference pref = findPreference("preference_video_log"); + Preference pref = findPreference(PreferenceKeys.VideoLogPreferenceKey); PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_video_settings"); pg.removePreference(pref); - pref = findPreference("preference_video_profile_gamma"); + pref = findPreference(PreferenceKeys.VideoProfileGammaPreferenceKey); pg = (PreferenceGroup)this.findPreference("preference_screen_video_settings"); pg.removePreference(pref); } -- GitLab From 0c4ca8fc1d558d58a395f3af2391f6780c434e78 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 28 May 2023 22:16:09 +0100 Subject: [PATCH 53/94] Should still show some settings if camera not open. --- _docs/history.html | 3 ++ .../sourceforge/opencamera/MainActivity.java | 1 + .../opencamera/MyPreferenceFragment.java | 35 ++++++++++++++++--- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index d7fd9e1a4..df503b8ec 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -56,6 +56,9 @@ FIXED Problem where clicking on gallery icon would sometimes go to a "base" im FIXED Collapse notification panel when launching from a quick settings tile. FIXED Some info toasts weren't showing (e.g., when cancelling SAF dialog, or denying location permission). +FIXED Problem where if setting Video Picture Profiles to non-default value caused camera to fail + to open, the Video Picture Profiles setting would no longer show to be able to set back to + default. UPDATED Improved performance for NR photo mode. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java index 1b5ec856b..598c2a10a 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java @@ -2592,6 +2592,7 @@ public class MainActivity extends AppCompatActivity { Bundle bundle = new Bundle(); bundle.putInt("cameraId", this.preview.getCameraId()); bundle.putInt("nCameras", preview.getCameraControllerManager().getNumberOfCameras()); + bundle.putBoolean("camera_open", this.preview.getCameraController() != null); bundle.putString("camera_api", this.preview.getCameraAPI()); bundle.putBoolean("using_android_l", this.preview.usingCamera2API()); if( this.preview.getCameraController() != null ) { diff --git a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java index 2765eb6d8..a47946818 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java @@ -102,6 +102,10 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared if( MyDebug.LOG ) Log.d(TAG, "nCameras: " + nCameras); + final boolean camera_open = bundle.getBoolean("camera_open"); + if( MyDebug.LOG ) + Log.d(TAG, "camera_open: " + camera_open); + final String camera_api = bundle.getString("camera_api"); final String photo_mode_string = bundle.getString("photo_mode_string"); @@ -150,7 +154,11 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared } if( MyDebug.LOG ) Log.d(TAG, "has_antibanding?: " + has_antibanding); - if( !has_antibanding ) { + if( !has_antibanding && ( camera_open || sharedPreferences.getString(PreferenceKeys.AntiBandingPreferenceKey, CameraController.ANTIBANDING_DEFAULT).equals(CameraController.ANTIBANDING_DEFAULT) ) ) { + // if camera not open, we'll think this setting isn't supported - but should only remove + // this preference if it's set to the default (otherwise if user sets to a non-default + // value that causes camera to not open, user won't be able to put it back to the + // default!) Preference pref = findPreference(PreferenceKeys.AntiBandingPreferenceKey); PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_processing_settings"); pg.removePreference(pref); @@ -167,7 +175,11 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared } if( MyDebug.LOG ) Log.d(TAG, "has_edge_mode?: " + has_edge_mode); - if( !has_edge_mode ) { + if( !has_edge_mode && ( camera_open || sharedPreferences.getString(PreferenceKeys.EdgeModePreferenceKey, CameraController.EDGE_MODE_DEFAULT).equals(CameraController.EDGE_MODE_DEFAULT) ) ) { + // if camera not open, we'll think this setting isn't supported - but should only remove + // this preference if it's set to the default (otherwise if user sets to a non-default + // value that causes camera to not open, user won't be able to put it back to the + // default!) Preference pref = findPreference(PreferenceKeys.EdgeModePreferenceKey); PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_processing_settings"); pg.removePreference(pref); @@ -184,7 +196,11 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared } if( MyDebug.LOG ) Log.d(TAG, "has_noise_reduction_mode?: " + has_noise_reduction_mode); - if( !has_noise_reduction_mode ) { + if( !has_noise_reduction_mode && ( camera_open || sharedPreferences.getString(PreferenceKeys.CameraNoiseReductionModePreferenceKey, CameraController.NOISE_REDUCTION_MODE_DEFAULT).equals(CameraController.NOISE_REDUCTION_MODE_DEFAULT) ) ) { + // if camera not open, we'll think this setting isn't supported - but should only remove + // this preference if it's set to the default (otherwise if user sets to a non-default + // value that causes camera to not open, user won't be able to put it back to the + // default!) Preference pref = findPreference(PreferenceKeys.CameraNoiseReductionModePreferenceKey); PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_processing_settings"); pg.removePreference(pref); @@ -194,7 +210,11 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared if( MyDebug.LOG ) Log.d(TAG, "supports_face_detection: " + supports_face_detection); - if( !supports_face_detection ) { + if( !supports_face_detection && ( camera_open || sharedPreferences.getBoolean(PreferenceKeys.FaceDetectionPreferenceKey, false) == false ) ) { + // if camera not open, we'll think this setting isn't supported - but should only remove + // this preference if it's set to the default (otherwise if user sets to a non-default + // value that causes camera to not open, user won't be able to put it back to the + // default!) Preference pref = findPreference(PreferenceKeys.FaceDetectionPreferenceKey); PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_category_camera_controls"); pg.removePreference(pref); @@ -731,7 +751,12 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared Log.d(TAG, "tonemap_max_curve_points: " + tonemap_max_curve_points); Log.d(TAG, "supports_tonemap_curve: " + supports_tonemap_curve); } - if( !supports_tonemap_curve ) { + if( !supports_tonemap_curve && ( camera_open || sharedPreferences.getString(PreferenceKeys.VideoLogPreferenceKey, "off").equals("off") ) ) { + // if camera not open, we'll think this setting isn't supported - but should only remove + // this preference if it's set to the default (otherwise if user sets to a non-default + // value that causes camera to not open, user won't be able to put it back to the + // default!) + // (needed for Pixel 6 Pro where setting to sRGB causes camera to fail to open when in video mode) Preference pref = findPreference(PreferenceKeys.VideoLogPreferenceKey); PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_video_settings"); pg.removePreference(pref); -- GitLab From eec4c7185ecf5bd88489b9a7b0d51b35bc0cd931 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 28 May 2023 22:26:26 +0100 Subject: [PATCH 54/94] Allow trying to switch between photo and video mode if camera fails to open. --- _docs/history.html | 2 ++ .../java/net/sourceforge/opencamera/preview/Preview.java | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/_docs/history.html b/_docs/history.html index df503b8ec..b3a817d22 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -59,6 +59,8 @@ FIXED Some info toasts weren't showing (e.g., when cancelling SAF dialog, or d FIXED Problem where if setting Video Picture Profiles to non-default value caused camera to fail to open, the Video Picture Profiles setting would no longer show to be able to set back to default. +FIXED Allow trying to switch between photo and video mode if camera fails to open (in some case + the failure may be specific to the mode). UPDATED Improved performance for NR photo mode. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index 712242511..d9f418d3f 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -4651,7 +4651,11 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu public void switchVideo(boolean during_startup, boolean change_user_pref) { if( MyDebug.LOG ) Log.d(TAG, "switchVideo()"); - if( camera_controller == null ) { + if( camera_controller == null && during_startup ) { + // if during_startup==false at least, we should allow switching to/from video mode if + // camera failed to open (it may be that the failure to open is specific to video mode + // for example, so should allow user to switch back to photo mode - e.g., setting + // video profile to sRGB on Pixel 6 Pro) if( MyDebug.LOG ) Log.d(TAG, "camera not opened!"); return; -- GitLab From 8f9c823de6a65e1ebd3b4fb9d1873be990c069eb Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 28 May 2023 22:26:34 +0100 Subject: [PATCH 55/94] Comment out some logging. --- .../main/java/net/sourceforge/opencamera/ui/DrawPreview.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java index 0376bf818..3476af83b 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java +++ b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java @@ -2787,11 +2787,11 @@ public class DrawPreview { float frac = ((time_now - camera_inactive_time_ms) / (float)dim_effect_time_c); frac = Math.min(frac, 1.0f); int alpha = (int)(frac * 127); - if( MyDebug.LOG ) { + /*if( MyDebug.LOG ) { Log.d(TAG, "time diff: " + (time_now - camera_inactive_time_ms)); Log.d(TAG, " frac: " + frac); Log.d(TAG, " alpha: " + alpha); - } + }*/ p.setColor(Color.BLACK); p.setAlpha(alpha); canvas.drawRect(0.0f, 0.0f, canvas.getWidth(), canvas.getHeight(), p); -- GitLab From d7930ab8e50030e2ff727f3e3479a49a0047b9d3 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 31 May 2023 21:48:15 +0200 Subject: [PATCH 56/94] Support for zoom with camera vendor extensions. --- _docs/help.html | 2 +- _docs/history.html | 1 + .../opencamera/MyApplicationInterface.java | 7 +++-- .../cameracontroller/CameraController.java | 1 + .../cameracontroller/CameraController2.java | 31 ++++++++++++++++--- .../opencamera/preview/Preview.java | 12 +++++-- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/_docs/help.html b/_docs/help.html index fad18f4a9..db5f1b2d9 100644 --- a/_docs/help.html +++ b/_docs/help.html @@ -241,7 +241,7 @@ will be available which instead work by making the screen light up (note, front
      • X-Bokeh: Blurs the background of photos. This is typically intended when taking portraits of people.
      • X-Bty: Face retouch or "beauty", applies cosmetic effects to people's faces.
      - Note many features are unavailable when using an extension mode, including flash, zoom and manual controls. + Note many features may be unavailable when using an extension mode, including flash, zoom and manual controls.
  • Auto-level - Enable the auto-level feature for photos (see diff --git a/_docs/history.html b/_docs/history.html index b3a817d22..e7dde43f0 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -61,6 +61,7 @@ FIXED Problem where if setting Video Picture Profiles to non-default value cau default. FIXED Allow trying to switch between photo and video mode if camera fails to open (in some case the failure may be specific to the mode). +ADDED Support for zoom with camera vendor extensions (for supported Android 13+ devices). UPDATED Improved performance for NR photo mode. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for diff --git a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java index 2ac39b8cc..430bd5a3c 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java @@ -1837,9 +1837,12 @@ public class MyApplicationInterface extends BasicApplicationInterface { @Override public boolean allowZoom() { - if( getPhotoMode() == PhotoMode.Panorama || isCameraExtensionPref() ) { + if( getPhotoMode() == PhotoMode.Panorama ) { // don't allow zooming in panorama mode, the algorithm isn't set up to support this! - // zoom also not supported for camera extensions + return false; + } + else if( isCameraExtensionPref() && !main_activity.getPreview().supportsZoomForCameraExtension(getCameraExtensionPref()) ) { + // zoom not supported for camera extension return false; } return true; diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java index 043613f1b..64d1c83ac 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController.java @@ -67,6 +67,7 @@ public abstract class CameraController { public List video_sizes_high_speed; // may be null if high speed not supported public List preview_sizes; public List supported_extensions; // if non-null, list of supported camera vendor extensions, see https://developer.android.com/reference/android/hardware/camera2/CameraExtensionCharacteristics + public List supported_extensions_zoom; // if non-null, list of camera vendor extensions that support zoom public List supported_flash_values; public List supported_focus_values; public float [] apertures; // may be null if not supported, else will have at least 2 values diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java index 478942fe0..d0b110828 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java @@ -11,6 +11,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Queue; +import java.util.Set; import java.util.concurrent.Executor; import android.app.Activity; @@ -86,6 +87,7 @@ public class CameraController2 extends CameraController { private List zoom_ratios; private int current_zoom_value; private int zoom_value_1x; // index into zoom_ratios list that is for zoom 1x + private List supported_extensions_zoom; // if non-null, list of camera vendor extensions that support zoom private boolean supports_face_detect_mode_simple; private boolean supports_face_detect_mode_full; private boolean supports_optical_stabilization; @@ -835,10 +837,7 @@ public class CameraController2 extends CameraController { } private void setControlZoomRatio(CaptureRequest.Builder builder) { - if( sessionType == SessionType.SESSIONTYPE_EXTENSION ) { - // don't set for extensions - } - else if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && has_control_zoom_ratio ) { + if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && has_control_zoom_ratio ) { builder.set(CaptureRequest.CONTROL_ZOOM_RATIO, control_zoom_ratio); } } @@ -2991,6 +2990,7 @@ public class CameraController2 extends CameraController { List extensions = extension_characteristics.getSupportedExtensions(); if( extensions != null ) { camera_features.supported_extensions = new ArrayList<>(); + camera_features.supported_extensions_zoom = new ArrayList<>(); for(int extension : extensions) { if( MyDebug.LOG ) Log.d(TAG, "vendor extension: " + extension); @@ -3045,10 +3045,23 @@ public class CameraController2 extends CameraController { if( MyDebug.LOG ) Log.d(TAG, " extension is supported: " + extension); camera_features.supported_extensions.add(extension); + + if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ) { + Set extension_supported_request_keys = extension_characteristics.getAvailableCaptureRequestKeys(extension); + for(CaptureRequest.Key key : extension_supported_request_keys) { + if( MyDebug.LOG ) + Log.d(TAG, " supported capture request key: " + key.getName()); + if( key == CaptureRequest.CONTROL_ZOOM_RATIO ) { + camera_features.supported_extensions_zoom.add(extension); + } + } + } } } } } + // save to local fields: + this.supported_extensions_zoom = camera_features.supported_extensions_zoom; if( characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ) { int [] supported_flash_modes_arr = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES); // Android format @@ -4742,6 +4755,16 @@ public class CameraController2 extends CameraController { Log.d(TAG, "zoom not supported"); return; } + if( sessionType == SessionType.SESSIONTYPE_EXTENSION ) { + if( this.supported_extensions_zoom != null && this.supported_extensions_zoom.contains(camera_extension) ) { + // fine, camera extension supports zoom + } + else { + if( MyDebug.LOG ) + Log.d(TAG, "zoom not supported for camera extension"); + return; + } + } if( value < 0 || value > zoom_ratios.size() ) { if( MyDebug.LOG ) Log.e(TAG, "invalid zoom value" + value); diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index d9f418d3f..b63df1e7a 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -332,6 +332,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu private int current_size_index = -1; // this is an index into the sizes array, or -1 if sizes not yet set public List supported_extensions; // if non-null, list of supported camera vendor extensions, see https://developer.android.com/reference/android/hardware/camera2/CameraExtensionCharacteristics + public List supported_extensions_zoom; // if non-null, list of camera vendor extensions that support zoom private boolean supports_video; private boolean has_capture_rate_factor; // whether we have a capture rate for faster (timelapse) or slow motion @@ -2346,11 +2347,12 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu this.video_quality_handler.setVideoSizesHighSpeed(camera_features.video_sizes_high_speed); this.supported_preview_sizes = camera_features.preview_sizes; this.supported_extensions = camera_features.supported_extensions; + this.supported_extensions_zoom = camera_features.supported_extensions_zoom; // need to do zoom last, as applicationInterface.allowZoom() may depend on the supported - // camera features (e.g., zoom not supported with camera extensions, so we need to have first + // camera features (e.g., zoom not necessarily supported with camera extensions, so we need to have first // stored supported_extensions - otherwise starting up in an extension photo mode will still - // show zoom controls) + // show zoom controls even if zoom not supported) this.camera_controller_supports_zoom = camera_features.is_zoom_supported; this.has_zoom = camera_features.is_zoom_supported && applicationInterface.allowZoom(); if( this.has_zoom ) { @@ -7273,6 +7275,12 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu return this.supported_extensions != null && this.supported_extensions.contains(extension); } + /** Whether the camera vendor extensions supports zoom. + */ + public boolean supportsZoomForCameraExtension(int extension) { + return this.supported_extensions_zoom != null && this.supported_extensions_zoom.contains(extension); + } + public boolean supportsRaw() { return this.supports_raw; } -- GitLab From 13e8cbb035c011c21aa3159699a291f5093c0566 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 31 May 2023 22:06:37 +0200 Subject: [PATCH 57/94] No longer cancel panorama when moving device orientation too far in wrong direction. --- _docs/history.html | 1 + .../sourceforge/opencamera/MyApplicationInterface.java | 5 +++-- .../java/net/sourceforge/opencamera/ui/DrawPreview.java | 8 ++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index e7dde43f0..8938e4498 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -66,6 +66,7 @@ UPDATED Improved performance for NR photo mode. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for Android 13+). +UPDATED No longer cancel panorama when moving device orientation too far in wrong direction. Version 1.51.1 (2023/01/02) diff --git a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java index 430bd5a3c..7c3108e5a 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java @@ -1995,10 +1995,11 @@ public class MyApplicationInterface extends BasicApplicationInterface { if( MyDebug.LOG ) Log.d(TAG, "TargetCallback.onTooFar"); - if( !main_activity.is_test ) { + // it's better not to cancel the panorama if the user moves the device too far in wrong direction + /*if( !main_activity.is_test ) { main_activity.getPreview().showToast(null, R.string.panorama_cancelled, true); MyApplicationInterface.this.stopPanorama(true); - } + }*/ } }); diff --git a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java index 3476af83b..47d04164e 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java +++ b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java @@ -3051,6 +3051,14 @@ public class DrawPreview { float radius = (radius_dp * scale + 0.5f); // convert dps to pixels float cx = canvas.getWidth()/2.0f + distance_x; float cy = canvas.getHeight()/2.0f + distance_y; + + // if gyro spots would be outside the field of view, it's still better to show them on the + // border of the canvas, so the user knows which direction to move the device + cx = Math.max(cx, 0.0f); + cx = Math.min(cx, canvas.getWidth()); + cy = Math.max(cy, 0.0f); + cy = Math.min(cy, canvas.getHeight()); + canvas.drawCircle(cx, cy, radius, p); p.setAlpha(255); p.setStyle(Paint.Style.FILL); // reset -- GitLab From 229c8dd586d696a999dd1531e02cb67913a7229d Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 1 Jun 2023 18:23:29 +0200 Subject: [PATCH 58/94] Support for displaying on-screen ISO and exposure time with camera vendor extensions. --- _docs/history.html | 2 ++ .../cameracontroller/CameraController2.java | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/_docs/history.html b/_docs/history.html index 8938e4498..aee2ca444 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -62,6 +62,8 @@ FIXED Problem where if setting Video Picture Profiles to non-default value cau FIXED Allow trying to switch between photo and video mode if camera fails to open (in some case the failure may be specific to the mode). ADDED Support for zoom with camera vendor extensions (for supported Android 13+ devices). +ADDED Support for displaying on-screen ISO and exposure time with camera vendor extensions (for + supported Android 13+ devices). UPDATED Improved performance for NR photo mode. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java index d0b110828..2ff8a7e15 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java @@ -3055,6 +3055,11 @@ public class CameraController2 extends CameraController { camera_features.supported_extensions_zoom.add(extension); } } + Set extension_supported_result_keys = extension_characteristics.getAvailableCaptureResultKeys(extension); + for(CaptureResult.Key key : extension_supported_result_keys) { + if( MyDebug.LOG ) + Log.d(TAG, " supported capture result key: " + key.getName()); + } } } } @@ -7915,7 +7920,10 @@ public class CameraController2 extends CameraController { // for previewCaptureCallback, we set has_received_frame in onCaptureCompleted(), but // that method doesn't exist for ExtensionCaptureCallback, and the other methods such as - // onCaptureSequenceCompleted aren't called for the preview captures + // onCaptureSequenceCompleted aren't called for the preview captures; + // onCaptureResultAvailable meanwhile is only called if + // CameraExtensionCharacteristics.getAvailableCaptureResultKeys() returns a non-empty + // list if( !has_received_frame ) { has_received_frame = true; if( MyDebug.LOG ) @@ -7968,6 +7976,11 @@ public class CameraController2 extends CameraController { } super.onCaptureSequenceAborted(session, sequenceId); } + + @Override + public void onCaptureResultAvailable(@NonNull CameraExtensionSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { + previewCaptureCallback.updateCachedCaptureResult(result); + } } private final MyCaptureCallback previewCaptureCallback = new MyCaptureCallback(); -- GitLab From 196e3e175a911b9a9555c0faf619e530b7d48b2a Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 7 Jun 2023 00:29:26 +0100 Subject: [PATCH 59/94] Fix logging. --- app/src/main/java/net/sourceforge/opencamera/MainActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java index 598c2a10a..f96467438 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java @@ -1612,7 +1612,7 @@ public class MainActivity extends AppCompatActivity { if( MyDebug.LOG ) { Log.d(TAG, "onDisplayChanged: " + displayId); Log.d(TAG, "rotation: " + rotation); - Log.d(TAG, "old_rotation: " + rotation); + Log.d(TAG, "old_rotation: " + old_rotation); } if( ( rotation == Surface.ROTATION_0 && old_rotation == Surface.ROTATION_180 ) || ( rotation == Surface.ROTATION_180 && old_rotation == Surface.ROTATION_0 ) || -- GitLab From 8e4aadd0ff467d0c33ee8b33767302c6a527c856 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 10 Jun 2023 19:11:30 +0100 Subject: [PATCH 60/94] Update logging. --- .../net/sourceforge/opencamera/preview/Preview.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index b63df1e7a..1b003114f 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -906,6 +906,10 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu int previewWidth = MeasureSpec.getSize(widthSpec); int previewHeight = MeasureSpec.getSize(heightSpec); + if( MyDebug.LOG ) { + Log.d(TAG, "previewWidth: " + previewWidth); + Log.d(TAG, "previewHeight: " + previewHeight); + } // Get the padding of the border background. int hPadding = cameraSurface.getView().getPaddingLeft() + cameraSurface.getView().getPaddingRight(); @@ -3839,12 +3843,14 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu // (b) on some devices (e.g., Nokia 8), when coming back from the Settings when device is held in Preview, // display size is returned in portrait format! (To reproduce, enable "Maximise preview size"; or if that's // already enabled, change the setting off and on.) + if( MyDebug.LOG ) + Log.d(TAG, "display_size: " + display_size.x + " x " + display_size.y); if( display_size.x < display_size.y ) { //noinspection SuspiciousNameCombination display_size.set(display_size.y, display_size.x); + if( MyDebug.LOG ) + Log.d(TAG, "swapped display_size to: " + display_size.x + " x " + display_size.y); } - if( MyDebug.LOG ) - Log.d(TAG, "display_size: " + display_size.x + " x " + display_size.y); } double targetRatio = calculateTargetRatioForPreview(display_size); int targetHeight = Math.min(display_size.y, display_size.x); -- GitLab From 09204171e9427b05a345ffd38402db8d9b7995bc Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 13 Jun 2023 22:28:44 +0100 Subject: [PATCH 61/94] Set timeout of 2 second for focusing with original camera API. --- _docs/history.html | 1 + .../cameracontroller/CameraController1.java | 26 +++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index aee2ca444..3d6a532f4 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -64,6 +64,7 @@ FIXED Allow trying to switch between photo and video mode if camera fails to o ADDED Support for zoom with camera vendor extensions (for supported Android 13+ devices). ADDED Support for displaying on-screen ISO and exposure time with camera vendor extensions (for supported Android 13+ devices). +UPDATED Applied a timeout of 2 second for focusing with original camera API. UPDATED Improved performance for NR photo mode. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java index f2e3ba48f..9a1ff0df5 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController1.java @@ -1628,17 +1628,36 @@ public class CameraController1 extends CameraController { public void autoFocus(final CameraController.AutoFocusCallback cb, boolean capture_follows_autofocus_hint) { if( MyDebug.LOG ) Log.d(TAG, "autoFocus"); - Camera.AutoFocusCallback camera_cb = new Camera.AutoFocusCallback() { + class MyAutoFocusCallback implements Camera.AutoFocusCallback { boolean done_autofocus = false; + private final Handler handler = new Handler(); + private final Runnable runnable = new Runnable() { + @Override + public void run() { + if( MyDebug.LOG ) + Log.d(TAG, "autofocus timeout check"); + if( !done_autofocus ) { + Log.e(TAG, "autofocus timeout!"); + done_autofocus = true; + cb.onAutoFocus(false); + } + } + }; + + private void setTimeout() { + handler.postDelayed(runnable, 2000); // set autofocus timeout + } @Override public void onAutoFocus(boolean success, Camera camera) { if( MyDebug.LOG ) Log.d(TAG, "autoFocus.onAutoFocus"); + handler.removeCallbacks(runnable); // in theory we should only ever get one call to onAutoFocus(), but some Samsung phones at least can call the callback multiple times // see http://stackoverflow.com/questions/36316195/take-picture-fails-on-samsung-phones // needed to fix problem on Samsung S7 with flash auto/on and continuous picture focus where it would claim failed to take picture even though it'd succeeded, // because we repeatedly call takePicture(), and the subsequent ones cause a runtime exception + // update: also the done_autofocus flag is needed in case we had an autofocus timeout, see above if( !done_autofocus ) { done_autofocus = true; cb.onAutoFocus(success); @@ -1648,8 +1667,11 @@ public class CameraController1 extends CameraController { Log.e(TAG, "ignore repeated autofocus"); } } - }; + } + MyAutoFocusCallback camera_cb = new MyAutoFocusCallback(); + try { + camera_cb.setTimeout(); camera.autoFocus(camera_cb); } catch(RuntimeException e) { -- GitLab From 41096d3fc1da3ab4aa688258cf9f9b75115c7a58 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 15 Jun 2023 20:46:47 +0100 Subject: [PATCH 62/94] Aspect ratio and other fixes in split-screen and multi-window modes. --- _docs/history.html | 1 + .../opencamera/test/MainActivityTest.java | 4 +- .../sourceforge/opencamera/MainActivity.java | 70 +++++++++++++++-- .../opencamera/MyApplicationInterface.java | 5 +- .../preview/ApplicationInterface.java | 13 +++- .../preview/BasicApplicationInterface.java | 2 +- .../opencamera/preview/Preview.java | 78 +++++++++++++------ .../opencamera/ui/DrawPreview.java | 2 +- .../net/sourceforge/opencamera/ui/MainUI.java | 10 +++ 9 files changed, 151 insertions(+), 34 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index 3d6a532f4..3c880a950 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -61,6 +61,7 @@ FIXED Problem where if setting Video Picture Profiles to non-default value cau default. FIXED Allow trying to switch between photo and video mode if camera fails to open (in some case the failure may be specific to the mode). +FIXED Aspect ratio and other fixes in split-screen and multi-window modes. ADDED Support for zoom with camera vendor extensions (for supported Android 13+ devices). ADDED Support for displaying on-screen ISO and exposure time with camera vendor extensions (for supported Android 13+ devices). diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java index b646344c3..5708b0da5 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java @@ -10488,7 +10488,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2= Build.VERSION_CODES.N && isInMultiWindowMode() ) { + Point display_size = new Point(); + Display display = getWindowManager().getDefaultDisplay(); + display.getSize(display_size); + if( MyDebug.LOG ) { + Log.d(TAG, " display width: " + display_size.x); + Log.d(TAG, " display height: " + display_size.y); + Log.d(TAG, " layoutUI display width: " + mainUI.layoutUI_display_w); + Log.d(TAG, " layoutUI display height: " + mainUI.layoutUI_display_h); + } + // We need to call layoutUI when the window is resized without an orientation change - + // this can happen in split-screen or multi-window mode, where onConfigurationChanged + // is not guaranteed to be called. + // We check against the size of when layoutUI was last called, to avoid repeated calls + // when the resize is due to the device rotating and onConfigurationChanged is called - + // in fact we'd have a problem of repeatedly calling layoutUI, since doing layoutUI + // causes onLayoutChange() to be called again. + if( display_size.x != mainUI.layoutUI_display_w || display_size.y != mainUI.layoutUI_display_h ) { + if( MyDebug.LOG ) + Log.d(TAG, "call layoutUI due to resize"); + mainUI.layoutUI(); + } + } + } + }; + // set up take photo long click takePhotoButton.setOnLongClickListener(new View.OnLongClickListener() { @Override @@ -1167,6 +1200,17 @@ public class MainActivity extends AppCompatActivity { // and we want to avoid notifications hanging around cancelImageSavingNotification(); + if( want_no_limits && navigation_gap != 0 ) { + if( MyDebug.LOG ) + Log.d(TAG, "clear FLAG_LAYOUT_NO_LIMITS"); + // it's unclear why this matters - but there is a bug when exiting split-screen mode, if the split-screen mode had set want_no_limits: + // even though the application is created when leaving split-screen mode, we still end up with the window flags for showing + // under the navigation bar! + // update: this issue is also fixed by not allowing want_no_limits mode in multi-window mode, but still good to reset things here + // just in case + showUnderNavigation(false); + } + // reduce risk of losing any images // we don't do this in onPause or onStop, due to risk of ANRs // note that even if we did call this earlier in onPause or onStop, we'd still want to wait again here: as it can happen @@ -1423,6 +1467,7 @@ public class MainActivity extends AppCompatActivity { mSensorManager.registerListener(accelerometerListener, mSensorAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); magneticSensor.registerMagneticListener(mSensorManager); orientationEventListener.enable(); + getWindow().getDecorView().addOnLayoutChangeListener(layoutChangeListener); // if BLE remote control is enabled, then start the background BLE service bluetoothRemoteControl.startRemoteControl(); @@ -1556,6 +1601,7 @@ public class MainActivity extends AppCompatActivity { mSensorManager.unregisterListener(accelerometerListener); magneticSensor.unregisterMagneticListener(mSensorManager); orientationEventListener.disable(); + getWindow().getDecorView().removeOnLayoutChangeListener(layoutChangeListener); bluetoothRemoteControl.stopRemoteControl(); freeAudioListener(false); //speechControl.stopSpeechRecognizer(); @@ -1669,6 +1715,8 @@ public class MainActivity extends AppCompatActivity { // n.b., need to call this first, before preview.setCameraDisplayOrientation(), since // preview.setCameraDisplayOrientation() will call getDisplayRotation() and we don't want // to be using the outdated cached value now that the rotation has changed! + // update: no longer relevant, as preview.setCameraDisplayOrientation() now sets + // prefer_later to true to avoid using cached value. But might as well call it first anyway. resetCachedSystemOrientation(); preview.setCameraDisplayOrientation(); @@ -1797,11 +1845,14 @@ public class MainActivity extends AppCompatActivity { } /** A wrapper for getWindowManager().getDefaultDisplay().getRotation(), except if - * lock_to_landscape==false, this checks for the display being inconsistent with the system - * orientation, and if so, returns a cached value. + * lock_to_landscape==false && prefer_later==false, this uses a cached value. */ - public int getDisplayRotation() { - if( lock_to_landscape ) { + public int getDisplayRotation(boolean prefer_later) { + /*if( MyDebug.LOG ) { + Log.d(TAG, "getDisplayRotationDegrees"); + Log.d(TAG, "prefer_later: " + prefer_later); + }*/ + if( lock_to_landscape || prefer_later ) { return getWindowManager().getDefaultDisplay().getRotation(); } // we cache to reduce effect of annoying problem where rotation changes shortly before the @@ -4987,7 +5038,16 @@ public class MainActivity extends AppCompatActivity { boolean old_want_no_limits = want_no_limits; this.want_no_limits = false; - if( set_window_insets_listener ) { + if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isInMultiWindowMode() ) { + if( MyDebug.LOG ) + Log.d(TAG, "multi-window mode"); + // don't support want_no_limits mode in multi-window mode - extra complexity that the + // preview size could change from simply resizing the window; also problem that the + // navigation_gap, and whether we'd want want_no_limits, can both change depending on + // device orientation (because application can e.g. be in landscape mode even if device + // has switched to portrait) + } + else if( set_window_insets_listener ) { Point display_size = new Point(); Display display = getWindowManager().getDefaultDisplay(); display.getSize(display_size); diff --git a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java index 7c3108e5a..0e347a89e 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java @@ -1458,13 +1458,14 @@ public class MyApplicationInterface extends BasicApplicationInterface { /** Returns the ROTATION_* enum of the display relative to the natural device orientation, but * also checks for the preview being rotated due to user preference * RotatePreviewPreferenceKey. + * See ApplicationInterface.getDisplayRotation() for more details, including for prefer_later. */ @Override - public int getDisplayRotation() { + public int getDisplayRotation(boolean prefer_later) { // important to use cached rotation to reduce issues of incorrect focus square location when // rotating device, due to strange Android behaviour where rotation changes shortly before // the configuration actually changes - int rotation = main_activity.getDisplayRotation(); + int rotation = main_activity.getDisplayRotation(prefer_later); String rotate_preview = sharedPreferences.getString(PreferenceKeys.RotatePreviewPreferenceKey, "0"); if( MyDebug.LOG ) diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/ApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/preview/ApplicationInterface.java index c8b8caf4d..1a9a500cc 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/ApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/ApplicationInterface.java @@ -143,7 +143,18 @@ public interface ApplicationInterface { double getCalibratedLevelAngle(); // set to non-zero to calibrate the accelerometer used for the level angles boolean canTakeNewPhoto(); // whether taking new photos is allowed (e.g., can return false if queue for processing images would become full) boolean imageQueueWouldBlock(int n_raw, int n_jpegs); // called during some burst operations, whether we can allow taking the supplied number of extra photos - int getDisplayRotation(); // same behaviour as Activity.getWindowManager().getDefaultDisplay().getRotation() (including returning a member of Surface.ROTATION_*), but allows application to modify e.g. for upside-down preview + /** Same behaviour as Activity.getWindowManager().getDefaultDisplay().getRotation() (including + * returning a member of Surface.ROTATION_*), but allows application to modify e.g. for + * upside-down preview. + * @param prefer_later When the device orientation changes, there can be some ambiguity if this + * is called during this rotation, since getRotation() may updated shortly + * before the UI appears to rotate. If prefer_later==false, then prefer the + * previous rotation in such cases. This can be implemented by caching the + * value. prefer_later should be set to false when this is being called + * frequently e.g. as part of a UI that should smoothly rotate as the device + * rotates. prefer_later should be set to true for "one-off" calls. + */ + int getDisplayRotation(boolean prefer_later); // Camera2 only modes: long getExposureTimePref(); // only called if getISOPref() is not "default" float getFocusDistancePref(boolean is_target_distance); diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/BasicApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/preview/BasicApplicationInterface.java index c79e17bc5..ee1dbc29c 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/BasicApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/BasicApplicationInterface.java @@ -288,7 +288,7 @@ public abstract class BasicApplicationInterface implements ApplicationInterface } @Override - public int getDisplayRotation() { + public int getDisplayRotation(boolean prefer_later) { Activity activity = (Activity)this.getContext(); return activity.getWindowManager().getDefaultDisplay().getRotation(); } diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index 1b003114f..4bdb3db7c 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -533,7 +533,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu // for CameraController2, except testing on Nexus 6 shows that we shouldn't change "result" for front facing camera. boolean mirror = (camera_controller.getFacing() == CameraController.Facing.FACING_FRONT); camera_to_preview_matrix.setScale(1, mirror ? -1 : 1); - int degrees = getDisplayRotationDegrees(); + int degrees = getDisplayRotationDegrees(false); int result = (camera_controller.getCameraOrientation() - degrees + 360) % 360; if( MyDebug.LOG ) { Log.d(TAG, "orientation of display relative to natural orientation: " + degrees); @@ -919,22 +919,51 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu previewWidth -= hPadding; previewHeight -= vPadding; - boolean widthLonger = previewWidth > previewHeight; - int longSide = (widthLonger ? previewWidth : previewHeight); - int shortSide = (widthLonger ? previewHeight : previewWidth); - if( longSide > shortSide * aspect_ratio ) { - longSide = (int) ((double) shortSide * aspect_ratio); + int result; + if( camera_controller != null ) { + // We shouldn't assume that previewWidth > previewHeight means the device is in landscape + // orientation - this isn't necessarily true for split-screen or multi-window mode. + // Important to use prefer_later==true, as in split-screen or multi-window mode, we don't always get a call + // to MainActivity.onConfigurationChanged() when device orientation changes, so have no way to know to + // reset the cached rotation. But we want the latest rotation value here anyway. + int degrees = getDisplayRotationDegrees(true); + result = (camera_controller.getCameraOrientation() - degrees + 360) % 360; + if( MyDebug.LOG ) { + Log.d(TAG, "orientation of display relative to natural orientation: " + degrees); + Log.d(TAG, "orientation of display relative to camera orientation: " + result); + } } else { - shortSide = (int) ((double) longSide / aspect_ratio); + // fall back to guessing via the window dimensions + result = (previewWidth > previewHeight) ? 0 : 90; + } + if( MyDebug.LOG ) + Log.d(TAG, "aspect_ratio: " + aspect_ratio); + if( result % 180 != 0 ) { + // Usually this means the device is in portrait mode, and hence e.g. an aspect ratio of + // 4:3 should give an on-screen preview of 3:4 (since the device is rotated 90 degrees + // compared to the natural camera orientation). + // It's important to use this code instead of checking if the display is in portrait + // (or that previewWidth < previewHeight), for split-screen or multi-window displays. + // E.g., if the device orientation is in portrait, it might still be that Open Camera + // is running in landscape with previewWidth > previewHeight, because of running in + // split-screen mode, or more generally in multi-window mode where the window is resized + // to landscape orientation. + // See https://developer.android.com/training/camera2/camera-preview#relative_rotation . + aspect_ratio = 1.0f / aspect_ratio; + if( MyDebug.LOG ) + Log.d(TAG, "aspect_ratio rotated to: " + aspect_ratio); } - if( widthLonger ) { - previewWidth = longSide; - previewHeight = shortSide; + + if( previewWidth > previewHeight * aspect_ratio ) { + previewWidth = (int) ((double) previewHeight * aspect_ratio); } else { - previewWidth = shortSide; - previewHeight = longSide; + previewHeight = (int) ((double) previewWidth / aspect_ratio); + } + if( MyDebug.LOG ) { + Log.d(TAG, "previewWidth is now: " + previewWidth); + Log.d(TAG, "previewHeight is now: " + previewHeight); } // Add the padding of the border. @@ -1071,9 +1100,16 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu Log.d(TAG, "nothing to do"); return; } - if( MyDebug.LOG ) + if( MyDebug.LOG ) { Log.d(TAG, "textureview size: " + textureview_w + ", " + textureview_h); - int rotation = applicationInterface.getDisplayRotation(); + Log.d(TAG, "preview size: " + preview_w + ", " + preview_h); + } + // Important to use prefer_later==true, as in split-screen or multi-window mode, we don't always get a call + // to MainActivity.onConfigurationChanged() when device orientation changes, so have no way to know to + // reset the cached rotation. But we want the latest rotation value here anyway. + int rotation = applicationInterface.getDisplayRotation(true); + if( MyDebug.LOG ) + Log.d(TAG, "configureTransform rotation: " + rotation); Matrix matrix = new Matrix(); RectF viewRect = new RectF(0, 0, this.textureview_w, this.textureview_h); RectF bufferRect = new RectF(0, 0, this.preview_h, this.preview_w); @@ -3972,10 +4008,8 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu /** Returns the rotation in degrees of the display relative to the natural device orientation. */ - private int getDisplayRotationDegrees() { - if( MyDebug.LOG ) - Log.d(TAG, "getDisplayRotationDegrees"); - int rotation = applicationInterface.getDisplayRotation(); + private int getDisplayRotationDegrees(boolean prefer_later) { + int rotation = applicationInterface.getDisplayRotation(prefer_later); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; @@ -4004,7 +4038,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu configureTransform(); } else { - int degrees = getDisplayRotationDegrees(); + int degrees = getDisplayRotationDegrees(true); if( MyDebug.LOG ) Log.d(TAG, " degrees = " + degrees); // note the code to make the rotation relative to the camera sensor is done in camera_controller.setDisplayOrientation() @@ -8028,7 +8062,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu final int downscale = 4; int bitmap_width = textureview_w / downscale; int bitmap_height = textureview_h / downscale; - int rotation = getDisplayRotationDegrees(); + int rotation = getDisplayRotationDegrees(false); if( rotation == 90 || rotation == 270 ) { int dummy = bitmap_width; //noinspection SuspiciousNameCombination @@ -8397,7 +8431,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu // The original orientation of the bitmap we get from textureView.getBitmap() needs to be rotated to // account for the orientation of camera vs device, but not to account for the current orientation // of the device - int rotation_degrees = preview.getDisplayRotationDegrees(); + int rotation_degrees = preview.getDisplayRotationDegrees(false); /*if( MyDebug.LOG ) { Log.d(TAG, "orientation of display relative to natural orientation: " + rotation_degrees); }*/ @@ -8456,7 +8490,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu output_allocation.destroy(); // See comments above for zebra stripes - int rotation_degrees = preview.getDisplayRotationDegrees(); + int rotation_degrees = preview.getDisplayRotationDegrees(false); if( MyDebug.LOG ) Log.d(TAG, "time before creating new_focus_peaking_bitmap: " + (System.currentTimeMillis() - debug_time)); Matrix matrix = new Matrix(); diff --git a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java index 47d04164e..1147a4ba5 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java +++ b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java @@ -2281,7 +2281,7 @@ public class DrawPreview { int o_radius = (int) (10 * scale + 0.5f); // convert dps to pixels double angle = - preview.getOrigLevelAngle(); // see http://android-developers.blogspot.co.uk/2010/09/one-screen-turn-deserves-another.html - int rotation = main_activity.getDisplayRotation(); + int rotation = main_activity.getDisplayRotation(false); switch (rotation) { case Surface.ROTATION_90: angle -= 90.0; diff --git a/app/src/main/java/net/sourceforge/opencamera/ui/MainUI.java b/app/src/main/java/net/sourceforge/opencamera/ui/MainUI.java index 50a98fbcd..011b4f610 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ui/MainUI.java +++ b/app/src/main/java/net/sourceforge/opencamera/ui/MainUI.java @@ -200,6 +200,10 @@ public class MainUI { } } + // stores with width and height of the last time we laid out the UI + public int layoutUI_display_w = -1; + public int layoutUI_display_h = -1; + private void layoutUI(boolean popup_container_only) { long debug_time = 0; if( MyDebug.LOG ) { @@ -342,6 +346,12 @@ public class MainUI { Point display_size = new Point(); Display display = main_activity.getWindowManager().getDefaultDisplay(); display.getSize(display_size); + this.layoutUI_display_w = display_size.x; + this.layoutUI_display_h = display_size.y; + if( MyDebug.LOG ) { + Log.d(TAG, "layoutUI_display_w: " + layoutUI_display_w); + Log.d(TAG, "layoutUI_display_h: " + layoutUI_display_h); + } final int display_height = Math.min(display_size.x, display_size.y); final float scale = main_activity.getResources().getDisplayMetrics().density; -- GitLab From c167f7abf03b180dfd6d0a0dab4197ef9c94723d Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 20 Jun 2023 22:29:16 +0100 Subject: [PATCH 63/94] MainUI.onOrientationChanged() only needed if lock_to_landscape==true. --- .../sourceforge/opencamera/MainActivity.java | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java index 665231fb9..6fdb28fb6 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java @@ -466,15 +466,18 @@ public class MainActivity extends AppCompatActivity { // initialise state of on-screen icons mainUI.updateOnScreenIcons(); - // listen for orientation event change - orientationEventListener = new OrientationEventListener(this) { - @Override - public void onOrientationChanged(int orientation) { - MainActivity.this.mainUI.onOrientationChanged(orientation); - } - }; - if( MyDebug.LOG ) - Log.d(TAG, "onCreate: time after setting orientation event listener: " + (System.currentTimeMillis() - debug_time)); + if( MainActivity.lock_to_landscape ) { + // listen for orientation event change (only required if lock_to_landscape==true + // (MainUI.onOrientationChanged() does nothing if lock_to_landscape==false) + orientationEventListener = new OrientationEventListener(this) { + @Override + public void onOrientationChanged(int orientation) { + MainActivity.this.mainUI.onOrientationChanged(orientation); + } + }; + if( MyDebug.LOG ) + Log.d(TAG, "onCreate: time after setting orientation event listener: " + (System.currentTimeMillis() - debug_time)); + } layoutChangeListener = new View.OnLayoutChangeListener() { @Override @@ -1466,7 +1469,9 @@ public class MainActivity extends AppCompatActivity { mSensorManager.registerListener(accelerometerListener, mSensorAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); magneticSensor.registerMagneticListener(mSensorManager); - orientationEventListener.enable(); + if( orientationEventListener != null ) { + orientationEventListener.enable(); + } getWindow().getDecorView().addOnLayoutChangeListener(layoutChangeListener); // if BLE remote control is enabled, then start the background BLE service @@ -1600,7 +1605,9 @@ public class MainActivity extends AppCompatActivity { unregisterDisplayListener(); mSensorManager.unregisterListener(accelerometerListener); magneticSensor.unregisterMagneticListener(mSensorManager); - orientationEventListener.disable(); + if( orientationEventListener != null ) { + orientationEventListener.disable(); + } getWindow().getDecorView().removeOnLayoutChangeListener(layoutChangeListener); bluetoothRemoteControl.stopRemoteControl(); freeAudioListener(false); -- GitLab From cf615f8b4519a4fd865e80eb5855964e516d155c Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 20 Jun 2023 23:41:30 +0100 Subject: [PATCH 64/94] Fix tests due to autofocus timeout change - should be doing calls on UI thread. --- .../opencamera/test/MainActivityTest.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java b/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java index 5708b0da5..26e2c2dc6 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/test/MainActivityTest.java @@ -3288,7 +3288,13 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2 Date: Wed, 21 Jun 2023 22:18:22 +0100 Subject: [PATCH 65/94] Update gradle to 8.0.2. --- app/build.gradle | 3 +++ build.gradle | 2 +- gradle.properties | 3 +++ gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d8985b3f6..b81c3782f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,6 +37,9 @@ android { checkReleaseBuilds false } namespace 'net.sourceforge.opencamera' + buildFeatures { + renderScript true + } //useLibrary 'android.test.mock' } diff --git a/build.gradle b/build.gradle index 4bf070117..e74a11432 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.0.2' } } diff --git a/gradle.properties b/gradle.properties index 5465fec0e..e3dfef4e0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,5 @@ +android.defaults.buildfeatures.buildconfig=true android.enableJetifier=true +android.nonFinalResIds=false +android.nonTransitiveRClass=false android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bb308dcba..8f5f04b95 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip -- GitLab From 8a7e9f99655b16edd4266cb7e7e0ebd672854deb Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 21 Jun 2023 23:39:51 +0100 Subject: [PATCH 66/94] Add logging. --- .../main/java/net/sourceforge/opencamera/preview/Preview.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index 4bdb3db7c..18b4f123f 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -1800,6 +1800,8 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu if( MyDebug.LOG ) Log.e(TAG, "error from CameraController: camera device failed"); if( camera_controller != null ) { + if( MyDebug.LOG ) + Log.e(TAG, "set camera_controller to null"); camera_controller = null; camera_open_state = CameraOpenState.CAMERAOPENSTATE_CLOSED; applicationInterface.onCameraError(); -- GitLab From c3a214ad7ce4a6e0aed4a205fe91954a9ac45f67 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Wed, 21 Jun 2023 23:40:14 +0100 Subject: [PATCH 67/94] Call onError on UI thread. --- .../cameracontroller/CameraController2.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java index 2ff8a7e15..7454c9431 100644 --- a/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java +++ b/app/src/main/java/net/sourceforge/opencamera/cameracontroller/CameraController2.java @@ -1958,7 +1958,16 @@ public class CameraController2 extends CameraController { // need to communicate the problem to the application // n.b., as this is potentially serious error, we always log even if MyDebug.LOG is false Log.e(TAG, "error occurred after camera was opened"); - camera_error_cb.onError(); + // important to run on UI thread to avoid synchronisation issues in the Preview + final Activity activity = (Activity)context; + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if( MyDebug.LOG ) + Log.d(TAG, "onError: call camera_error_cb.onError() on UI thread"); + camera_error_cb.onError(); + } + }); } } @@ -2258,8 +2267,7 @@ public class CameraController2 extends CameraController { Log.d(TAG, "camera now opened: " + camera); /*{ - // test error handling - final Handler handler = new Handler(); + // test error handling on background thread handler.postDelayed(new Runnable() { @Override public void run() { -- GitLab From 7673c6008e39e0f5a88280fa517514a869a51a4a Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Mon, 26 Jun 2023 22:08:12 +0100 Subject: [PATCH 68/94] Google Play pre-launch accessibility warnings - use COMPLEX_UNIT_SP not COMPLEX_UNIT_DIP for text. --- _docs/history.html | 1 + .../net/sourceforge/opencamera/ui/PopupView.java | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index 3c880a950..ff8b24967 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -71,6 +71,7 @@ UPDATED Drop support for notifications for background saving, due to Android 13 UPDATED No longer allow a screenshot of the camera preview to show in "recent apps" view (for Android 13+). UPDATED No longer cancel panorama when moving device orientation too far in wrong direction. +UPDATED Made more text scale according to user's font size preference. Version 1.51.1 (2023/01/02) diff --git a/app/src/main/java/net/sourceforge/opencamera/ui/PopupView.java b/app/src/main/java/net/sourceforge/opencamera/ui/PopupView.java index 6c5df2b2f..7088452c7 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ui/PopupView.java +++ b/app/src/main/java/net/sourceforge/opencamera/ui/PopupView.java @@ -347,7 +347,7 @@ public class PopupView extends LinearLayout { // don't show auto-stabilise checkbox on popup if there's an on-screen icon CheckBox checkBox = new CheckBox(main_activity); checkBox.setText(getResources().getString(R.string.preference_auto_stabilise)); - checkBox.setTextSize(TypedValue.COMPLEX_UNIT_DIP, standard_text_size_dip); + checkBox.setTextSize(TypedValue.COMPLEX_UNIT_SP, standard_text_size_dip); checkBox.setTextColor(Color.WHITE); { // align the checkbox a bit better @@ -1451,7 +1451,7 @@ public class PopupView extends LinearLayout { ll2.addView(view); button.setText(button_string); - button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, button_text_size_dip); + button.setTextSize(TypedValue.COMPLEX_UNIT_SP, button_text_size_dip); button.setTextColor(Color.WHITE); // need 0 padding so we have enough room to display text for ISO buttons, when there are 6 ISO settings final int padding = (int) (0 * scale + 0.5f); // convert dps to pixels @@ -1548,7 +1548,7 @@ public class PopupView extends LinearLayout { final TextView text_view = view.findViewById(R.id.text_view); text_view.setText(title + ":"); - text_view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, title_text_size_dip); + text_view.setTextSize(TypedValue.COMPLEX_UNIT_SP, title_text_size_dip); text_view.setTypeface(null, Typeface.BOLD); //text_view.setBackgroundColor(Color.GRAY); // debug this.addView(text_view); @@ -1601,7 +1601,7 @@ public class PopupView extends LinearLayout { button.setBackgroundColor(Color.TRANSPARENT); // workaround for Android 6 crash! button.setText(title + "..."); button.setAllCaps(false); - button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, title_text_size_dip); + button.setTextSize(TypedValue.COMPLEX_UNIT_SP, title_text_size_dip); this.addView(button); if( MyDebug.LOG ) Log.d(TAG, "addRadioOptionsToPopup time 1: " + (System.nanoTime() - debug_time)); @@ -1704,7 +1704,7 @@ public class PopupView extends LinearLayout { button.setId(count); button.setText(supported_option_entry); - button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, standard_text_size_dip); + button.setTextSize(TypedValue.COMPLEX_UNIT_SP, standard_text_size_dip); button.setTextColor(Color.WHITE); if( MyDebug.LOG ) Log.d(TAG, "addRadioOptionsToGroup time 3: " + (System.nanoTime() - debug_time)); @@ -1798,7 +1798,7 @@ public class PopupView extends LinearLayout { setArrayOptionsText(supported_options, title, text_view, title_in_options, title_in_options_first_only, current_index); //text_view.setBackgroundColor(Color.GRAY); // debug - text_view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, standard_text_size_dip); + text_view.setTextSize(TypedValue.COMPLEX_UNIT_SP, standard_text_size_dip); text_view.setSingleLine(true); // if text too long for the button, we'd rather not have wordwrap, even if it means cutting some text off LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f); // Yuck! We want the arrow_button_w to be fairly large so that users can touch the arrow buttons easily, but if @@ -1813,7 +1813,7 @@ public class PopupView extends LinearLayout { prev_button.setBackgroundColor(Color.TRANSPARENT); // workaround for Android 6 crash! //ll2.addView(prev_button); prev_button.setText("<"); - prev_button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, arrow_text_size_dip); + prev_button.setTextSize(TypedValue.COMPLEX_UNIT_SP, arrow_text_size_dip); prev_button.setTypeface(null, Typeface.BOLD); prev_button.setPadding(padding, padding, padding, padding); ViewGroup.LayoutParams vg_params = prev_button.getLayoutParams(); @@ -1830,7 +1830,7 @@ public class PopupView extends LinearLayout { next_button.setBackgroundColor(Color.TRANSPARENT); // workaround for Android 6 crash! //ll2.addView(next_button); next_button.setText(">"); - next_button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, arrow_text_size_dip); + next_button.setTextSize(TypedValue.COMPLEX_UNIT_SP, arrow_text_size_dip); next_button.setTypeface(null, Typeface.BOLD); next_button.setPadding(padding, padding, padding, padding); vg_params = next_button.getLayoutParams(); -- GitLab From ccb321caa8d0311cf8ce21aa811a02a9872bc15a Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Mon, 26 Jun 2023 22:09:10 +0100 Subject: [PATCH 69/94] Crash related to multi-camera devices. --- _docs/history.html | 1 + .../java/net/sourceforge/opencamera/MainActivity.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/_docs/history.html b/_docs/history.html index ff8b24967..abd736462 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -49,6 +49,7 @@
     Version 1.52 (Working in progress)
     
    +FIXED   Crash related to multi-camera devices.
     FIXED   Don't show zebra stripes, focus peaking or histogram, when displaying resultant photo for
             "Pause after taking photo" option.
     FIXED   Problem where clicking on gallery icon would sometimes go to a "base" image instead of HDR
    diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java
    index 6fdb28fb6..95e456f88 100644
    --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java
    +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java
    @@ -2251,7 +2251,14 @@ public class MainActivity extends AppCompatActivity {
             if( indx == -1 ) {
                 Log.e(TAG, "camera id not in current camera set");
                 // this shouldn't happen, but if it does, revert to the first camera id in the set
    -            cameraId = camera_set.get(0);
    +            // update: oddly had reports of IndexOutOfBoundsException crashes from Google Play from camera_set.get(0)
    +            // because of camera_set having length 0, so stick with currCameraId in such cases
    +            if( camera_set.size() == 0 ) {
    +                Log.e(TAG, "camera_set is empty");
    +                cameraId = currCameraId;
    +            }
    +            else
    +                cameraId = camera_set.get(0);
             }
             else {
                 indx = (indx+1) % camera_set.size();
    -- 
    GitLab
    
    
    From 330745113b5391d25b1a17986b73f089ca85cb7e Mon Sep 17 00:00:00 2001
    From: Mark Harman 
    Date: Mon, 26 Jun 2023 22:11:02 +0100
    Subject: [PATCH 70/94] Possible crash when failing to save with Storage Access
     Framework.
    
    ---
     _docs/history.html                                         | 1 +
     .../main/java/net/sourceforge/opencamera/StorageUtils.java | 7 +++++++
     2 files changed, 8 insertions(+)
    
    diff --git a/_docs/history.html b/_docs/history.html
    index abd736462..793b3b6a5 100644
    --- a/_docs/history.html
    +++ b/_docs/history.html
    @@ -50,6 +50,7 @@
     Version 1.52 (Working in progress)
     
     FIXED   Crash related to multi-camera devices.
    +FIXED   Possible crash when failing to save with Storage Access Framework.
     FIXED   Don't show zebra stripes, focus peaking or histogram, when displaying resultant photo for
             "Pause after taking photo" option.
     FIXED   Problem where clicking on gallery icon would sometimes go to a "base" image instead of HDR
    diff --git a/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java b/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java
    index e5d95f084..be383d86c 100644
    --- a/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java
    +++ b/app/src/main/java/net/sourceforge/opencamera/StorageUtils.java
    @@ -811,6 +811,13 @@ public class StorageUtils {
                 e.printStackTrace();
                 throw new IOException();
             }
    +        catch(NullPointerException e) {
    +            // Have reports of this from Google Play for DocumentsContract.createDocument - better to fail gracefully and tell user rather than crash!
    +            if( MyDebug.LOG )
    +                Log.e(TAG, "createOutputMediaFileSAF failed with NullPointerException");
    +            e.printStackTrace();
    +            throw new IOException();
    +        }
             catch(SecurityException e) {
                 // Have reports of this from Google Play - better to fail gracefully and tell user rather than crash!
                 if( MyDebug.LOG )
    -- 
    GitLab
    
    
    From 38959a8777a0664376499dbc976523b14aeb840c Mon Sep 17 00:00:00 2001
    From: Mark Harman 
    Date: Wed, 28 Jun 2023 23:58:52 +0100
    Subject: [PATCH 71/94] Support "Touch to capture" for video.
    
    ---
     _docs/help.html                                              | 5 ++---
     _docs/history.html                                           | 1 +
     .../java/net/sourceforge/opencamera/preview/Preview.java     | 4 ++--
     app/src/main/res/values/strings.xml                          | 2 +-
     4 files changed, 6 insertions(+), 6 deletions(-)
    
    diff --git a/_docs/help.html b/_docs/help.html
    index db5f1b2d9..f7b98aaa3 100644
    --- a/_docs/help.html
    +++ b/_docs/help.html
    @@ -491,9 +491,8 @@ Fast Burst photo mode, or long press the "take photo" button in Standard or Fast
     
     

    More camera controls... - Select to access the following controls:

    -

    Touch to capture - This option allows you to take a photo either just by touching or double-tapping on the -preview screen. Note that starting/stopping video recording is still performed in the normal way; this option only -affects taking photos.

    +

    Touch to capture - This option allows you to take a photo, or start and stop video recording, + just by either touching or double-tapping on the preview screen.

    Pause after taking photo - If ticked, after taking a photo the display will pause, with options to share Share icon or delete diff --git a/_docs/history.html b/_docs/history.html index 793b3b6a5..da25d4e6c 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -67,6 +67,7 @@ FIXED Aspect ratio and other fixes in split-screen and multi-window modes. ADDED Support for zoom with camera vendor extensions (for supported Android 13+ devices). ADDED Support for displaying on-screen ISO and exposure time with camera vendor extensions (for supported Android 13+ devices). +UPDATED "Touch to capture" option now supports starting and stopping video. UPDATED Applied a timeout of 2 second for focusing with original camera API. UPDATED Improved performance for NR photo mode. UPDATED Drop support for notifications for background saving, due to Android 13 permissions faff. diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index 18b4f123f..d9d11986d 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -731,7 +731,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu } // don't take a photo on touch if the user is touching to unpause! - if( !this.is_video && !was_paused && touch_capture ) { + if( !was_paused && touch_capture ) { if( MyDebug.LOG ) Log.d(TAG, "touch to capture"); // Interpret as if user had clicked take photo/video button, except that we set the focus/metering areas. @@ -826,7 +826,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu /** Returns whether we will take a photo on a double tap. */ private boolean takePhotoOnDoubleTap() { - return !is_video && applicationInterface.getDoubleTapCapturePref(); + return applicationInterface.getDoubleTapCapturePref(); } @SuppressWarnings("SameReturnValue") diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d2de3caf8..a2545664a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -100,7 +100,7 @@ Repeat mode interval More camera controls… Touch to capture - Take a photo just by touching or double-tapping the preview + Take a photo or record video just by touching or double-tapping the preview Pause after taking photo Pause the screen after taking a photo, with the option to share or delete the photo Shutter sound -- GitLab From 9b28d7c41805f05e16f9d6a1ee0ff82bcbba14bd Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 29 Jun 2023 21:36:49 +0100 Subject: [PATCH 72/94] More info on multiple cameras. --- _docs/help.html | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/_docs/help.html b/_docs/help.html index f7b98aaa3..e52cd7079 100644 --- a/_docs/help.html +++ b/_docs/help.html @@ -1297,13 +1297,25 @@ option is on. (If they're being rotated even when the phone is held level, it ma device isn't calibrated.) It's off by default, but you may have accidentally switched it on. To turn off, go to the "popup" menu and untick Auto-level.

    -

    Why doesn't Open Camera support dual / multiple cameras? - Open Camera supports switching between all cameras -that are made available to third party applications. Usually this means front and back cameras, but some devices have -multiple front and/or back-facing cameras. Use the -Switch multi-camera iconswitch multi-camera icon -to switch between multiple front or back cameras. Note that some devices do not expose the multiple cameras explicitly, -but instead will automatically switch cameras as required when zooming in or out. In some cases the extra cameras aren't -made available to third party applications, so it isn't possible for Open Camera to support them.

    +

    Why doesn't Open Camera support dual / multiple cameras? - Open Camera supports cameras + that are made available to third party applications, although you may need to set Settings/"Camera API" + to "Camera2 API". When using Camera2 API. many devices expose multiple cameras via the zoom - zooming out to less + than 1x switches to the ultra-wide camera, zooming in automatically switches to the telephoto when + required. On other devices, the cameras can be manually switched by using the + Switch multi-camera iconswitch multi-camera icon. + Note that some devices don't allow third party applications to use their extra cameras, either + via zoom or by explicitly switching to the camera. In such cases Open Camera cannot access them.

    + +

    But another third party camera app can access the extra cameras on my device, why can't Open Camera? - + On some devices, it may be possible to access the camera by ignoring what the device claims, and trying to + access the camera IDs anyway. This is a hack - on other devices, this will lead to buggy behaviour where + cameras are exposed that hang or otherwise don't work. The problem here is that the device does not + support exposing the cameras to third party camera applications via the Android camera API.

    + +

    But can't you use the hacky method to access the extra cameras anyway? - Put it this way: + you paid hundreds of pounds for a device from a large company with lots of resources, but you + want the free application to do the extra work to workaround the device's limitation, even when it's a hacky + method? Sometimes I do implement workarounds for device limitations - but it is risky to do so here.

    Why doesn't Open Camera support the maximum video resolution on my device? - If you are using Camera2 API, make sure that you're not in slow motion mode (see "Speed" under -- GitLab From 177bd97481646880bb8a94455b1e5d274623674a Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 6 Jul 2023 21:30:08 +0100 Subject: [PATCH 73/94] Reorder. --- .../main/java/net/sourceforge/opencamera/HDRProcessor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 8f8a609b3..3df7fc92c 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -942,6 +942,9 @@ public class HDRProcessor { if( MyDebug.LOG ) Log.d(TAG, "### time after copying to bitmap: " + (System.currentTimeMillis() - time_s)); + if( free_output_allocation ) + output_allocation.destroy(); + if( release_bitmaps ) { // make it so that we store the output bitmap as first in the list bitmaps.set(0, output_bitmap); @@ -950,8 +953,6 @@ public class HDRProcessor { } } - if( free_output_allocation ) - output_allocation.destroy(); for(int i=0;i Date: Thu, 6 Jul 2023 21:32:56 +0100 Subject: [PATCH 74/94] Refactor. --- .../main/java/net/sourceforge/opencamera/HDRProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 3df7fc92c..e90fc704b 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1401,11 +1401,11 @@ public class HDRProcessor { final boolean filter_align = false; //final boolean filter_align = true; - if( allocation_avg_align == null ) { + if( bitmap_avg_align == null ) { bitmap_avg_align = Bitmap.createBitmap(bitmap_avg, align_x, align_y, align_width, align_height, align_scale_matrix, filter_align); allocation_avg_align = Allocation.createFromBitmap(rs, bitmap_avg_align); if( MyDebug.LOG ) - Log.d(TAG, "### time after creating avg allocation for autoalignment: " + (System.currentTimeMillis() - time_s)); + Log.d(TAG, "### time after creating avg bitmap for autoalignment: " + (System.currentTimeMillis() - time_s)); } bitmap_new_align = Bitmap.createBitmap(bitmap_new, align_x, align_y, align_width, align_height, align_scale_matrix, filter_align); allocation_new_align = Allocation.createFromBitmap(rs, bitmap_new_align); -- GitLab From a9672e652c320a3b02c0e9905b80e00fac016538 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 6 Jul 2023 21:33:53 +0100 Subject: [PATCH 75/94] Reorder. --- app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index e90fc704b..b69c97d62 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -1511,7 +1511,6 @@ public class HDRProcessor { } Allocation allocation_avg = allocation_out; - boolean free_allocation_avg = false; if( allocation_out == null ) { if( MyDebug.LOG ) @@ -1521,6 +1520,7 @@ public class HDRProcessor { Log.d(TAG, "### time after create allocation_out: " + (System.currentTimeMillis() - time_s)); } + boolean free_allocation_avg = false; if( allocation_avg == null ) { allocation_avg = Allocation.createFromBitmap(rs, bitmap_avg); free_allocation_avg = true; -- GitLab From 353be9ba22764d9505edd9a75192d5cc50e2b432 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Thu, 6 Jul 2023 21:37:28 +0100 Subject: [PATCH 76/94] Add comment for computeHistogramAllocation(). --- app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index b69c97d62..81b2171d4 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2671,7 +2671,7 @@ public class HDRProcessor { } } - /** + /** Only call this from computeHistogram()! * @param avg If true, compute the color value as the average of the rgb values. If false, * compute the color value as the maximum of the rgb values. * @param floating_point Whether the allocation_in is in floating point (F32_3) format, or -- GitLab From d31688e1a2a0cc22d974ec223c47513c8df94d41 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 8 Jul 2023 21:55:59 +0100 Subject: [PATCH 77/94] Update translation. --- app/src/main/res/values-pl/strings.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index f25256b6a..e7c7c4a66 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -961,4 +961,15 @@ X-Bty Rozszerzenie: Piękno / Retusz twarzy + Mapowanie tonalne HDR + Algorytm używany do mapowania tonów w trybie zdjęć HDR\n%s + Prosty clamp + Wykładniczo + Domyślnie + + Usuń dane EXIF urządzenia + Czy usuwać metadane EXIF urządzenia ze zdjęć JPEG. Nie spowoduje to usunięcia znaczników exif zastosowanych w innych ustawieniach, które stosują metadane EXIF (np. lokalizacja/geotagowanie). Te inne opcje są niezależne i zastąpią to ustawienie. Nie dotyczy to zdjęć RAW/DNG oraz filmów.\n%s + Nie usuwaj danych EXIF urządzenia + Usuń dane EXIF urządzenia + Usuń z wyjątkiem daty/godziny -- GitLab From 24bb99425e0cc2b68db613a807aff045400919da Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 8 Jul 2023 22:15:07 +0100 Subject: [PATCH 78/94] Fix for want_no_limits for tablets where navigation bar isn't in a fixed position. --- _docs/history.html | 2 + .../sourceforge/opencamera/MainActivity.java | 80 +++++++++++++++++-- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index da25d4e6c..63427675e 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -64,6 +64,8 @@ FIXED Problem where if setting Video Picture Profiles to non-default value cau FIXED Allow trying to switch between photo and video mode if camera fails to open (in some case the failure may be specific to the mode). FIXED Aspect ratio and other fixes in split-screen and multi-window modes. +FIXED Problem on some tablets where zoom seekbar showed under navigation bar is landscape + orientation in some circumstances. ADDED Support for zoom with camera vendor extensions (for supported Android 13+ devices). ADDED Support for displaying on-screen ISO and exposure time with camera vendor extensions (for supported Android 13+ devices). diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java index 95e456f88..47bcc3f22 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java @@ -3332,18 +3332,81 @@ public class MainActivity extends AppCompatActivity { Log.d(TAG, "set a window insets listener"); this.set_window_insets_listener = true; decorView.getRootView().setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + private boolean has_last_system_orientation; + private SystemOrientation last_system_orientation; @Override public @NonNull WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) { if( MyDebug.LOG ) { + Log.d(TAG, "inset left: " + insets.getSystemWindowInsetLeft()); + Log.d(TAG, "inset top: " + insets.getSystemWindowInsetTop()); Log.d(TAG, "inset right: " + insets.getSystemWindowInsetRight()); Log.d(TAG, "inset bottom: " + insets.getSystemWindowInsetBottom()); } - if( navigation_gap == 0 ) { - SystemOrientation system_orientation = getSystemOrientation(); - boolean system_orientation_portrait = system_orientation == SystemOrientation.PORTRAIT; - navigation_gap = system_orientation_portrait ? insets.getSystemWindowInsetBottom() : insets.getSystemWindowInsetRight(); + SystemOrientation system_orientation = getSystemOrientation(); + int new_navigation_gap; + switch ( system_orientation ) { + case PORTRAIT: + new_navigation_gap = insets.getSystemWindowInsetBottom(); + break; + case LANDSCAPE: + new_navigation_gap = insets.getSystemWindowInsetRight(); + break; + case REVERSE_LANDSCAPE: + new_navigation_gap = insets.getSystemWindowInsetLeft(); + break; + default: + Log.e(TAG, "unknown system_orientation?!: " + system_orientation); + new_navigation_gap = 0; + break; + } + + if( has_last_system_orientation && system_orientation != last_system_orientation && new_navigation_gap != navigation_gap ) { + if( MyDebug.LOG ) + Log.d(TAG, "navigation_gap changed due to system orientation change, from " + navigation_gap + " to " + new_navigation_gap); + + navigation_gap = new_navigation_gap; + + if( MyDebug.LOG ) + Log.d(TAG, "want_no_limits: " + want_no_limits); + if( want_no_limits ) { + // If we want no_limits mode, then need to take care in case of device orientation + // in cases where that changes the navigation_gap: + // - Need to set showUnderNavigation() (in case navigation_gap when from zero to non-zero or vice versa). + // - Need to call layoutUI() (for different value of navigation_gap) + + // Need to call showUnderNavigation() from handler for it to take effect. + // Similarly we have problems if we call layoutUI without post-ing it - + // sometimes when rotating a device, we get a call to OnApplyWindowInsetsListener + // with 0 navigation_gap followed by the call with the correct non-zero values - + // posting the call to layoutUI means it runs after the second call, so we have the + // correct navigation_gap. + Handler handler = new Handler(); + handler.post(new Runnable() { + @Override + public void run() { + if( MyDebug.LOG ) + Log.d(TAG, "runnable for change in navigation_gap due to orientation change"); + if( navigation_gap != 0 ) { + if( MyDebug.LOG ) + Log.d(TAG, "set FLAG_LAYOUT_NO_LIMITS"); + showUnderNavigation(true); + } + else { + if( MyDebug.LOG ) + Log.d(TAG, "clear FLAG_LAYOUT_NO_LIMITS"); + showUnderNavigation(false); + } + if( MyDebug.LOG ) + Log.d(TAG, "layout UI due to changing navigation_gap"); + mainUI.layoutUI(); + } + }); + } + } + else if( navigation_gap == 0 ) { if( MyDebug.LOG ) - Log.d(TAG, "navigation_gap is " + navigation_gap); + Log.d(TAG, "navigation_gap changed from zero to " + new_navigation_gap); + navigation_gap = new_navigation_gap; // Sometimes when this callback is called, the navigation_gap may still be 0 even if // the device doesn't have physical navigation buttons - we need to wait // until we have found a non-zero value before switching to no limits. @@ -3356,6 +3419,9 @@ public class MainActivity extends AppCompatActivity { } } + has_last_system_orientation = true; + last_system_orientation = system_orientation; + // see comments in MainUI.layoutUI() for why we don't use this /*if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && getSystemOrientation() == SystemOrientation.LANDSCAPE ) { Rect privacy_indicator_rect = insets.getPrivacyIndicatorBounds(); @@ -5093,6 +5159,8 @@ public class MainActivity extends AppCompatActivity { Log.d(TAG, "set FLAG_LAYOUT_NO_LIMITS"); showUnderNavigation(true); // need to layout the UI again due to now taking the navigation gap into account + if( MyDebug.LOG ) + Log.d(TAG, "layout UI due to changing want_no_limits behaviour"); mainUI.layoutUI(); } else { @@ -5106,6 +5174,8 @@ public class MainActivity extends AppCompatActivity { Log.d(TAG, "clear FLAG_LAYOUT_NO_LIMITS"); showUnderNavigation(false); // need to layout the UI again due to no longer taking the navigation gap into account + if( MyDebug.LOG ) + Log.d(TAG, "layout UI due to changing want_no_limits behaviour"); mainUI.layoutUI(); } } -- GitLab From b5635cf403366fbfc1fbfd7ac79a404d3ed269cf Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 11 Jul 2023 23:35:37 +0100 Subject: [PATCH 79/94] fix typo in logging. --- .../main/java/net/sourceforge/opencamera/preview/Preview.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index d9d11986d..2e9aaebd8 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -4212,7 +4212,7 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu for(int i=zoom_factor;i= zoom_ratio ) { if( MyDebug.LOG ) - Log.d(TAG, "zoom int, found new zoom by comparing " + zoom_ratios.get(i)/100.0f + " >= " + zoom_ratio); + Log.d(TAG, "zoom in, found new zoom by comparing " + zoom_ratios.get(i)/100.0f + " >= " + zoom_ratio); new_zoom_factor = i; break; } -- GitLab From db4329f777757b46001efc827fef0283977fe1a6 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 15 Jul 2023 21:35:34 +0100 Subject: [PATCH 80/94] Pinch zoom improvements - zoom faster, but also fix jitter. --- _docs/history.html | 2 + .../opencamera/preview/Preview.java | 56 ++++++++++++++++++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/_docs/history.html b/_docs/history.html index 63427675e..7b4543eb2 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -51,6 +51,7 @@ Version 1.52 (Working in progress) FIXED Crash related to multi-camera devices. FIXED Possible crash when failing to save with Storage Access Framework. +FIXED Jittery zoom when using multitouch pinch but pinching slowly. FIXED Don't show zebra stripes, focus peaking or histogram, when displaying resultant photo for "Pause after taking photo" option. FIXED Problem where clicking on gallery icon would sometimes go to a "base" image instead of HDR @@ -69,6 +70,7 @@ FIXED Problem on some tablets where zoom seekbar showed under navigation bar i ADDED Support for zoom with camera vendor extensions (for supported Android 13+ devices). ADDED Support for displaying on-screen ISO and exposure time with camera vendor extensions (for supported Android 13+ devices). +UPDATED Made pinch zoom more sensitive (better support for modern devices with higher zoom levels). UPDATED "Touch to capture" option now supports starting and stopping video. UPDATED Applied a timeout of 2 second for focusing with original camera API. UPDATED Improved performance for NR photo mode. diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index 2e9aaebd8..f62b7e436 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -766,7 +766,12 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu @Override public boolean onScale(@NonNull ScaleGestureDetector detector) { if( Preview.this.camera_controller != null && Preview.this.has_zoom ) { - Preview.this.scaleZoom(detector.getScaleFactor()); + float scale_factor = detector.getScaleFactor(); + if( MyDebug.LOG ) + Log.d(TAG, "onScale: " + scale_factor); + // make pinch zoom more sensitive: + scale_factor = 1.0f + 2.0f*(scale_factor - 1.0f); + Preview.this.scaleZoom(scale_factor); } return true; } @@ -4205,8 +4210,55 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu if( has_smooth_zoom ) smooth_zoom = zoom_ratios.get(max_zoom_factor)/100.0f; } + else if( has_smooth_zoom ) { + // Find the closest zoom level by rounding to nearest. + // Important to have same behaviour whether zooming in or out, otherwise problem when touching with two fingers and not + // moving - we'll get very small scale factors alternately between zooming in and out. + // The only reason we have separate codepath for zooming in or out is for performance (since we know to only look at + // higher or lower zoom ratios). + float dist = Math.abs(zoom_ratio - zoom_ratios.get(zoom_factor)/100.0f); + if( MyDebug.LOG ) + Log.d(TAG, " current dist: " + dist); + + if( scale_factor > 1.0f ) { + // zooming in + for(int i=zoom_factor+1;i dist+1.0e-5f ) { + break; + } + } + } + else { + // zooming out + for(int i=zoom_factor-1;i>=0;i--) { + float this_dist = Math.abs(zoom_ratio - zoom_ratios.get(i)/100.0f); + if( this_dist < dist ) { + new_zoom_factor = i; + dist = this_dist; + if( MyDebug.LOG ) + Log.d(TAG, "zoom out, found new zoom by comparing " + zoom_ratios.get(i)/100.0f + " to " + zoom_ratio + " , dist " + dist); + } + else if( this_dist > dist+1.0e-5f ) { + break; + } + } + } + + smooth_zoom = zoom_ratio; + } else { // find the closest zoom level + // unclear if we need this code anymore (smooth_zoom should always be true?) + if( scale_factor > 1.0f ) { // zooming in for(int i=zoom_factor;i Date: Fri, 21 Jul 2023 21:22:31 +0100 Subject: [PATCH 81/94] Tweak frame rate for Android 13+. --- .../net/sourceforge/opencamera/preview/Preview.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java index f62b7e436..c1653bc07 100644 --- a/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java +++ b/app/src/main/java/net/sourceforge/opencamera/preview/Preview.java @@ -8714,9 +8714,17 @@ public class Preview implements SurfaceHolder.Callback, TextureView.SurfaceTextu frame rate when applying the dimming effect when reopening or updating the camera (see DrawPreview.setDimPreview()) (especially for MainActivity.updateForSettings() when we pause/unpause the preview instead of reopening the camera). + Update: On more recent Android versions, this effect no longer seems to happen, and on + Android 13 (at least Pixel 6 Pro), we see the reverse (but more reasonable) behaviour + where we have fewer janky frames with a longer frame rate. Behaviour is much better at + 32ms compared to 16ms; and we shouldn't go any slower (firstly so that UI still runs + smoothly; secondly for dimming effect as noted above). */ // - if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) { + if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ) { + return 32; + } + else if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) { if( is_test_junit4 ) { // see https://stackoverflow.com/questions/29550508/espresso-freezing-on-view-with-looping-animation return 32; -- GitLab From ead93ab49ae6dc1c4ece42916556d9490b1e34b1 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 21 Jul 2023 21:22:55 +0100 Subject: [PATCH 82/94] Add commented out performance timing. --- .../net/sourceforge/opencamera/ui/DrawPreview.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java index 1147a4ba5..008d483ba 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java +++ b/app/src/main/java/net/sourceforge/opencamera/ui/DrawPreview.java @@ -2720,6 +2720,11 @@ public class DrawPreview { public void onDrawPreview(Canvas canvas) { /*if( MyDebug.LOG ) Log.d(TAG, "onDrawPreview");*/ + /*if( MyDebug.LOG ) + Log.d(TAG, "onDrawPreview hardware accelerated: " + canvas.isHardwareAccelerated());*/ + + final long time_ms = System.currentTimeMillis(); + if( !has_settings ) { if( MyDebug.LOG ) Log.d(TAG, "onDrawPreview: need to update settings"); @@ -2729,8 +2734,6 @@ public class DrawPreview { CameraController camera_controller = preview.getCameraController(); int ui_rotation = preview.getUIRotation(); - final long time_ms = System.currentTimeMillis(); - // set up preview bitmaps (histogram etc) boolean want_preview_bitmap = want_histogram || want_zebra_stripes || want_focus_peaking; if( want_preview_bitmap != preview.isPreviewBitmapEnabled() ) { @@ -3003,6 +3006,11 @@ public class DrawPreview { } } } + + /*if( MyDebug.LOG ) { + long time_taken = System.currentTimeMillis() - time_ms; + Log.d(TAG, "onDrawPreview time: " + time_taken); + }*/ } private void setLastImageMatrix(Canvas canvas, Bitmap bitmap, int this_ui_rotation, boolean flip_front) { -- GitLab From a9bcdf3387a1b10599eaf2426e0bae18e875e9ad Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 21 Jul 2023 21:39:07 +0100 Subject: [PATCH 83/94] Pass width/height to computeHistogram(). --- .../net/sourceforge/opencamera/HDRProcessor.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 81b2171d4..59c5bae83 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -992,7 +992,7 @@ public class HDRProcessor { if( dro_tonemapping_algorithm == DROTonemappingAlgorithm.DROALGORITHM_GAINGAMMA ) { // brighten? - int [] histo = computeHistogram(allocation, false, false); + int [] histo = computeHistogram(allocation, width, height, false, false); HistogramInfo histogramInfo = getHistogramInfo(histo); int brightness = histogramInfo.median_brightness; int max_brightness = histogramInfo.max_brightness; @@ -2743,7 +2743,7 @@ public class HDRProcessor { Allocation allocation_in = Allocation.createFromBitmap(rs, bitmap); if( MyDebug.LOG ) Log.d(TAG, "time after createFromBitmap: " + (System.currentTimeMillis() - time_s)); - int [] histogram = computeHistogram(allocation_in, avg, false); + int [] histogram = computeHistogram(allocation_in, bitmap.getWidth(), bitmap.getHeight(), avg, false); allocation_in.destroy(); freeScripts(); if( MyDebug.LOG ) { @@ -2754,9 +2754,12 @@ public class HDRProcessor { } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - private int [] computeHistogram(Allocation allocation, boolean avg, boolean floating_point) { - if( MyDebug.LOG ) - Log.d(TAG, "computeHistogram"); + private int [] computeHistogram(Allocation allocation, int width, int height, boolean avg, boolean floating_point) { + if( MyDebug.LOG ) { + Log.d(TAG, "computeHistogram [allocation]"); + Log.d(TAG, "avg: " + avg); + Log.d(TAG, "floating_point: " + floating_point); + } long time_s = System.currentTimeMillis(); int [] histogram = new int[256]; Allocation histogramAllocation = computeHistogramAllocation(allocation, avg, floating_point, time_s); @@ -3026,7 +3029,7 @@ public class HDRProcessor { long time_s = System.currentTimeMillis(); - int [] histo = computeHistogram(input, false, true); + int [] histo = computeHistogram(input, width, height, false, true); HistogramInfo histogramInfo = getHistogramInfo(histo); int brightness = histogramInfo.median_brightness; int max_brightness = histogramInfo.max_brightness; -- GitLab From b78b5dff5c2d5a3a64aea453438070c3bcbabcb0 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 21 Jul 2023 21:41:06 +0100 Subject: [PATCH 84/94] Comment out some logging. --- .../main/java/net/sourceforge/opencamera/HDRProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 59c5bae83..4bfd0438e 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -3040,9 +3040,9 @@ public class HDRProcessor { Log.d(TAG, "median brightness: " + histogramInfo.median_brightness); Log.d(TAG, "mean brightness: " + histogramInfo.mean_brightness); Log.d(TAG, "max brightness: " + max_brightness); - for(int i=0;i<256;i++) { + /*for(int i=0;i<256;i++) { Log.d(TAG, "histogram[" + i + "]: " + histo[i]); - } + }*/ } BrightenFactors brighten_factors = computeBrightenFactors(true, iso, exposure_time, brightness, max_brightness); -- GitLab From 7b7eee0e93e0e8ef3ee1392ca8180020a8075932 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 21 Jul 2023 21:47:47 +0100 Subject: [PATCH 85/94] Rename bitmap to output_bitmap. --- .../java/net/sourceforge/opencamera/HDRProcessor.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index 4bfd0438e..e280261fd 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -3087,8 +3087,8 @@ public class HDRProcessor { avgBrightenScript.set_median_filter_strength(median_filter_strength); avgBrightenScript.invoke_setBrightenParameters(gain, gamma, low_x, mid_x, max_brightness); - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Allocation allocation_out = Allocation.createFromBitmap(rs, bitmap); + Bitmap output_bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Allocation allocation_out = Allocation.createFromBitmap(rs, output_bitmap); if( MyDebug.LOG ) Log.d(TAG, "### time after creating allocation_out: " + (System.currentTimeMillis() - time_s)); @@ -3123,7 +3123,7 @@ public class HDRProcessor { Log.d(TAG, "### time after adjustHistogram: " + (System.currentTimeMillis() - time_s)); } - allocation_out.copyTo(bitmap); + allocation_out.copyTo(output_bitmap); allocation_out.destroy(); if( MyDebug.LOG ) Log.d(TAG, "### time after copying to bitmap: " + (System.currentTimeMillis() - time_s)); @@ -3131,7 +3131,7 @@ public class HDRProcessor { freeScripts(); if( MyDebug.LOG ) Log.d(TAG, "### total time for avgBrighten: " + (System.currentTimeMillis() - time_s)); - return bitmap; + return output_bitmap; } /** Final stage of the noise reduction algorithm. -- GitLab From 2fa4f1ef6720726b390275d239fd3f314d5d2864 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 21 Jul 2023 21:58:21 +0100 Subject: [PATCH 86/94] Rename adjustHistogram to adjustHistogramRS. --- .../net/sourceforge/opencamera/HDRProcessor.java | 12 ++++++------ .../sourceforge/opencamera/PanoramaProcessor.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index e280261fd..e7773a3ea 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -933,7 +933,7 @@ public class HDRProcessor { } if( hdr_alpha != 0.0f ) { - adjustHistogram(output_allocation, output_allocation, width, height, hdr_alpha, n_tiles, ce_preserve_blacks, time_s); + adjustHistogramRS(output_allocation, output_allocation, width, height, hdr_alpha, n_tiles, ce_preserve_blacks, time_s); if( MyDebug.LOG ) Log.d(TAG, "### time after adjustHistogram: " + (System.currentTimeMillis() - time_s)); } @@ -1036,7 +1036,7 @@ public class HDRProcessor { } } - adjustHistogram(allocation, output_allocation, width, height, hdr_alpha, n_tiles, ce_preserve_blacks, time_s); + adjustHistogramRS(allocation, output_allocation, width, height, hdr_alpha, n_tiles, ce_preserve_blacks, time_s); output_allocation.copyTo(output_bitmap); if( MyDebug.LOG ) @@ -1742,7 +1742,7 @@ public class HDRProcessor { } if( hdr_alpha != 0.0f ) { - adjustHistogram(allocation0, allocation0, width, height, hdr_alpha, n_tiles, ce_preserve_blacks, time_s); + adjustHistogramRS(allocation0, allocation0, width, height, hdr_alpha, n_tiles, ce_preserve_blacks, time_s); if( MyDebug.LOG ) Log.d(TAG, "### time after adjustHistogram: " + (System.currentTimeMillis() - time_s)); } @@ -2534,9 +2534,9 @@ public class HDRProcessor { } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - void adjustHistogram(Allocation allocation_in, Allocation allocation_out, int width, int height, float hdr_alpha, int n_tiles, boolean ce_preserve_blacks, long time_s) { + void adjustHistogramRS(Allocation allocation_in, Allocation allocation_out, int width, int height, float hdr_alpha, int n_tiles, boolean ce_preserve_blacks, long time_s) { if( MyDebug.LOG ) - Log.d(TAG, "adjustHistogram"); + Log.d(TAG, "adjustHistogram [renderscript]"); //final boolean adjust_histogram_local = false; final boolean adjust_histogram_local = true; @@ -3118,7 +3118,7 @@ public class HDRProcessor { Log.d(TAG, "dro alpha: " + alpha); Log.d(TAG, "dro amount: " + amount); } - adjustHistogram(allocation_out, allocation_out, width, height, amount, 1, true, time_s); + adjustHistogramRS(allocation_out, allocation_out, width, height, amount, 1, true, time_s); if( MyDebug.LOG ) Log.d(TAG, "### time after adjustHistogram: " + (System.currentTimeMillis() - time_s)); } diff --git a/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java b/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java index 5e2050ee4..c885a80ef 100644 --- a/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java @@ -3135,7 +3135,7 @@ public class PanoramaProcessor { Allocation allocation = Allocation.createFromBitmap(rs, panorama); if( MyDebug.LOG ) Log.d(TAG, "### time after creating allocation_out: " + (System.currentTimeMillis() - time_s)); - hdrProcessor.adjustHistogram(allocation, allocation, panorama.getWidth(), panorama.getHeight(), 0.25f, 1, true, time_s); + hdrProcessor.adjustHistogramRS(allocation, allocation, panorama.getWidth(), panorama.getHeight(), 0.25f, 1, true, time_s); if( MyDebug.LOG ) Log.d(TAG, "### time after adjustHistogram: " + (System.currentTimeMillis() - time_s)); allocation.copyTo(panorama); -- GitLab From ff7359182a6238431a3c6033f6644ef4378e6793 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Fri, 21 Jul 2023 22:02:15 +0100 Subject: [PATCH 87/94] Update logging. --- .../java/net/sourceforge/opencamera/HDRProcessor.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java index e7773a3ea..339dc18ad 100644 --- a/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java +++ b/app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java @@ -2185,7 +2185,7 @@ public class HDRProcessor { alignMTBScript.forEach_align(mtb_allocations[base_bitmap], launch_options); if( MyDebug.LOG ) { Log.d(TAG, "time for alignMTBScript: " + (System.currentTimeMillis() - this_time_s)); - Log.d(TAG, "time after alignMTBScript: " + (System.currentTimeMillis() - time_s)); + Log.d(TAG, "### time after alignMTBScript: " + (System.currentTimeMillis() - time_s)); } errorsAllocation.copyTo(errors); errorsAllocation.destroy(); @@ -2524,8 +2524,8 @@ public class HDRProcessor { histogram[y] -= move; } } - if( MyDebug.LOG ) - Log.d(TAG, " histogram pulled up to: " + histogram[x]); + /*if( MyDebug.LOG ) + Log.d(TAG, " histogram pulled up to: " + histogram[x]);*/ /*if( temp_c_histogram[x] >= c_equal_limit ) throw new RuntimeException(); // test*/ } @@ -2637,11 +2637,11 @@ public class HDRProcessor { for(int x=1;x<256;x++) { c_histogram[histogram_offset+x] = c_histogram[histogram_offset+x-1] + histogram[x]; } - if( MyDebug.LOG ) { + /*if( MyDebug.LOG ) { for(int x=0;x<256;x++) { Log.d(TAG, "histogram[" + x + "] = " + histogram[x] + " cumulative: " + c_histogram[histogram_offset+x]); } - } + }*/ } } -- GitLab From dc22b8fac97f63d7ae30df6b2bb8a25a67496978 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 29 Jul 2023 21:24:37 +0100 Subject: [PATCH 88/94] Fix warnings. --- app/src/main/java/net/sourceforge/opencamera/ImageSaver.java | 1 - .../java/net/sourceforge/opencamera/MyApplicationInterface.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java index b89f9a576..9517eab87 100644 --- a/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java +++ b/app/src/main/java/net/sourceforge/opencamera/ImageSaver.java @@ -45,7 +45,6 @@ import android.net.Uri; import android.os.Build; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; -import android.renderscript.Allocation; import android.util.Log; import android.util.TypedValue; import android.util.Xml; diff --git a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java index 0e347a89e..1294553e8 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyApplicationInterface.java @@ -1842,7 +1842,7 @@ public class MyApplicationInterface extends BasicApplicationInterface { // don't allow zooming in panorama mode, the algorithm isn't set up to support this! return false; } - else if( isCameraExtensionPref() && !main_activity.getPreview().supportsZoomForCameraExtension(getCameraExtensionPref()) ) { + else if( isCameraExtensionPref() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !main_activity.getPreview().supportsZoomForCameraExtension(getCameraExtensionPref()) ) { // zoom not supported for camera extension return false; } -- GitLab From 1dfb866cb2c47a08f87da908956105c9744deb35 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 29 Jul 2023 23:51:56 +0100 Subject: [PATCH 89/94] Fix typo. --- _docs/history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_docs/history.html b/_docs/history.html index 7b4543eb2..c25e1e138 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -65,7 +65,7 @@ FIXED Problem where if setting Video Picture Profiles to non-default value cau FIXED Allow trying to switch between photo and video mode if camera fails to open (in some case the failure may be specific to the mode). FIXED Aspect ratio and other fixes in split-screen and multi-window modes. -FIXED Problem on some tablets where zoom seekbar showed under navigation bar is landscape +FIXED Problem on some tablets where zoom seekbar showed under navigation bar in landscape orientation in some circumstances. ADDED Support for zoom with camera vendor extensions (for supported Android 13+ devices). ADDED Support for displaying on-screen ISO and exposure time with camera vendor extensions (for -- GitLab From 566ee5ad846a6720ee3a371093bce87ce30b00be Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sat, 29 Jul 2023 23:52:28 +0100 Subject: [PATCH 90/94] Bump to version 1.52 / 88. --- app/src/main/AndroidManifest.xml | 4 ++-- .../sourceforge/opencamera/MainActivity.java | 2 +- app/src/main/res/values/strings.xml | 18 ++++++++---------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ed54d2639..d7b3873b7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ diff --git a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java index 47bcc3f22..df64dfe0d 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MainActivity.java +++ b/app/src/main/java/net/sourceforge/opencamera/MainActivity.java @@ -615,7 +615,7 @@ public class MainActivity extends AppCompatActivity { // E.g., we have a "What's New" for 1.44 (64), but then push out a quick fix for 1.44.1 (65). We don't want to // show the dialog again to people who already received 1.44 (64), but we still want to show the dialog to people // upgrading from earlier versions. - int whats_new_version = 86; // 1.51 + int whats_new_version = 88; // 1.52 whats_new_version = Math.min(whats_new_version, version_code); // whats_new_version should always be <= version_code, but just in case! if( MyDebug.LOG ) { Log.d(TAG, "whats_new_version: " + whats_new_version); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2545664a..b93221359 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1069,17 +1069,15 @@ [This dialog is shown when Open Camera is updated. You can disable it under Settings/On screen GUI/Show What\'s New dialog.] - \n\nv1.51:\n

      -
    • New option Settings/Photo settings/\"Remove device EXIF data\" to remove device metadata from JPEG photos.\n
    • -
    • New option Settings/Photo settings/\"HDR tonemapping\" to choose tonemapping algorithm used for HDR photo mode.\n
    • -
    • Fixed slow focusing for Camera2 API on some devices.\n
    • -
    • Fixed incorrect thumbnail orientation on some devices.\n
    • -
    • Fixed focus bracketing on some devices.\n
    • -
    • Fixed geotagging not working on some devices.\n
    • -
    • Improvement to HDR algorithm for dark scenes.\n
    • -
    • Default to Camera2 API for some devices (will only take affect for new installs or if resetting settings).\n
    • -
    • Various other UI improvements and bug fixes.\n
    • +
    • Improvements for pinch zooming.\n
    • +
    • Fixes for split-screen and multi-window mode.\n
    • +
    • Support for zoom and on-screen ISO/exposure display when using camera vendor extensions (for supported Android 13+ devices).\n
    • +
    • \"Touch to capture\" option now supports starting and stopping video.\n
    • +
    • Applied a 2s timeout for focusing with original camera API.\n
    • +
    • Improved performance for NR photo mode.\n
    • +
    • No longer cancel panorama when rotating device too far in wrong direction.\n
    • +
    • Various other improvements and bug fixes.\n
    -- GitLab From 609216f4e2b90dfd316b605579b4d0baf984a255 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 6 Aug 2023 12:09:52 +0100 Subject: [PATCH 91/94] Refactor. --- .../java/net/sourceforge/opencamera/MyPreferenceFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java index a47946818..d79f12f87 100644 --- a/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java +++ b/app/src/main/java/net/sourceforge/opencamera/MyPreferenceFragment.java @@ -1758,7 +1758,8 @@ public class MyPreferenceFragment extends PreferenceFragment implements OnShared //SpannableString span = new SpannableString(getActivity().getResources().getString(R.string.preference_privacy_policy_text)); //Linkify.addLinks(span, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES); - Spanned span = Html.fromHtml(getActivity().getResources().getString(R.string.preference_privacy_policy_text)); + String privacy_policy_text = getActivity().getResources().getString(R.string.preference_privacy_policy_text); + Spanned span = Html.fromHtml(privacy_policy_text); // clickable text is only supported if we call setMovementMethod on the TextView - which means we need to create // our own for the AlertDialog! @SuppressLint("InflateParams") // we add the view to the alert dialog in addTextViewForAlertDialog() -- GitLab From c3697ab9b798f6faaa3326a6d5eff868551a6cc6 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 6 Aug 2023 12:10:35 +0100 Subject: [PATCH 92/94] Fix missing version number from last commit. --- app/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b93221359..c6ebed8fc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1069,6 +1069,7 @@ [This dialog is shown when Open Camera is updated. You can disable it under Settings/On screen GUI/Show What\'s New dialog.] + \n\nv1.52:\n
    • Improvements for pinch zooming.\n
    • Fixes for split-screen and multi-window mode.\n
    • -- GitLab From 0685d2cf87849706b68958124a3ff42a7bab1d8f Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Tue, 8 Aug 2023 00:49:08 +0100 Subject: [PATCH 93/94] Make test more tolerant. --- .../java/net/sourceforge/opencamera/TestUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java index 8de859ab0..e332c6078 100644 --- a/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java +++ b/app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java @@ -1151,9 +1151,9 @@ public class TestUtils { else if( is_nr ) { suffix = "_NR"; if( activity.getApplicationInterface().getNRModePref() == MyApplicationInterface.NRModePref.NRMODE_LOW_LIGHT ) - max_time_s += 6; // takes longer to save low light photo + max_time_s += 11; // takes longer to save low light photo else - max_time_s += 5; + max_time_s += 10; } else if( is_expo ) { suffix = "_" + (n_expo_images-1); -- GitLab From 542a575b4d3a1072d7810fc95abee49ab4bf6a99 Mon Sep 17 00:00:00 2001 From: Mark Harman Date: Sun, 13 Aug 2023 20:17:25 +0100 Subject: [PATCH 94/94] Add date for release. --- _docs/history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_docs/history.html b/_docs/history.html index c25e1e138..c34d512ea 100644 --- a/_docs/history.html +++ b/_docs/history.html @@ -47,7 +47,7 @@

      < Main Page.

      -Version 1.52 (Working in progress)
      +Version 1.52 (2023/08/13)
       
       FIXED   Crash related to multi-camera devices.
       FIXED   Possible crash when failing to save with Storage Access Framework.
      -- 
      GitLab