1 /* 2 * Copyright 2024 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 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.*; 18 import static com.google.media.codecs.ultrahdr.UltraHDREncoder.UHDR_USAGE_BEST_QUALITY; 19 20 import java.io.File; 21 import java.io.FileInputStream; 22 import java.io.BufferedReader; 23 import java.io.FileReader; 24 import java.io.BufferedWriter; 25 import java.io.FileWriter; 26 import java.io.FileOutputStream; 27 import java.io.IOException; 28 import java.nio.ByteBuffer; 29 import java.nio.ByteOrder; 30 31 import com.google.media.codecs.ultrahdr.UltraHDRDecoder; 32 import com.google.media.codecs.ultrahdr.UltraHDREncoder; 33 import com.google.media.codecs.ultrahdr.UltraHDRDecoder.GainMapMetadata; 34 import com.google.media.codecs.ultrahdr.UltraHDRDecoder.RawImage; 35 36 /** 37 * Ultra HDR Encoding/Decoding Demo Application 38 */ 39 public class UltraHdrApp { 40 private final String mHdrIntentRawFile; 41 private final String mSdrIntentRawFile; 42 private final String mSdrIntentCompressedFile; 43 private final String mGainMapCompressedFile; 44 private final String mGainMapMetadaCfgFile; 45 private final String mExifFile; 46 private final String mUhdrFile; 47 private final String mOutputFile; 48 private final int mWidth; 49 private final int mHeight; 50 private final int mHdrCf; 51 private final int mSdrCf; 52 private final int mHdrCg; 53 private final int mSdrCg; 54 private final int mHdrTf; 55 private final int mQuality; 56 private final int mOTF; 57 private final int mOfmt; 58 private final boolean mFullRange; 59 private final int mMapDimensionScaleFactor; 60 private final int mMapCompressQuality; 61 private final boolean mUseMultiChannelGainMap; 62 private final float mGamma; 63 private final boolean mEnableGLES; 64 private final int mEncPreset; 65 private final float mMinContentBoost; 66 private final float mMaxContentBoost; 67 private final float mTargetDispPeakBrightness; 68 69 byte[] mYuv420YData, mYuv420CbData, mYuv420CrData; 70 short[] mP010YData, mP010CbCrData; 71 int[] mRgba1010102Data, mRgba8888Data; 72 long[] mRgbaF16Data; 73 byte[] mCompressedImageData; 74 byte[] mGainMapCompressedImageData; 75 byte[] mExifData; 76 byte[] mUhdrImagedata; 77 GainMapMetadata mMetadata; 78 RawImage mDecodedUhdrRgbImage; 79 UltraHdrApp(String hdrIntentRawFile, String sdrIntentRawFile, String sdrIntentCompressedFile, String gainmapCompressedFile, String gainmapMetadataCfgFile, String exifFile, String outputFile, int width, int height, int hdrCf, int sdrCf, int hdrCg, int sdrCg, int hdrTf, int quality, int oTf, int oFmt, boolean isHdrCrFull, int gainmapScaleFactor, int gainmapQuality, boolean enableMultiChannelGainMap, float gamma, int encPreset, float minContentBoost, float maxContentBoost, float targetDispPeakBrightness)80 public UltraHdrApp(String hdrIntentRawFile, String sdrIntentRawFile, 81 String sdrIntentCompressedFile, String gainmapCompressedFile, 82 String gainmapMetadataCfgFile, String exifFile, String outputFile, int width, 83 int height, int hdrCf, int sdrCf, int hdrCg, int sdrCg, int hdrTf, int quality, int oTf, 84 int oFmt, boolean isHdrCrFull, int gainmapScaleFactor, int gainmapQuality, 85 boolean enableMultiChannelGainMap, float gamma, int encPreset, float minContentBoost, 86 float maxContentBoost, float targetDispPeakBrightness) { 87 mHdrIntentRawFile = hdrIntentRawFile; 88 mSdrIntentRawFile = sdrIntentRawFile; 89 mSdrIntentCompressedFile = sdrIntentCompressedFile; 90 mGainMapCompressedFile = gainmapCompressedFile; 91 mGainMapMetadaCfgFile = gainmapMetadataCfgFile; 92 mExifFile = exifFile; 93 mUhdrFile = null; 94 mOutputFile = outputFile; 95 mWidth = width; 96 mHeight = height; 97 mHdrCf = hdrCf; 98 mSdrCf = sdrCf; 99 mHdrCg = hdrCg; 100 mSdrCg = sdrCg; 101 mHdrTf = hdrTf; 102 mQuality = quality; 103 mOTF = oTf; 104 mOfmt = oFmt; 105 mFullRange = isHdrCrFull; 106 mMapDimensionScaleFactor = gainmapScaleFactor; 107 mMapCompressQuality = gainmapQuality; 108 mUseMultiChannelGainMap = enableMultiChannelGainMap; 109 mGamma = gamma; 110 mEnableGLES = false; 111 mEncPreset = encPreset; 112 mMinContentBoost = minContentBoost; 113 mMaxContentBoost = maxContentBoost; 114 mTargetDispPeakBrightness = targetDispPeakBrightness; 115 } 116 UltraHdrApp(String gainmapMetadataCfgFile, String uhdrFile, String outputFile, int oTF, int oFmt, boolean enableGLES)117 public UltraHdrApp(String gainmapMetadataCfgFile, String uhdrFile, String outputFile, int oTF, 118 int oFmt, boolean enableGLES) { 119 mHdrIntentRawFile = null; 120 mSdrIntentRawFile = null; 121 mSdrIntentCompressedFile = null; 122 mGainMapCompressedFile = null; 123 mGainMapMetadaCfgFile = gainmapMetadataCfgFile; 124 mExifFile = null; 125 mUhdrFile = uhdrFile; 126 mOutputFile = outputFile; 127 mWidth = 0; 128 mHeight = 0; 129 mHdrCf = UHDR_IMG_FMT_UNSPECIFIED; 130 mSdrCf = UHDR_IMG_FMT_UNSPECIFIED; 131 mHdrCg = UHDR_CG_UNSPECIFIED; 132 mSdrCg = UHDR_CG_UNSPECIFIED; 133 mHdrTf = UHDR_CT_UNSPECIFIED; 134 mQuality = 95; 135 mOTF = oTF; 136 mOfmt = oFmt; 137 mFullRange = false; 138 mMapDimensionScaleFactor = 1; 139 mMapCompressQuality = 95; 140 mUseMultiChannelGainMap = true; 141 mGamma = 1.0f; 142 mEnableGLES = enableGLES; 143 mEncPreset = UHDR_USAGE_BEST_QUALITY; 144 mMinContentBoost = Float.MIN_VALUE; 145 mMaxContentBoost = Float.MAX_VALUE; 146 mTargetDispPeakBrightness = -1.0f; 147 } 148 readFile(String filename)149 public byte[] readFile(String filename) throws IOException { 150 byte[] data; 151 try (FileInputStream fis = new FileInputStream(filename)) { 152 File descriptor = new File(filename); 153 long size = descriptor.length(); 154 if (size <= 0 || size > Integer.MAX_VALUE) { 155 throw new IOException("Unexpected file size received for file: " + filename); 156 } 157 data = new byte[(int) size]; 158 if (fis.read(data) != size) { 159 throw new IOException("Failed to read file: " + filename + " completely"); 160 } 161 } 162 return data; 163 } 164 fillP010ImageHandle()165 public void fillP010ImageHandle() throws IOException { 166 final int bpp = 2; 167 final int lumaSampleCount = mWidth * mHeight; 168 final int chromaSampleCount = (mWidth / 2) * (mHeight / 2) * 2; 169 final int expectedSize = (lumaSampleCount + chromaSampleCount) * bpp; 170 byte[] data = readFile(mHdrIntentRawFile); 171 if (data.length < expectedSize) { 172 throw new RuntimeException( 173 "For the configured width, height, P010 Image File is expected to contain " 174 + expectedSize + " bytes, but the file has " + data.length + " bytes"); 175 } 176 ByteBuffer byteBuffer = ByteBuffer.wrap(data); 177 byteBuffer.order(ByteOrder.nativeOrder()); 178 mP010YData = new short[lumaSampleCount]; 179 byteBuffer.asShortBuffer().get(mP010YData); 180 byteBuffer.position(lumaSampleCount * bpp); 181 mP010CbCrData = new short[chromaSampleCount]; 182 byteBuffer.asShortBuffer().get(mP010CbCrData); 183 } 184 fillRGBA1010102ImageHandle()185 public void fillRGBA1010102ImageHandle() throws IOException { 186 final int bpp = 4; 187 final int rgbSampleCount = mHeight * mWidth; 188 final int expectedSize = rgbSampleCount * bpp; 189 byte[] data = readFile(mHdrIntentRawFile); 190 if (data.length < expectedSize) { 191 throw new RuntimeException("For the configured width, height, RGBA1010102 Image File is" 192 + " expected to contain " + expectedSize + " bytes, but the file has " 193 + data.length + " bytes"); 194 } 195 ByteBuffer byteBuffer = ByteBuffer.wrap(data); 196 byteBuffer.order(ByteOrder.nativeOrder()); 197 mRgba1010102Data = new int[mHeight * mWidth]; 198 byteBuffer.asIntBuffer().get(mRgba1010102Data); 199 } 200 fillRGBAF16ImageHandle()201 public void fillRGBAF16ImageHandle() throws IOException { 202 final int bpp = 8; 203 final int rgbSampleCount = mHeight * mWidth; 204 final int expectedSize = rgbSampleCount * bpp; 205 byte[] data = readFile(mHdrIntentRawFile); 206 if (data.length < expectedSize) { 207 throw new RuntimeException("For the configured width, height, RGBA1010102 Image File is" 208 + " expected to contain " + expectedSize + " bytes, but the file has " 209 + data.length + " bytes"); 210 } 211 ByteBuffer byteBuffer = ByteBuffer.wrap(data); 212 byteBuffer.order(ByteOrder.nativeOrder()); 213 mRgbaF16Data = new long[mHeight * mWidth]; 214 byteBuffer.asLongBuffer().get(mRgbaF16Data); 215 } 216 fillRGBA8888Handle()217 public void fillRGBA8888Handle() throws IOException { 218 final int bpp = 4; 219 final int rgbSampleCount = mHeight * mWidth; 220 final int expectedSize = rgbSampleCount * bpp; 221 byte[] data = readFile(mSdrIntentRawFile); 222 if (data.length < expectedSize) { 223 throw new RuntimeException("For the configured width, height, RGBA8888 Image File is" 224 + " expected to contain " + expectedSize + " bytes, but the file has " 225 + data.length + " bytes"); 226 } 227 ByteBuffer byteBuffer = ByteBuffer.wrap(data); 228 byteBuffer.order(ByteOrder.nativeOrder()); 229 mRgba8888Data = new int[mHeight * mWidth]; 230 byteBuffer.asIntBuffer().get(mRgba8888Data); 231 } 232 fillYUV420ImageHandle()233 public void fillYUV420ImageHandle() throws IOException { 234 final int lumaSampleCount = mWidth * mHeight; 235 final int cbSampleCount = (mWidth / 2) * (mHeight / 2); 236 final int crSampleCount = (mWidth / 2) * (mHeight / 2); 237 try (FileInputStream fis = new FileInputStream(mSdrIntentRawFile)) { 238 mYuv420YData = new byte[lumaSampleCount]; 239 int bytesRead = fis.read(mYuv420YData); 240 if (bytesRead != lumaSampleCount) { 241 throw new IOException("Failed to read " + lumaSampleCount + " bytes from file: " 242 + mSdrIntentRawFile); 243 } 244 mYuv420CbData = new byte[cbSampleCount]; 245 bytesRead = fis.read(mYuv420CbData); 246 if (bytesRead != cbSampleCount) { 247 throw new IOException("Failed to read " + cbSampleCount + " bytes from file: " 248 + mSdrIntentRawFile); 249 } 250 mYuv420CrData = new byte[crSampleCount]; 251 bytesRead = fis.read(mYuv420CrData); 252 if (bytesRead != crSampleCount) { 253 throw new IOException("Failed to read " + crSampleCount + " bytes from file: " 254 + mSdrIntentRawFile); 255 } 256 } 257 } 258 fillSdrCompressedImageHandle()259 public void fillSdrCompressedImageHandle() throws IOException { 260 mCompressedImageData = readFile(mSdrIntentCompressedFile); 261 } 262 fillGainMapCompressedImageHandle()263 public void fillGainMapCompressedImageHandle() throws IOException { 264 mGainMapCompressedImageData = readFile(mGainMapCompressedFile); 265 } 266 fillExifMemoryBlock()267 public void fillExifMemoryBlock() throws IOException { 268 mExifData = readFile(mExifFile); 269 } 270 fillUhdrImageHandle()271 public void fillUhdrImageHandle() throws IOException { 272 mUhdrImagedata = readFile(mUhdrFile); 273 } 274 fillGainMapMetadataDescriptor()275 public void fillGainMapMetadataDescriptor() throws IOException { 276 mMetadata = new GainMapMetadata(); 277 try (BufferedReader reader = new BufferedReader(new FileReader(mGainMapMetadaCfgFile))) { 278 String line; 279 while ((line = reader.readLine()) != null) { 280 String[] parts = line.split("\\s+"); 281 if (parts.length == 2 && parts[0].startsWith("--")) { 282 String option = parts[0].substring(2); // remove the "--" prefix 283 float value = Float.parseFloat(parts[1]); 284 switch (option) { 285 case "maxContentBoost": 286 mMetadata.maxContentBoost = value; 287 break; 288 case "minContentBoost": 289 mMetadata.minContentBoost = value; 290 break; 291 case "gamma": 292 mMetadata.gamma = value; 293 break; 294 case "offsetSdr": 295 mMetadata.offsetSdr = value; 296 break; 297 case "offsetHdr": 298 mMetadata.offsetHdr = value; 299 break; 300 case "hdrCapacityMin": 301 mMetadata.hdrCapacityMin = value; 302 break; 303 case "hdrCapacityMax": 304 mMetadata.hdrCapacityMax = value; 305 break; 306 default: 307 System.err.println("ignoring option: " + option); 308 break; 309 } 310 } else { 311 System.err.println("Unable to parse line : " + line); 312 } 313 } 314 } 315 } 316 writeGainMapMetadataToFile(GainMapMetadata metadata)317 public void writeGainMapMetadataToFile(GainMapMetadata metadata) throws IOException { 318 try (BufferedWriter writer = new BufferedWriter(new FileWriter(mGainMapMetadaCfgFile))) { 319 writer.write("--maxContentBoost " + metadata.maxContentBoost + "\n"); 320 writer.write("--minContentBoost " + metadata.minContentBoost + "\n"); 321 writer.write("--gamma " + metadata.gamma + "\n"); 322 writer.write("--offsetSdr " + metadata.offsetSdr + "\n"); 323 writer.write("--offsetHdr " + metadata.offsetHdr + "\n"); 324 writer.write("--hdrCapacityMin " + metadata.hdrCapacityMin + "\n"); 325 writer.write("--hdrCapacityMax " + metadata.hdrCapacityMax + "\n"); 326 } 327 } 328 writeFile(String fileName, RawImage img)329 public void writeFile(String fileName, RawImage img) throws IOException { 330 try (FileOutputStream fos = new FileOutputStream(fileName)) { 331 if (img.fmt == UHDR_IMG_FMT_32bppRGBA8888 || img.fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat 332 || img.fmt == UHDR_IMG_FMT_32bppRGBA1010102) { 333 byte[] data = img.nativeOrderBuffer; 334 int bpp = img.fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4; 335 int stride = img.stride * bpp; 336 int length = img.w * bpp; 337 for (int i = 0; i < img.h; i++) { 338 fos.write(data, i * stride, length); 339 } 340 } else { 341 throw new RuntimeException("Unsupported color format "); 342 } 343 } 344 } 345 writeFile(String fileName, byte[] data)346 public void writeFile(String fileName, byte[] data) throws IOException { 347 try (FileOutputStream fos = new FileOutputStream(fileName)) { 348 fos.write(data); 349 } 350 } 351 encode()352 public void encode() throws Exception { 353 try (UltraHDREncoder handle = new UltraHDREncoder()) { 354 if (mHdrIntentRawFile != null) { 355 if (mHdrCf == UHDR_IMG_FMT_24bppYCbCrP010) { 356 fillP010ImageHandle(); 357 handle.setRawImage(mP010YData, mP010CbCrData, mWidth, mHeight, mWidth, mWidth, 358 mHdrCg, mHdrTf, mFullRange ? UHDR_CR_FULL_RANGE : UHDR_CR_LIMITED_RANGE, 359 mHdrCf, UHDR_HDR_IMG); 360 } else if (mHdrCf == UHDR_IMG_FMT_32bppRGBA1010102) { 361 fillRGBA1010102ImageHandle(); 362 handle.setRawImage(mRgba1010102Data, mWidth, mHeight, mWidth, mHdrCg, mHdrTf, 363 UHDR_CR_FULL_RANGE, mHdrCf, UHDR_HDR_IMG); 364 } else if (mHdrCf == UHDR_IMG_FMT_64bppRGBAHalfFloat) { 365 fillRGBAF16ImageHandle(); 366 handle.setRawImage(mRgbaF16Data, mWidth, mHeight, mWidth, mHdrCg, mHdrTf, 367 UHDR_CR_FULL_RANGE, mHdrCf, UHDR_HDR_IMG); 368 } else { 369 throw new IllegalArgumentException("invalid hdr intent color format " + mHdrCf); 370 } 371 } 372 if (mSdrIntentRawFile != null) { 373 if (mSdrCf == UHDR_IMG_FMT_12bppYCbCr420) { 374 fillYUV420ImageHandle(); 375 handle.setRawImage(mYuv420YData, mYuv420CbData, mYuv420CrData, mWidth, mHeight, 376 mWidth, mWidth / 2, mWidth / 2, mSdrCg, UHDR_CT_SRGB, 377 UHDR_CR_FULL_RANGE, mSdrCf, UHDR_SDR_IMG); 378 } else if (mSdrCf == UHDR_IMG_FMT_32bppRGBA8888) { 379 fillRGBA8888Handle(); 380 handle.setRawImage(mRgba8888Data, mWidth, mHeight, mWidth, mSdrCg, UHDR_CT_SRGB, 381 UHDR_CR_FULL_RANGE, mSdrCf, UHDR_SDR_IMG); 382 } else { 383 throw new IllegalArgumentException("invalid sdr intent color format " + mSdrCf); 384 } 385 } 386 if (mSdrIntentCompressedFile != null) { 387 fillSdrCompressedImageHandle(); 388 handle.setCompressedImage(mCompressedImageData, mCompressedImageData.length, mSdrCg, 389 UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, 390 (mGainMapCompressedFile != null && mGainMapMetadaCfgFile != null) ? 391 UHDR_BASE_IMG : UHDR_SDR_IMG); 392 } 393 if (mGainMapCompressedFile != null && mGainMapMetadaCfgFile != null) { 394 fillGainMapCompressedImageHandle(); 395 fillGainMapMetadataDescriptor(); 396 handle.setGainMapImageInfo(mGainMapCompressedImageData, 397 mGainMapCompressedImageData.length, mMetadata.maxContentBoost, 398 mMetadata.minContentBoost, mMetadata.gamma, mMetadata.offsetSdr, 399 mMetadata.offsetHdr, mMetadata.hdrCapacityMin, mMetadata.hdrCapacityMax); 400 } 401 if (mExifFile != null) { 402 fillExifMemoryBlock(); 403 handle.setExifData(mExifData, mExifData.length); 404 } 405 handle.setQualityFactor(mQuality, UHDR_BASE_IMG); 406 handle.setQualityFactor(mMapCompressQuality, UHDR_GAIN_MAP_IMG); 407 handle.setMultiChannelGainMapEncoding(mUseMultiChannelGainMap); 408 handle.setGainMapScaleFactor(mMapDimensionScaleFactor); 409 handle.setGainMapGamma(mGamma); 410 handle.setEncPreset(mEncPreset); 411 if (mMinContentBoost != Float.MIN_VALUE || mMaxContentBoost != Float.MAX_VALUE) { 412 handle.setMinMaxContentBoost(mMinContentBoost, mMaxContentBoost); 413 } 414 if (mTargetDispPeakBrightness != -1.0f) { 415 handle.setTargetDisplayPeakBrightness(mTargetDispPeakBrightness); 416 } 417 handle.encode(); 418 mUhdrImagedata = handle.getOutput(); 419 writeFile(mOutputFile, mUhdrImagedata); 420 } 421 } 422 decode()423 public void decode() throws Exception { 424 fillUhdrImageHandle(); 425 try (UltraHDRDecoder handle = new UltraHDRDecoder()) { 426 handle.setCompressedImage(mUhdrImagedata, mUhdrImagedata.length, UHDR_CG_UNSPECIFIED, 427 UHDR_CG_UNSPECIFIED, UHDR_CR_UNSPECIFIED); 428 handle.setColorTransfer(mOTF); 429 handle.setOutputFormat(mOfmt); 430 if (mEnableGLES) { 431 handle.enableGpuAcceleration(mEnableGLES ? 1 : 0); 432 } 433 handle.probe(); 434 if (mGainMapMetadaCfgFile != null) { 435 GainMapMetadata metadata = handle.getGainmapMetadata(); 436 writeGainMapMetadataToFile(metadata); 437 } 438 handle.decode(); 439 mDecodedUhdrRgbImage = handle.getDecodedImage(); 440 writeFile(mOutputFile, mDecodedUhdrRgbImage); 441 } 442 } 443 usage()444 public static void usage() { 445 System.out.println("\n## uhdr demo application. lib version: " + getVersionString()); 446 System.out.println("Usage : java -Djava.library.path=<path> -jar uhdr-java.jar"); 447 System.out.println(" -m mode of operation. [0:encode, 1:decode]"); 448 System.out.println("\n## encoder options :"); 449 System.out.println(" -p raw hdr intent input resource (10-bit), required for encoding" 450 + " scenarios 0, 1, 2, 3."); 451 System.out.println(" -y raw sdr intent input resource (8-bit), required for encoding" 452 + " scenarios 1, 2."); 453 System.out.println(" -a raw hdr intent color format, optional. [0:p010, " 454 + "4: rgbahalffloat, 5:rgba1010102 (default)]"); 455 System.out.println(" -b raw sdr intent color format, optional. [1:yuv420, 3:rgba8888" 456 + " (default)]"); 457 System.out.println(" -i compressed sdr intent input resource (jpeg), required for " 458 + "encoding scenarios 2, 3, 4."); 459 System.out.println(" -g compressed gainmap input resource (jpeg), required for " 460 + "encoding scenario 4."); 461 System.out.println( 462 " -w input file width, required for encoding scenarios 0, 1, 2, 3."); 463 System.out.println( 464 " -h input file height, required for encoding scenarios 0, 1, 2, 3."); 465 System.out.println( 466 " -C hdr intent color gamut, optional. [0:bt709, 1:p3 (default), 2:bt2100]"); 467 System.out.println( 468 " -c sdr intent color gamut, optional. [0:bt709 (default), 1:p3, 2:bt2100]"); 469 System.out.println( 470 " -t hdr intent color transfer, optional. [0:linear, 1:hlg (default), 2:pq]"); 471 System.out.println( 472 " It should be noted that not all combinations of input color format and" 473 + " input color transfer are supported."); 474 System.out.println( 475 " srgb color transfer shall be paired with rgba8888 or yuv420 only."); 476 System.out.println(" hlg, pq shall be paired with rgba1010102 or p010."); 477 System.out.println(" linear shall be paired with rgbahalffloat."); 478 System.out.println(" -q quality factor to be used while encoding sdr intent, " 479 + "optional. [0-100], 95 : default."); 480 System.out.println(" -R color range of hdr intent, optional. [0:narrow-range " 481 + "(default), 1:full-range]."); 482 System.out.println(" -s gainmap image downsample factor, optional. [integer values" 483 + " in range [1 - 128] (1 : default)]."); 484 System.out.println(" -Q quality factor to be used while encoding gain map image," 485 + " optional. [0-100], 95 : default."); 486 System.out.println(" -G gamma correction to be applied on the gainmap image, " 487 + "optional. [any positive real number (1.0 : default)]."); 488 System.out.println(" -M select multi channel gain map, optional. [0:disable, " 489 + " 1:enable (default)]."); 490 System.out.println(" -D select encoding preset, optional. [0:real time," 491 + " 1:best quality (default)]."); 492 System.out.println(" -k min content boost recommendation, must be in linear scale," 493 + " optional. any positive real number"); 494 System.out.println(" -K max content boost recommendation, must be in linear scale," 495 + " optional. any positive real number"); 496 System.out.println(" -L set target display peak brightness in nits, optional"); 497 System.out.println(" For HLG content, this defaults to 1000 nits."); 498 System.out.println(" For PQ content, this defaults to 10000 nits."); 499 System.out.println(" any real number in range [203, 10000]."); 500 System.out.println(" -x binary input resource containing exif data to insert, " 501 + "optional."); 502 System.out.println("\n## decoder options :"); 503 System.out.println(" -j ultra hdr compressed input resource, required."); 504 System.out.println(" -o output transfer function, optional. [0:linear," 505 + " 1:hlg (default), 2:pq, 3:srgb]"); 506 System.out.println(" -O output color format, optional. [3:rgba8888, 4:rgbahalffloat, " 507 + "5:rgba1010102 (default)]"); 508 System.out.println(" It should be noted that not all combinations of output color" 509 + " format and output"); 510 System.out.println(" transfer function are supported."); 511 System.out.println( 512 " srgb output color transfer shall be paired with rgba8888 only."); 513 System.out.println(" hlg, pq shall be paired with rgba1010102."); 514 System.out.println(" linear shall be paired with rgbahalffloat."); 515 System.out.println( 516 " -u enable gles acceleration, optional. [0:disable (default), 1:enable]."); 517 System.out.println("\n## common options :"); 518 System.out.println(" -z output filename, optional."); 519 System.out.println(" in encoding mode, default output filename 'out.jpeg'."); 520 System.out.println(" in decoding mode, default output filename 'outrgb.raw'."); 521 System.out.println(" -f gainmap metadata config file."); 522 System.out.println(" in encoding mode, resource from which gainmap metadata is " 523 + "read, required for encoding scenario 4."); 524 System.out.println(" in decoding mode, resource to which gainmap metadata is " 525 + "written, optional."); 526 System.out.println("\n## examples of usage :"); 527 System.out.println("\n## encode scenario 0 :"); 528 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 529 + "cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -a 0"); 530 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 531 + "cosmat_1920x1080_rgba1010102.raw -w 1920 -h 1080 -q 97 -a 5"); 532 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 533 + "cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -C 1 -t 2 -a 0"); 534 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 535 + "cosmat_1920x1080_rgba1010102.raw -w 1920 -h 1080 -q 97 -C 1 -t 2 -a 5"); 536 System.out.println("\n## encode scenario 1 :"); 537 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 538 + "cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 -h 1080 -q 97 " 539 + "-a 0 -b 1"); 540 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 541 + "cosmat_1920x1080_rgba1010102.raw -y cosmat_1920x1080_rgba8888.raw -w 1920 -h " 542 + "1080 -q 97 -a 5 -b 3"); 543 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 544 + "cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 -h 1080 -q 97 -C" 545 + " 2 -c 1 -t 1 -a 0 -b 1"); 546 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 547 + "cosmat_1920x1080_rgba1010102.raw -y cosmat_1920x1080_rgba8888.raw -w 1920 " 548 + "-h 1080 -q 97 -C 2 -c 1 -t 1 -a 5 -b 3"); 549 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 550 + "cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 -h 1080 -q 97 -C" 551 + " 2 -c 1 -t 1 -a 0 -b 1"); 552 System.out.println("\n## encode scenario 2 :"); 553 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 554 + "cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -i " 555 + "cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -O 3 -a 0 -b 1"); 556 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 557 + "cosmat_1920x1080_rgba1010102.raw -y cosmat_1920x1080_420.yuv -i " 558 + "cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -O 3 -a 5 -b 1"); 559 System.out.println("\n## encode scenario 3 :"); 560 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 561 + "cosmat_1920x1080_p010.yuv -i cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t " 562 + "1 -o 1 -O 5 -a 0"); 563 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 564 + "cosmat_1920x1080_rgba1010102.raw -i cosmat_1920x1080_420_8bit.jpg -w 1920 -h " 565 + "1080 -t 1 -o 1 -O 5 -a 5"); 566 System.out.println("\n## encode scenario 4 :"); 567 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -i " 568 + "cosmat_1920x1080_420_8bit.jpg -g cosmat_1920x1080_420_8bit.jpg -f metadata.cfg"); 569 System.out.println("\n## encode at high quality :"); 570 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 0 -p " 571 + "hdr_intent.raw -y sdr_intent.raw -w 640 -h 480 -c <select> -C <select> -t " 572 + "<select> -s 1 -M 1 -Q 98 -q 98 -D 1"); 573 System.out.println("\n## decode api :"); 574 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 1 " 575 + "-j cosmat_1920x1080_hdr.jpg"); 576 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 1 -j " 577 + "cosmat_1920x1080_hdr.jpg -o 3 -O 3"); 578 System.out.println(" java -Djava.library.path=<path> -jar uhdr-java.jar -m 1 -j " 579 + "cosmat_1920x1080_hdr.jpg -o 1 -O 5"); 580 System.out.println("\n"); 581 } 582 main(String[] args)583 public static void main(String[] args) throws Exception { 584 String hdr_intent_raw_file = null; 585 String sdr_intent_raw_file = null; 586 String sdr_intent_compressed_file = null; 587 String gainmap_compressed_file = null; 588 String uhdr_file = null; 589 String gainmap_metadata_cfg_file = null; 590 String output_file = null; 591 String exif_file = null; 592 int width = 0, height = 0; 593 int hdr_cg = UHDR_CG_DISPLAY_P3; 594 int sdr_cg = UHDR_CG_BT709; 595 int hdr_cf = UHDR_IMG_FMT_32bppRGBA1010102; 596 int sdr_cf = UHDR_IMG_FMT_32bppRGBA8888; 597 int hdr_tf = UHDR_CT_HLG; 598 int quality = 95; 599 int out_tf = UHDR_CT_HLG; 600 int out_cf = UHDR_IMG_FMT_32bppRGBA1010102; 601 int mode = -1; 602 int gain_map_scale_factor = 1; 603 int gainmap_compression_quality = 95; 604 int enc_preset = UHDR_USAGE_BEST_QUALITY; 605 float gamma = 1.0f; 606 boolean enable_gles = false; 607 float min_content_boost = Float.MIN_VALUE; 608 float max_content_boost = Float.MAX_VALUE; 609 float target_disp_max_brightness = -1.0f; 610 boolean use_full_range_color_hdr = false; 611 boolean use_multi_channel_gainmap = true; 612 613 for (int i = 0; i < args.length; i++) { 614 if (args[i].length() == 2 && args[i].charAt(0) == '-') { 615 switch (args[i].charAt(1)) { 616 case 'a': 617 hdr_cf = Integer.parseInt(args[++i]); 618 break; 619 case 'b': 620 sdr_cf = Integer.parseInt(args[++i]); 621 break; 622 case 'p': 623 hdr_intent_raw_file = args[++i]; 624 break; 625 case 'y': 626 sdr_intent_raw_file = args[++i]; 627 break; 628 case 'i': 629 sdr_intent_compressed_file = args[++i]; 630 break; 631 case 'g': 632 gainmap_compressed_file = args[++i]; 633 break; 634 case 'f': 635 gainmap_metadata_cfg_file = args[++i]; 636 break; 637 case 'w': 638 width = Integer.parseInt(args[++i]); 639 break; 640 case 'h': 641 height = Integer.parseInt(args[++i]); 642 break; 643 case 'C': 644 hdr_cg = Integer.parseInt(args[++i]); 645 break; 646 case 'c': 647 sdr_cg = Integer.parseInt(args[++i]); 648 break; 649 case 't': 650 hdr_tf = Integer.parseInt(args[++i]); 651 break; 652 case 'q': 653 quality = Integer.parseInt(args[++i]); 654 break; 655 case 'O': 656 out_cf = Integer.parseInt(args[++i]); 657 break; 658 case 'o': 659 out_tf = Integer.parseInt(args[++i]); 660 break; 661 case 'm': 662 mode = Integer.parseInt(args[++i]); 663 break; 664 case 'R': 665 use_full_range_color_hdr = Integer.parseInt(args[++i]) == 1; 666 break; 667 case 's': 668 gain_map_scale_factor = Integer.parseInt(args[++i]); 669 break; 670 case 'M': 671 use_multi_channel_gainmap = Integer.parseInt(args[++i]) == 1; 672 break; 673 case 'Q': 674 gainmap_compression_quality = Integer.parseInt(args[++i]); 675 break; 676 case 'G': 677 gamma = Float.parseFloat(args[++i]); 678 break; 679 case 'j': 680 uhdr_file = args[++i]; 681 break; 682 case 'z': 683 output_file = args[++i]; 684 break; 685 case 'x': 686 exif_file = args[++i]; 687 break; 688 case 'u': 689 enable_gles = Integer.parseInt(args[++i]) == 1; 690 break; 691 case 'D': 692 enc_preset = Integer.parseInt(args[++i]); 693 break; 694 case 'k': 695 min_content_boost = Float.parseFloat(args[++i]); 696 break; 697 case 'K': 698 max_content_boost = Float.parseFloat(args[++i]); 699 break; 700 case 'L': 701 target_disp_max_brightness = Float.parseFloat(args[++i]); 702 break; 703 default: 704 System.err.println("Unrecognized option, arg: " + args[i]); 705 usage(); 706 return; 707 } 708 } else { 709 System.err.println("Invalid argument format, arg: " + args[i]); 710 usage(); 711 return; 712 } 713 } 714 if (mode == 0) { 715 if (width <= 0 && gainmap_metadata_cfg_file == null) { 716 System.err.println("did not receive valid image width for encoding. width : " 717 + width); 718 return; 719 } 720 if (height <= 0 && gainmap_metadata_cfg_file == null) { 721 System.err.println("did not receive valid image height for encoding. height : " 722 + height); 723 return; 724 } 725 if (hdr_intent_raw_file == null && (sdr_intent_compressed_file == null 726 || gainmap_compressed_file == null || gainmap_metadata_cfg_file == null)) { 727 System.err.println("did not receive raw resources for encoding."); 728 return; 729 } 730 UltraHdrApp appInput = new UltraHdrApp(hdr_intent_raw_file, sdr_intent_raw_file, 731 sdr_intent_compressed_file, gainmap_compressed_file, gainmap_metadata_cfg_file, 732 exif_file, output_file != null ? output_file : "out.jpeg", width, height, 733 hdr_cf, sdr_cf, hdr_cg, sdr_cg, hdr_tf, quality, out_tf, out_cf, 734 use_full_range_color_hdr, gain_map_scale_factor, gainmap_compression_quality, 735 use_multi_channel_gainmap, gamma, enc_preset, min_content_boost, 736 max_content_boost, target_disp_max_brightness); 737 appInput.encode(); 738 } else if (mode == 1) { 739 if (uhdr_file == null) { 740 System.err.println("did not receive resources for decoding"); 741 return; 742 } 743 UltraHdrApp appInput = new UltraHdrApp(gainmap_metadata_cfg_file, uhdr_file, 744 output_file != null ? output_file : "outrgb.raw", out_tf, out_cf, enable_gles); 745 appInput.decode(); 746 } else { 747 if (args.length > 0) { 748 System.err.println("did not receive valid mode of operation"); 749 } 750 usage(); 751 } 752 } 753 } 754