Skip to content

Commit 0a58d34

Browse files
[Sample] [Graphics] [Compose] UltraHDR image rendering in Compose. (#89)
* Render an UltraHDR image in Compose. Added two views (ImageView (Regular android view)) and Image (Compose)) to display functionality and support of rendering an ultrahdr. * (1) Loading asset and image decoding on a background thread (2) Add activity color mode controls for hdr and sdr (3) Remove fragment class and use the Sample annotation (4) Resolve minor readability comments * Removed dependencies which are not required * Update DisplayUltraHDRScreen logic to be Compose friendly (#116) * Update DisplayUltraHDRScreen logic to be Compose friendly * Add ImageView below to Compose Image composable Fetch colorMode details when screen is rendered * Update README.md --------- Co-authored-by: Yacine Rezgui <rezgui.y@gmail.com>
1 parent 44a6f74 commit 0a58d34

File tree

3 files changed

+220
-0
lines changed

3 files changed

+220
-0
lines changed

samples/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Demonstrates how to implement data access auditing for your app to identify
2828
This sample demonstrates displaying an UltraHDR image.
2929
- [Displaying UltraHDR (3P Libraries)](graphics/ultrahdr/src/main/java/com/example/platform/graphics/ultrahdr/display/DisplayingUltraHDRUsing3PLibrary.kt):
3030
This sample demonstrates using the various popular image loading library to
31+
- [Displaying UltraHDR (Compose)](graphics/ultrahdr/src/main/java/com/example/platform/graphics/ultrahdr/display/DisplayUltraHDRScreen.kt):
32+
This sample demonstrates displaying an UltraHDR image in a Compose View and an Android View
3133
- [Downloadable Fonts](user-interface/text/src/main/java/com/example/platform/ui/text/DownloadableFonts.kt):
3234
Download fonts instead of bundling them in the app resources.
3335
- [Drag and Drop](user-interface/draganddrop/src/main/java/com/example/platform/ui/draganddrop/DragAndDrop.kt):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* Copyright 2023 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+
package com.example.platform.graphics.ultrahdr.display
17+
18+
import android.app.Activity
19+
import android.content.Context
20+
import android.content.ContextWrapper
21+
import android.content.pm.ActivityInfo
22+
import android.graphics.Bitmap
23+
import android.graphics.BitmapFactory
24+
import android.os.Build
25+
import android.util.Log
26+
import android.view.Display
27+
import android.view.Window
28+
import android.widget.ImageView
29+
import androidx.annotation.RequiresApi
30+
import androidx.compose.foundation.Image
31+
import androidx.compose.foundation.layout.Arrangement
32+
import androidx.compose.foundation.layout.Column
33+
import androidx.compose.foundation.layout.Row
34+
import androidx.compose.foundation.layout.Spacer
35+
import androidx.compose.foundation.layout.fillMaxWidth
36+
import androidx.compose.foundation.layout.padding
37+
import androidx.compose.foundation.layout.width
38+
import androidx.compose.material3.Button
39+
import androidx.compose.material3.Divider
40+
import androidx.compose.material3.MaterialTheme
41+
import androidx.compose.material3.Text
42+
import androidx.compose.runtime.Composable
43+
import androidx.compose.runtime.DisposableEffect
44+
import androidx.compose.runtime.LaunchedEffect
45+
import androidx.compose.runtime.getValue
46+
import androidx.compose.runtime.mutableStateOf
47+
import androidx.compose.runtime.remember
48+
import androidx.compose.runtime.setValue
49+
import androidx.compose.ui.Alignment
50+
import androidx.compose.ui.Modifier
51+
import androidx.compose.ui.graphics.asImageBitmap
52+
import androidx.compose.ui.platform.LocalContext
53+
import androidx.compose.ui.platform.LocalView
54+
import androidx.compose.ui.res.dimensionResource
55+
import androidx.compose.ui.res.stringResource
56+
import androidx.compose.ui.unit.dp
57+
import androidx.compose.ui.viewinterop.AndroidView
58+
import androidx.compose.ui.window.DialogWindowProvider
59+
import com.example.platform.graphics.ultrahdr.R
60+
import com.google.android.catalog.framework.annotations.Sample
61+
import kotlinx.coroutines.Dispatchers
62+
import kotlinx.coroutines.withContext
63+
import java.util.function.Consumer
64+
65+
@RequiresApi(34)
66+
@Sample(
67+
name = "Displaying UltraHDR (Compose)",
68+
description = "This sample demonstrates displaying an UltraHDR image in a Compose View and an Android View",
69+
documentation = "https://developer.android.com/guide/topics/media/hdr-image-format",
70+
tags = ["UltraHDR", "Compose"],
71+
)
72+
73+
@Composable
74+
fun DisplayUltraHDRScreen() {
75+
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
76+
77+
val context = LocalContext.current
78+
var colorMode by remember { mutableStateOf<ColorMode>(ColorMode.Default) }
79+
val window = findWindow()
80+
val display = LocalView.current.display
81+
82+
// Load asset and bitmap on background thread
83+
LaunchedEffect(Unit) {
84+
window?.let {
85+
colorMode = getColorMode(it, display)
86+
}
87+
88+
// Same bitmap is used to load image in an image view and image (Compose)
89+
bitmap = withContext(Dispatchers.IO) {
90+
val ultraHdrImage = "gainmaps/night_highrise.jpg"
91+
context.assets.open(ultraHdrImage).use { inputStream ->
92+
BitmapFactory.decodeStream(inputStream)
93+
}
94+
}
95+
}
96+
97+
val hdrSdrRatioChangeListener = Consumer<Display> { display ->
98+
if (window == null) return@Consumer
99+
100+
Log.d(TAG, "HDR/SDR Ratio Changed ${display.hdrSdrRatio}")
101+
102+
colorMode = getColorMode(window, display)
103+
}
104+
105+
DisposableEffect(window, display) {
106+
if (display.isHdrSdrRatioAvailable) {
107+
display.registerHdrSdrRatioChangedListener(
108+
{ executable -> executable.run() },
109+
hdrSdrRatioChangeListener,
110+
)
111+
}
112+
// When the effect leaves the Composition, remove the observer
113+
onDispose {
114+
display.unregisterHdrSdrRatioChangedListener(hdrSdrRatioChangeListener)
115+
}
116+
}
117+
118+
119+
Column(
120+
modifier = Modifier.fillMaxWidth(),
121+
verticalArrangement = Arrangement.SpaceEvenly,
122+
horizontalAlignment = Alignment.CenterHorizontally,
123+
) {
124+
125+
val details = when (colorMode) {
126+
is ColorMode.Default -> stringResource(R.string.color_mode_sdr)
127+
is ColorMode.Unknown -> stringResource(R.string.color_mode_unknown)
128+
is ColorMode.Hdr -> stringResource(
129+
R.string.color_mode_hdr_with_ratio,
130+
(colorMode as ColorMode.Hdr).hdrSdrRatio,
131+
)
132+
}
133+
134+
Text(stringResource(R.string.color_mode_details, details))
135+
136+
// Add SDR/HDR Color mode controls
137+
Row(
138+
horizontalArrangement = Arrangement.Center,
139+
modifier = Modifier.padding(dimensionResource(R.dimen.ultrahdr_color_mode_current_mode_padding)),
140+
) {
141+
Button(
142+
modifier = Modifier.weight(1f),
143+
onClick = { window?.colorMode = ActivityInfo.COLOR_MODE_DEFAULT },
144+
) {
145+
Text(stringResource(R.string.color_mode_sdr))
146+
}
147+
Spacer(Modifier.width(dimensionResource(R.dimen.ultrahdr_color_mode_current_mode_padding)))
148+
Button(
149+
modifier = Modifier.weight(1f),
150+
onClick = { window?.colorMode = ActivityInfo.COLOR_MODE_HDR },
151+
) {
152+
Text(stringResource(R.string.color_mode_hdr))
153+
}
154+
}
155+
156+
// Render UltraHDR in a Compose Image
157+
Text(text = "Image (Compose)")
158+
159+
if (bitmap != null) {
160+
Image(
161+
bitmap = bitmap!!.asImageBitmap(),
162+
contentDescription = null,
163+
modifier = Modifier.weight(1f),
164+
)
165+
}
166+
167+
Divider(
168+
modifier = Modifier
169+
.fillMaxWidth()
170+
.padding(vertical = 4.dp),
171+
thickness = 1.dp,
172+
color = MaterialTheme.colorScheme.primary,
173+
)
174+
175+
// Render UltraHDR in View (ImageView)
176+
Text(text = "ImageView (Android View)")
177+
AndroidView(
178+
modifier = Modifier.weight(1f),
179+
factory = {
180+
ImageView(it).apply {
181+
setImageBitmap(bitmap)
182+
}
183+
},
184+
update = {
185+
it.setImageBitmap(bitmap)
186+
},
187+
)
188+
}
189+
}
190+
191+
private const val TAG = "DisplayUltraHDRScreen"
192+
193+
private sealed interface ColorMode {
194+
object Default : ColorMode
195+
object Unknown : ColorMode
196+
197+
@JvmInline
198+
value class Hdr(val hdrSdrRatio: Float) : ColorMode
199+
}
200+
201+
@Composable
202+
private fun findWindow(): Window? =
203+
(LocalView.current.parent as? DialogWindowProvider)?.window ?: LocalContext.current.findWindow()
204+
205+
private tailrec fun Context.findWindow(): Window? =
206+
when (this) {
207+
is Activity -> window
208+
is ContextWrapper -> baseContext.findWindow()
209+
else -> null
210+
}
211+
212+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
213+
private fun getColorMode(window: Window, display: Display) = when (window.colorMode) {
214+
ActivityInfo.COLOR_MODE_DEFAULT -> ColorMode.Default
215+
ActivityInfo.COLOR_MODE_HDR -> ColorMode.Hdr(display.hdrSdrRatio)
216+
else -> ColorMode.Unknown
217+
}

samples/graphics/ultrahdr/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<string name="color_mode_sdr">SDR</string>
4747
<string name="color_mode_hdr">HDR</string>
4848
<string name="color_mode_hdr_with_ratio">HDR | SDR/HDR Ratio = %f</string>
49+
<string name="color_mode_details">Color Mode = %s</string>
4950
<string name="color_mode_unknown">Unknown</string>
5051

5152
<string name="refresh_ultrahdr_edited_image">Refresh</string>

0 commit comments

Comments
 (0)