Skip to content

Commit 5b710a6

Browse files
committed
Add support for HEIC_ULTRAHDR
1 parent 37335ea commit 5b710a6

File tree

4 files changed

+58
-17
lines changed

4 files changed

+58
-17
lines changed

app/src/main/java/com/example/platform/app/SampleDemo.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.example.android.pip.PiPSampleActivity
2424
import com.example.platform.accessibility.ColorContrast
2525
import com.example.platform.accessibility.LiveRegionView
2626
import com.example.platform.accessibility.SpeakableText
27+
import com.example.platform.camera.imagecapture.Camera2HeicUltraHDRCapture
2728
import com.example.platform.camera.imagecapture.Camera2ImageCapture
2829
import com.example.platform.camera.imagecapture.Camera2UltraHDRCapture
2930
import com.example.platform.camera.preview.Camera2Preview
@@ -209,7 +210,18 @@ val SAMPLE_DEMOS by lazy {
209210
content = { AndroidFragment<Camera2UltraHDRCapture>() },
210211
),
211212
ComposableSampleDemo(
212-
id = "ultrahdr-image-capture",
213+
id = "ultrahdr-heic-image-capture",
214+
name = "HEIC UltraHDR Image Capture",
215+
description = "This sample demonstrates how to capture a 10-bit compressed still image and " +
216+
"store it using the new UltraHDR image format using Camera2.",
217+
documentation = "https://developer.android.com/guide/topics/media/hdr-image-format",
218+
minSdk = android.os.Build.VERSION_CODES.BAKLAVA, // Added minSdk for HEIC_ULTRAHDR
219+
apiSurface = CameraCamera2ApiSurface,
220+
tags = listOf("UltraHDR", "Camera2"),
221+
content = { AndroidFragment<Camera2HeicUltraHDRCapture>() },
222+
),
223+
ComposableSampleDemo(
224+
id = "image-preview",
213225
name = "Camera2 Preview",
214226
description = "Demonstrates displaying processed pixel data directly from the camera sensor "
215227
+ "to the screen using Camera2.",

samples/camera/camera2/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ plugins {
2222

2323
android {
2424
namespace = "com.example.platform.camera"
25-
compileSdk = 35
25+
compileSdk = 36
2626

2727
defaultConfig {
2828
minSdk = 21
29-
targetSdk = 35
29+
targetSdk = 36
3030
}
3131
kotlinOptions {
3232
jvmTarget = "1.8"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.platform.camera.imagecapture
18+
19+
import android.graphics.ImageFormat
20+
import android.os.Build
21+
import androidx.annotation.RequiresApi
22+
23+
@RequiresApi(Build.VERSION_CODES.BAKLAVA)
24+
class Camera2HeicUltraHDRCapture : Camera2UltraHDRCapture() {
25+
override val ULTRAHDR_FORMAT = ImageFormat.HEIC_ULTRAHDR
26+
}

samples/camera/camera2/src/main/java/com/example/platform/camera/imagecapture/Camera2UltraHDRCapture.kt

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import androidx.annotation.RequiresApi
4848
import androidx.core.content.ContextCompat
4949
import androidx.core.graphics.drawable.toDrawable
5050
import androidx.core.os.bundleOf
51-
import androidx.exifinterface.media.ExifInterface
5251
import androidx.fragment.app.Fragment
5352
import androidx.fragment.app.commit
5453
import androidx.lifecycle.lifecycleScope
@@ -76,14 +75,15 @@ import kotlin.coroutines.resume
7675
import kotlin.coroutines.resumeWithException
7776
import kotlin.coroutines.suspendCoroutine
7877

79-
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
80-
class Camera2UltraHDRCapture : Fragment() {
78+
open class Camera2UltraHDRCapture : Fragment() {
8179
/**
8280
* Android ViewBinding.
8381
*/
8482
private var _binding: Camera2UltrahdrCaptureBinding? = null
8583
private val binding get() = _binding!!
8684

85+
protected open val ULTRAHDR_FORMAT = ImageFormat.JPEG_R
86+
8787
/**
8888
* Detects, characterizes, and connects to a CameraDevice (used for all camera operations).
8989
*/
@@ -303,13 +303,13 @@ class Camera2UltraHDRCapture : Fragment() {
303303

304304
private fun setUpImageReader() {
305305
// Initialize an image reader which will be used to capture still photos
306-
val pixelFormat = ImageFormat.JPEG_R
306+
val pixelFormat = ULTRAHDR_FORMAT
307307
val configMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
308308
configMap?.let { config ->
309309
config.getOutputSizes(pixelFormat).maxByOrNull { it.height * it.width }
310310
?.let { size ->
311311
imageReader = ImageReader.newInstance(
312-
size.width, size.height, pixelFormat, IMAGE_BUFFER_SIZE,
312+
size.width, size.height, ULTRAHDR_FORMAT, IMAGE_BUFFER_SIZE,
313313
)
314314
}
315315
}
@@ -324,13 +324,11 @@ class Camera2UltraHDRCapture : Fragment() {
324324
takePhoto().use { result ->
325325
Log.d(TAG, "Result received: $result")
326326

327-
// Save the result to disk, update EXIF metadata with orientation info
327+
// Save the result to disk, the previous EXIF orientation approach
328+
// will not work in case of HEIC images so use the corresponding
329+
// Camera2 control path instead.
328330
val output = saveResult(result)
329331
Log.d(TAG, "Image saved: ${output.absolutePath}")
330-
val exif = ExifInterface(output.absolutePath)
331-
exif.setAttribute(ExifInterface.TAG_ORIENTATION, result.orientation.toString())
332-
exif.saveAttributes()
333-
Log.d(TAG, "EXIF metadata saved: ${output.absolutePath}")
334332

335333
// Display the photo taken to user
336334
lifecycleScope.launch(Dispatchers.Main) {
@@ -355,7 +353,7 @@ class Camera2UltraHDRCapture : Fragment() {
355353
// Query the available output formats.
356354
val formats = c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)?.outputFormats
357355

358-
val canEncodeUltraHDR = formats?.contains(ImageFormat.JPEG_R) ?: false
356+
val canEncodeUltraHDR = formats?.contains(ULTRAHDR_FORMAT) ?: false
359357

360358
return canEncodeUltraHDR
361359
}
@@ -499,6 +497,7 @@ class Camera2UltraHDRCapture : Fragment() {
499497

500498
val request = session.device.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
501499
.apply { addTarget(imageReader.surface) }
500+
request.set(CaptureRequest.JPEG_ORIENTATION, relativeOrientation.value ?: 0)
502501

503502
session.capture(
504503
request.build(),
@@ -584,7 +583,7 @@ class Camera2UltraHDRCapture : Fragment() {
584583
val buffer = result.image.planes[0].buffer
585584
val bytes = ByteArray(buffer.remaining()).apply { buffer.get(this) }
586585
try {
587-
val output = createFile(requireContext())
586+
val output = createFile(requireContext(), ULTRAHDR_FORMAT)
588587
FileOutputStream(output).use { it.write(bytes) }
589588
cont.resume(output)
590589
} catch (exc: IOException) {
@@ -635,9 +634,13 @@ class Camera2UltraHDRCapture : Fragment() {
635634
*
636635
* @return [File] created.
637636
*/
638-
private fun createFile(context: Context): File {
637+
private fun createFile(context: Context, format: Int): File {
639638
val sdf = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss_SSS", Locale.US)
640-
return File(context.filesDir, "IMG_${sdf.format(Date())}.jpg")
639+
val fileType = when (format) {
640+
ImageFormat.JPEG_R -> ".jpg"
641+
else -> ".heic"
642+
}
643+
return File(context.filesDir, "IMG_${sdf.format(Date())}" + fileType)
641644
}
642645
}
643646
}

0 commit comments

Comments
 (0)