1 /* 2 * Copyright (C) 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 * http://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.android.test.silkfx.hdr 18 19 import android.graphics.Gainmap 20 import android.view.Gravity 21 import android.view.LayoutInflater 22 import android.view.View 23 import android.view.ViewGroup 24 import android.widget.Button 25 import android.widget.PopupWindow 26 import android.widget.SeekBar 27 import android.widget.TextView 28 import com.android.test.silkfx.R 29 30 data class GainmapMetadata( 31 var ratioMin: Float, 32 var ratioMax: Float, 33 var capacityMin: Float, 34 var capacityMax: Float, 35 var gamma: Float, 36 var offsetSdr: Float, 37 var offsetHdr: Float 38 ) 39 40 class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) { 41 private var gainmap: Gainmap? = null 42 private var showingEdits = false 43 44 private var metadataPopup: PopupWindow? = null 45 46 private var originalMetadata: GainmapMetadata = GainmapMetadata( 47 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f) 48 private var currentMetadata: GainmapMetadata = originalMetadata.copy() 49 50 private val maxProgress = 100.0f 51 52 private val minRatioMin = .001f 53 private val maxRatioMin = 1.0f 54 private val minRatioMax = 1.0f 55 private val maxRatioMax = 16.0f 56 private val minCapacityMin = 1.0f 57 private val maxCapacityMin = maxRatioMax 58 private val minCapacityMax = 1.001f 59 private val maxCapacityMax = maxRatioMax 60 private val minGamma = 0.1f 61 private val maxGamma = 3.0f 62 // Min and max offsets are 0.0 and 1.0 respectively 63 setGainmapnull64 fun setGainmap(newGainmap: Gainmap?) { 65 gainmap = newGainmap 66 originalMetadata = GainmapMetadata(gainmap!!.getRatioMin()[0], 67 gainmap!!.getRatioMax()[0], gainmap!!.getMinDisplayRatioForHdrTransition(), 68 gainmap!!.getDisplayRatioForFullHdr(), gainmap!!.getGamma()[0], 69 gainmap!!.getEpsilonSdr()[0], gainmap!!.getEpsilonHdr()[0]) 70 currentMetadata = originalMetadata.copy() 71 } 72 useOriginalMetadatanull73 fun useOriginalMetadata() { 74 showingEdits = false 75 applyMetadata(originalMetadata) 76 } 77 useEditMetadatanull78 fun useEditMetadata() { 79 showingEdits = true 80 applyMetadata(currentMetadata) 81 } 82 closeEditornull83 fun closeEditor() { 84 metadataPopup?.let { 85 it.dismiss() 86 metadataPopup = null 87 } 88 } 89 openEditornull90 fun openEditor() { 91 if (metadataPopup != null) return 92 93 val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gainmap_metadata, null) 94 95 metadataPopup = PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, 96 ViewGroup.LayoutParams.WRAP_CONTENT) 97 metadataPopup!!.showAtLocation(view, Gravity.CENTER, 0, 0) 98 99 (view.getParent() as ViewGroup).removeView(view) 100 parent.addView(view) 101 102 view.findViewById<Button>(R.id.gainmap_metadata_done)!!.setOnClickListener { 103 closeEditor() 104 } 105 106 view.findViewById<Button>(R.id.gainmap_metadata_reset)!!.setOnClickListener { 107 resetGainmapMetadata() 108 } 109 110 updateMetadataUi() 111 112 val gainmapMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin) 113 val gainmapMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax) 114 val capacityMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin) 115 val capacityMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax) 116 val gammaSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gamma) 117 val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr) 118 val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr) 119 arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek, 120 offsetSdrSeek, offsetHdrSeek).forEach { 121 it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{ 122 override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { 123 if (!fromUser) return 124 val normalized = progress.toFloat() / maxProgress 125 when (seekBar) { 126 gainmapMinSeek -> updateGainmapMin(normalized) 127 gainmapMaxSeek -> updateGainmapMax(normalized) 128 capacityMinSeek -> updateCapacityMin(normalized) 129 capacityMaxSeek -> updateCapacityMax(normalized) 130 gammaSeek -> updateGamma(normalized) 131 offsetSdrSeek -> updateOffsetSdr(normalized) 132 offsetHdrSeek -> updateOffsetHdr(normalized) 133 } 134 } 135 136 override fun onStartTrackingTouch(seekBar: SeekBar) {} 137 override fun onStopTrackingTouch(seekBar: SeekBar) {} 138 }) 139 } 140 } 141 updateMetadataUinull142 private fun updateMetadataUi() { 143 val gainmapMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin) 144 val gainmapMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax) 145 val capacityMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin) 146 val capacityMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax) 147 val gammaSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gamma) 148 val offsetSdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr) 149 val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr) 150 151 gainmapMinSeek.setProgress( 152 ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt()) 153 gainmapMaxSeek.setProgress( 154 ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt()) 155 capacityMinSeek.setProgress( 156 ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt()) 157 capacityMaxSeek.setProgress( 158 ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt()) 159 gammaSeek.setProgress( 160 ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt()) 161 // Log base 3 via: log_b(x) = log_y(x) / log_y(b) 162 offsetSdrSeek.setProgress( 163 ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0) 164 .toFloat() * maxProgress).toInt()) 165 offsetHdrSeek.setProgress( 166 ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0) 167 .toFloat() * maxProgress).toInt()) 168 169 parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText( 170 "%.3f".format(currentMetadata.ratioMin)) 171 parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText( 172 "%.3f".format(currentMetadata.ratioMax)) 173 parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText( 174 "%.3f".format(currentMetadata.capacityMin)) 175 parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText( 176 "%.3f".format(currentMetadata.capacityMax)) 177 parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText( 178 "%.3f".format(currentMetadata.gamma)) 179 parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText( 180 "%.5f".format(currentMetadata.offsetSdr)) 181 parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText( 182 "%.5f".format(currentMetadata.offsetHdr)) 183 } 184 resetGainmapMetadatanull185 private fun resetGainmapMetadata() { 186 currentMetadata = originalMetadata.copy() 187 applyMetadata(currentMetadata) 188 updateMetadataUi() 189 } 190 applyMetadatanull191 private fun applyMetadata(newMetadata: GainmapMetadata) { 192 gainmap!!.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin) 193 gainmap!!.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax) 194 gainmap!!.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin) 195 gainmap!!.setDisplayRatioForFullHdr(newMetadata.capacityMax) 196 gainmap!!.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma) 197 gainmap!!.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr) 198 gainmap!!.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr) 199 renderView.invalidate() 200 } 201 updateGainmapMinnull202 private fun updateGainmapMin(normalized: Float) { 203 val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin) 204 parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText( 205 "%.3f".format(newValue)) 206 currentMetadata.ratioMin = newValue 207 if (showingEdits) { 208 gainmap!!.setRatioMin(newValue, newValue, newValue) 209 renderView.invalidate() 210 } 211 } 212 updateGainmapMaxnull213 private fun updateGainmapMax(normalized: Float) { 214 val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax) 215 parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText( 216 "%.3f".format(newValue)) 217 currentMetadata.ratioMax = newValue 218 if (showingEdits) { 219 gainmap!!.setRatioMax(newValue, newValue, newValue) 220 renderView.invalidate() 221 } 222 } 223 updateCapacityMinnull224 private fun updateCapacityMin(normalized: Float) { 225 val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin) 226 parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText( 227 "%.3f".format(newValue)) 228 currentMetadata.capacityMin = newValue 229 if (showingEdits) { 230 gainmap!!.setMinDisplayRatioForHdrTransition(newValue) 231 renderView.invalidate() 232 } 233 } 234 updateCapacityMaxnull235 private fun updateCapacityMax(normalized: Float) { 236 val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax) 237 parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText( 238 "%.3f".format(newValue)) 239 currentMetadata.capacityMax = newValue 240 if (showingEdits) { 241 gainmap!!.setDisplayRatioForFullHdr(newValue) 242 renderView.invalidate() 243 } 244 } 245 updateGammanull246 private fun updateGamma(normalized: Float) { 247 val newValue = minGamma + normalized * (maxGamma - minGamma) 248 parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText( 249 "%.3f".format(newValue)) 250 currentMetadata.gamma = newValue 251 if (showingEdits) { 252 gainmap!!.setGamma(newValue, newValue, newValue) 253 renderView.invalidate() 254 } 255 } 256 updateOffsetSdrnull257 private fun updateOffsetSdr(normalized: Float) { 258 var newValue = 0.0f 259 if (normalized > 0.0f ) { 260 newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat() 261 } 262 parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText( 263 "%.5f".format(newValue)) 264 currentMetadata.offsetSdr = newValue 265 if (showingEdits) { 266 gainmap!!.setEpsilonSdr(newValue, newValue, newValue) 267 renderView.invalidate() 268 } 269 } 270 updateOffsetHdrnull271 private fun updateOffsetHdr(normalized: Float) { 272 var newValue = 0.0f 273 if (normalized > 0.0f ) { 274 newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat() 275 } 276 parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText( 277 "%.5f".format(newValue)) 278 currentMetadata.offsetHdr = newValue 279 if (showingEdits) { 280 gainmap!!.setEpsilonHdr(newValue, newValue, newValue) 281 renderView.invalidate() 282 } 283 } 284 } 285