xref: /aosp_15_r20/external/libultrahdr/java/com/google/media/codecs/ultrahdr/UltraHDRDecoder.java (revision 89a0ef05262152531a00a15832a2d3b1e3990773)
1 /*
2  * Copyright (C) 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 package com.google.media.codecs.ultrahdr;
18 
19 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_CG_UNSPECIFIED;
20 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_CR_UNSPECIFIED;
21 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_CT_UNSPECIFIED;
22 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_32bppRGBA1010102;
23 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_32bppRGBA8888;
24 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_64bppRGBAHalfFloat;
25 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_8bppYCbCr400;
26 import static com.google.media.codecs.ultrahdr.UltraHDRCommon.UHDR_IMG_FMT_UNSPECIFIED;
27 
28 import java.io.IOException;
29 import java.nio.ByteBuffer;
30 import java.nio.ByteOrder;
31 
32 /**
33  * Ultra HDR decoding utility class.
34  */
35 public class UltraHDRDecoder implements AutoCloseable {
36 
37     /**
38      * GainMap Metadata Descriptor
39      */
40     public static class GainMapMetadata {
41         public float maxContentBoost;
42         public float minContentBoost;
43         public float gamma;
44         public float offsetSdr;
45         public float offsetHdr;
46         public float hdrCapacityMin;
47         public float hdrCapacityMax;
48 
GainMapMetadata()49         public GainMapMetadata() {
50             this.maxContentBoost = 1.0f;
51             this.minContentBoost = 1.0f;
52             this.gamma = 1.0f;
53             this.offsetSdr = 0.0f;
54             this.offsetHdr = 0.0f;
55             this.hdrCapacityMin = 1.0f;
56             this.hdrCapacityMax = 1.0f;
57         }
58 
GainMapMetadata(float maxContentBoost, float minContentBoost, float gamma, float offsetSdr, float offsetHdr, float hdrCapacityMin, float hdrCapacityMax)59         public GainMapMetadata(float maxContentBoost, float minContentBoost, float gamma,
60                 float offsetSdr, float offsetHdr, float hdrCapacityMin, float hdrCapacityMax) {
61             this.maxContentBoost = maxContentBoost;
62             this.minContentBoost = minContentBoost;
63             this.gamma = gamma;
64             this.offsetSdr = offsetSdr;
65             this.offsetHdr = offsetHdr;
66             this.hdrCapacityMin = hdrCapacityMin;
67             this.hdrCapacityMax = hdrCapacityMax;
68         }
69     }
70 
71     /**
72      * Raw Image Descriptor.
73      */
74     public static abstract class RawImage {
75         public byte[] nativeOrderBuffer;
76         public int fmt;
77         public int cg;
78         public int ct;
79         public int range;
80         public int w;
81         public int h;
82         public int stride;
83 
RawImage(byte[] nativeOrderBuffer, int fmt, int cg, int ct, int range, int w, int h, int stride)84         public RawImage(byte[] nativeOrderBuffer, int fmt, int cg, int ct, int range, int w, int h,
85                 int stride) {
86             this.nativeOrderBuffer = nativeOrderBuffer;
87             this.fmt = fmt;
88             this.cg = cg;
89             this.ct = ct;
90             this.range = range;
91             this.w = w;
92             this.h = h;
93             this.stride = stride;
94         }
95     }
96 
97     /**
98      * To represent packed pixel formats with 4 bytes-per-sample.
99      */
100     public static class RawImage32 extends RawImage {
101         public int[] data;
102 
RawImage32(byte[] nativeOrderBuffer, int fmt, int cg, int ct, int range, int w, int h, int[] data, int stride)103         public RawImage32(byte[] nativeOrderBuffer, int fmt, int cg, int ct, int range, int w,
104                 int h, int[] data, int stride) {
105             super(nativeOrderBuffer, fmt, cg, ct, range, w, h, stride);
106             this.data = data;
107         }
108     }
109 
110     /**
111      * To represent packed pixel formats with 8 bits-per-sample.
112      */
113     public static class RawImage8 extends RawImage {
114         public byte[] data;
115 
RawImage8(byte[] nativeOrderBuffer, int fmt, int cg, int ct, int range, int w, int h, byte[] data, int stride)116         public RawImage8(byte[] nativeOrderBuffer, int fmt, int cg, int ct, int range, int w, int h,
117                 byte[] data, int stride) {
118             super(nativeOrderBuffer, fmt, cg, ct, range, w, h, stride);
119             this.data = data;
120         }
121     }
122 
123     /**
124      * To represent packed pixel formats with 8 bytes-per-sample.
125      */
126     public static class RawImage64 extends RawImage {
127         public long[] data;
128 
RawImage64(byte[] nativeOrderBuffer, int fmt, int cg, int ct, int range, int w, int h, long[] data, int stride)129         public RawImage64(byte[] nativeOrderBuffer, int fmt, int cg, int ct, int range, int w,
130                 int h, long[] data, int stride) {
131             super(nativeOrderBuffer, fmt, cg, ct, range, w, h, stride);
132             this.data = data;
133         }
134     }
135 
136     // APIs
137 
138     /**
139      * Checks if the current input image is a valid ultrahdr image
140      *
141      * @param data The compressed image data.
142      * @param size The size of the compressed image data.
143      * @return TRUE if the input data has a primary image, gainmap image and gainmap metadata.
144      * FALSE if any errors are encountered during parsing process or if the image does not have
145      * primary image or gainmap image or gainmap metadata
146      * @throws IOException If parameters are not valid exception is thrown.
147      */
isUHDRImage(byte[] data, int size)148     public static boolean isUHDRImage(byte[] data, int size) throws IOException {
149         if (data == null) {
150             throw new IOException("received null for image data handle");
151         }
152         if (size <= 0) {
153             throw new IOException("received invalid compressed image size, size is <= 0");
154         }
155         return (isUHDRImageNative(data, size) == 1);
156     }
157 
158     /**
159      * Create and Initialize an ultrahdr decoder instance
160      *
161      * @throws IOException If the codec cannot be created then exception is thrown
162      */
UltraHDRDecoder()163     public UltraHDRDecoder() throws IOException {
164         handle = 0;
165         init();
166         resetState();
167     }
168 
169     /**
170      * Release current ultrahdr decoder instance
171      *
172      * @throws Exception during release, if errors are seen, then exception is thrown
173      */
174     @Override
close()175     public void close() throws Exception {
176         destroy();
177         resetState();
178     }
179 
180     /**
181      * Add compressed image data to be decoded to the decoder context. The function goes through
182      * all the arguments and checks for their sanity. If no anomalies are seen then the image
183      * info is added to internal list. Repeated calls to this function will replace the old entry
184      * with the current.
185      *
186      * @param data          The compressed image data.
187      * @param size          The size of the compressed image data.
188      * @param colorGamut    color standard of the image. Certain image formats are capable of
189      *                      storing color standard information in the bitstream, for instance heif.
190      *                      Some formats are not capable of storing the same. This field can be used
191      *                      as an additional source to convey this information. If unknown, this can
192      *                      be set to {@link UltraHDRCommon#UHDR_CG_UNSPECIFIED}.
193      * @param colorTransfer color transfer of the image. Just like colorGamut parameter, this
194      *                      field can be used as an additional source to convey image transfer
195      *                      characteristics. If unknown, this can be set to
196      *                      {@link UltraHDRCommon#UHDR_CT_UNSPECIFIED}.
197      * @param range         color range. Just like colorGamut parameter, this field can be used
198      *                      as an additional source to convey color range characteristics. If
199      *                      unknown, this can be set to {@link UltraHDRCommon#UHDR_CR_UNSPECIFIED}.
200      * @throws IOException If parameters are not valid or current decoder instance is not valid
201      *                     or current decoder instance is not suitable for configuration
202      *                     exception is thrown
203      */
setCompressedImage(byte[] data, int size, int colorGamut, int colorTransfer, int range)204     public void setCompressedImage(byte[] data, int size, int colorGamut, int colorTransfer,
205             int range) throws IOException {
206         if (data == null) {
207             throw new IOException("received null for image data handle");
208         }
209         if (size <= 0) {
210             throw new IOException("received invalid compressed image size, size is <= 0");
211         }
212         setCompressedImageNative(data, size, colorGamut, colorTransfer, range);
213     }
214 
215     /**
216      * Set output image color format
217      *
218      * @param fmt output image color format. Supported values are
219      *            {@link UltraHDRCommon#UHDR_IMG_FMT_32bppRGBA8888},
220      *            {@link UltraHDRCommon#UHDR_IMG_FMT_32bppRGBA1010102},
221      *            {@link UltraHDRCommon#UHDR_IMG_FMT_64bppRGBAHalfFloat}
222      * @throws IOException If parameters are not valid or current decoder instance is not valid
223      *                     or current decoder instance is not suitable for configuration
224      *                     exception is thrown
225      */
setOutputFormat(int fmt)226     public void setOutputFormat(int fmt) throws IOException {
227         setOutputFormatNative(fmt);
228     }
229 
230     /**
231      * Set output image color transfer characteristics. It should be noted that not all
232      * combinations of output color format and output transfer function are supported.
233      * {@link UltraHDRCommon#UHDR_CT_SRGB} output color transfer shall be paired with
234      * {@link UltraHDRCommon#UHDR_IMG_FMT_32bppRGBA8888} only. {@link UltraHDRCommon#UHDR_CT_HLG}
235      * and {@link UltraHDRCommon#UHDR_CT_PQ} shall be paired with
236      * {@link UltraHDRCommon#UHDR_IMG_FMT_32bppRGBA1010102}.
237      * {@link UltraHDRCommon#UHDR_CT_LINEAR} shall be paired with
238      * {@link UltraHDRCommon#UHDR_IMG_FMT_64bppRGBAHalfFloat}.
239      *
240      * @param ct output image color transfer.
241      * @throws IOException If parameters are not valid or current decoder instance is not valid
242      *                     or current decoder instance is not suitable for configuration
243      *                     exception is thrown
244      */
setColorTransfer(int ct)245     public void setColorTransfer(int ct) throws IOException {
246         setColorTransferNative(ct);
247     }
248 
249     /**
250      * Set output display's HDR capacity. Value MUST be in linear scale. This value determines
251      * the weight by which the gain map coefficients are scaled. If no value is configured, no
252      * weight is applied to gainmap image.
253      *
254      * @param displayBoost hdr capacity of target display. Any real number >= 1.0f
255      * @throws IOException If parameters are not valid or current decoder instance is not valid
256      *                     or current decoder instance is not suitable for configuration
257      *                     exception is thrown
258      */
setMaxDisplayBoost(float displayBoost)259     public void setMaxDisplayBoost(float displayBoost) throws IOException {
260         setMaxDisplayBoostNative(displayBoost);
261     }
262 
263     /**
264      * Enable/Disable GPU acceleration. If enabled, certain operations (if possible) of uhdr
265      * decode will be offloaded to GPU.
266      * <p>
267      * NOTE: It is entirely possible for this API to have no effect on the decode operation
268      *
269      * @param enable enable/disable gpu acceleration
270      * @throws IOException If current decoder instance is not valid or current decoder instance
271      *                     is not suitable for configuration exception is thrown.
272      */
enableGpuAcceleration(int enable)273     public void enableGpuAcceleration(int enable) throws IOException {
274         enableGpuAccelerationNative(enable);
275     }
276 
277     /**
278      * This function parses the bitstream that is registered with the decoder context and makes
279      * image information available to the client via getter functions. It does not decompress the
280      * image. That is done by {@link UltraHDRDecoder#decode()}.
281      *
282      * @throws IOException during parsing process if any errors are seen exception is thrown
283      */
probe()284     public void probe() throws IOException {
285         probeNative();
286     }
287 
288     /**
289      * Get base image width
290      *
291      * @return base image width
292      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
293      *                     process if any errors are seen exception is thrown
294      */
getImageWidth()295     public int getImageWidth() throws IOException {
296         return getImageWidthNative();
297     }
298 
299     /**
300      * Get base image height
301      *
302      * @return base image height
303      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
304      *                     process if any errors are seen exception is thrown
305      */
getImageHeight()306     public int getImageHeight() throws IOException {
307         return getImageHeightNative();
308     }
309 
310     /**
311      * Get gainmap image width
312      *
313      * @return gainmap image width
314      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
315      *                     process if any errors are seen exception is thrown
316      */
getGainMapWidth()317     public int getGainMapWidth() throws IOException {
318         return getGainMapWidthNative();
319     }
320 
321     /**
322      * Get gainmap image height
323      *
324      * @return gainmap image height
325      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
326      *                     process if any errors are seen exception is thrown
327      */
getGainMapHeight()328     public int getGainMapHeight() throws IOException {
329         return getGainMapHeightNative();
330     }
331 
332     /**
333      * Get exif information
334      *
335      * @return A byte array containing the EXIF metadata
336      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
337      *                     process if any errors are seen exception is thrown
338      */
getExif()339     public byte[] getExif() throws IOException {
340         return getExifNative();
341     }
342 
343     /**
344      * Get icc information
345      *
346      * @return A byte array containing the icc data
347      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
348      *                     process if any errors are seen exception is thrown
349      */
getIcc()350     public byte[] getIcc() throws IOException {
351         return getIccNative();
352     }
353 
354     /**
355      * Get base image (compressed)
356      *
357      * @return A byte array containing the base image data
358      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
359      *                     process if any errors are seen exception is thrown
360      */
getBaseImage()361     public byte[] getBaseImage() throws IOException {
362         return getBaseImageNative();
363     }
364 
365     /**
366      * Get gain map image (compressed)
367      *
368      * @return A byte array containing the gain map image data
369      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
370      *                     process if any errors are seen exception is thrown
371      */
getGainMapImage()372     public byte[] getGainMapImage() throws IOException {
373         return getGainMapImageNative();
374     }
375 
376     /**
377      * Get gain map metadata
378      *
379      * @return gainmap metadata descriptor
380      * @throws IOException If {@link UltraHDRDecoder#probe()} is not yet called or during parsing
381      *                     process if any errors are seen exception is thrown
382      */
getGainmapMetadata()383     public GainMapMetadata getGainmapMetadata() throws IOException {
384         getGainmapMetadataNative();
385         return new GainMapMetadata(maxContentBoost, minContentBoost, gamma, offsetSdr,
386                 offsetHdr, hdrCapacityMin, hdrCapacityMax);
387     }
388 
389     /**
390      * Decode process call.
391      * <p>
392      * After initializing the decode context, call to this function will submit data for
393      * encoding. If the call is successful, the decode output is stored internally and is
394      * accessible via {@link UltraHDRDecoder#getDecodedImage()}.
395      *
396      * @throws IOException If any errors are encountered during the decoding process, exception is
397      *                     thrown
398      */
decode()399     public void decode() throws IOException {
400         decodeNative();
401     }
402 
403     /**
404      * Get decoded image data
405      *
406      * @return Raw image descriptor containing decoded image data
407      * @throws IOException If {@link UltraHDRDecoder#decode()} is not called or decoding process
408      *                     is not successful, exception is thrown
409      */
getDecodedImage()410     public RawImage getDecodedImage() throws IOException {
411         if (decodedDataNativeOrder == null) {
412             decodedDataNativeOrder = getDecodedImageNative();
413         }
414         if (imgFormat == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
415             if (decodedDataInt64 == null) {
416                 ByteBuffer data = ByteBuffer.wrap(decodedDataNativeOrder);
417                 data.order(ByteOrder.nativeOrder());
418                 decodedDataInt64 = new long[imgWidth * imgHeight];
419                 data.asLongBuffer().get(decodedDataInt64);
420             }
421             return new RawImage64(decodedDataNativeOrder, imgFormat, imgGamut, imgTransfer,
422                     imgRange, imgWidth, imgHeight, decodedDataInt64, imgStride);
423         } else if (imgFormat == UHDR_IMG_FMT_32bppRGBA8888
424                 || imgFormat == UHDR_IMG_FMT_32bppRGBA1010102) {
425             if (decodedDataInt32 == null) {
426                 ByteBuffer data = ByteBuffer.wrap(decodedDataNativeOrder);
427                 data.order(ByteOrder.nativeOrder());
428                 decodedDataInt32 = new int[imgWidth * imgHeight];
429                 data.asIntBuffer().get(decodedDataInt32);
430             }
431             return new RawImage32(decodedDataNativeOrder, imgFormat, imgGamut, imgTransfer,
432                     imgRange, imgWidth, imgHeight, decodedDataInt32, imgStride);
433         }
434         return null;
435     }
436 
437     /**
438      * Get decoded gainmap image data
439      *
440      * @return Raw image descriptor containing decoded gainmap image data
441      * @throws IOException If {@link UltraHDRDecoder#decode()} is not called or decoding process
442      *                     is not successful, exception is thrown
443      */
getDecodedGainMapImage()444     public RawImage getDecodedGainMapImage() throws IOException {
445         if (decodedGainMapDataNativeOrder == null) {
446             decodedGainMapDataNativeOrder = getDecodedGainMapImageNative();
447         }
448         if (gainmapFormat == UHDR_IMG_FMT_32bppRGBA8888) {
449             if (decodedGainMapDataInt32 == null) {
450                 ByteBuffer data = ByteBuffer.wrap(decodedGainMapDataNativeOrder);
451                 data.order(ByteOrder.nativeOrder());
452                 decodedGainMapDataInt32 = new int[imgWidth * imgHeight];
453                 data.asIntBuffer().get(decodedGainMapDataInt32);
454             }
455             return new RawImage32(decodedGainMapDataNativeOrder, imgFormat, imgGamut, imgTransfer,
456                     imgRange, imgWidth, imgHeight, decodedGainMapDataInt32, imgStride);
457         } else if (imgFormat == UHDR_IMG_FMT_8bppYCbCr400) {
458             return new RawImage8(decodedGainMapDataNativeOrder, gainmapFormat, UHDR_CG_UNSPECIFIED,
459                     UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, gainmapWidth, gainmapHeight,
460                     decodedGainMapDataNativeOrder, gainmapStride);
461         }
462         return null;
463     }
464 
465     /**
466      * Reset decoder instance. Clears all previous settings and resets to default state and ready
467      * for re-initialization and usage.
468      *
469      * @throws IOException If the current decoder instance is not valid exception is thrown.
470      */
reset()471     public void reset() throws IOException {
472         resetNative();
473         resetState();
474     }
475 
resetState()476     private void resetState() {
477         maxContentBoost = 1.0f;
478         minContentBoost = 1.0f;
479         gamma = 1.0f;
480         offsetSdr = 0.0f;
481         offsetHdr = 0.0f;
482         hdrCapacityMin = 1.0f;
483         hdrCapacityMax = 1.0f;
484 
485         decodedDataNativeOrder = null;
486         decodedDataInt32 = null;
487         decodedDataInt64 = null;
488         imgWidth = -1;
489         imgHeight = -1;
490         imgStride = 0;
491         imgFormat = UHDR_IMG_FMT_UNSPECIFIED;
492         imgGamut = UHDR_CG_UNSPECIFIED;
493         imgTransfer = UHDR_CG_UNSPECIFIED;
494         imgRange = UHDR_CG_UNSPECIFIED;
495 
496         decodedGainMapDataNativeOrder = null;
497         decodedGainMapDataInt32 = null;
498         gainmapWidth = -1;
499         gainmapHeight = -1;
500         gainmapStride = 0;
501         gainmapFormat = UHDR_IMG_FMT_UNSPECIFIED;
502     }
503 
isUHDRImageNative(byte[] data, int size)504     private static native int isUHDRImageNative(byte[] data, int size) throws IOException;
505 
init()506     private native void init() throws IOException;
507 
destroy()508     private native void destroy() throws IOException;
509 
setCompressedImageNative(byte[] data, int size, int colorGamut, int colorTransfer, int range)510     private native void setCompressedImageNative(byte[] data, int size, int colorGamut,
511             int colorTransfer, int range) throws IOException;
512 
setOutputFormatNative(int fmt)513     private native void setOutputFormatNative(int fmt) throws IOException;
514 
setColorTransferNative(int ct)515     private native void setColorTransferNative(int ct) throws IOException;
516 
setMaxDisplayBoostNative(float displayBoost)517     private native void setMaxDisplayBoostNative(float displayBoost) throws IOException;
518 
enableGpuAccelerationNative(int enable)519     private native void enableGpuAccelerationNative(int enable) throws IOException;
520 
probeNative()521     private native void probeNative() throws IOException;
522 
getImageWidthNative()523     private native int getImageWidthNative() throws IOException;
524 
getImageHeightNative()525     private native int getImageHeightNative() throws IOException;
526 
getGainMapWidthNative()527     private native int getGainMapWidthNative() throws IOException;
528 
getGainMapHeightNative()529     private native int getGainMapHeightNative() throws IOException;
530 
getExifNative()531     private native byte[] getExifNative() throws IOException;
532 
getIccNative()533     private native byte[] getIccNative() throws IOException;
534 
getBaseImageNative()535     private native byte[] getBaseImageNative() throws IOException;
536 
getGainMapImageNative()537     private native byte[] getGainMapImageNative() throws IOException;
538 
getGainmapMetadataNative()539     private native void getGainmapMetadataNative() throws IOException;
540 
decodeNative()541     private native void decodeNative() throws IOException;
542 
getDecodedImageNative()543     private native byte[] getDecodedImageNative() throws IOException;
544 
getDecodedGainMapImageNative()545     private native byte[] getDecodedGainMapImageNative() throws IOException;
546 
resetNative()547     private native void resetNative() throws IOException;
548 
549     /**
550      * Decoder handle. Filled by {@link UltraHDRDecoder#init()}
551      */
552     private long handle;
553 
554     /**
555      * gainmap metadata fields. Filled by {@link UltraHDRDecoder#getGainmapMetadataNative()}
556      */
557     private float maxContentBoost;
558     private float minContentBoost;
559     private float gamma;
560     private float offsetSdr;
561     private float offsetHdr;
562     private float hdrCapacityMin;
563     private float hdrCapacityMax;
564 
565     /**
566      * decoded image fields. Filled by {@link UltraHDRDecoder#getDecodedImageNative()}
567      */
568     private byte[] decodedDataNativeOrder;
569     private int[] decodedDataInt32;
570     private long[] decodedDataInt64;
571     private int imgWidth;
572     private int imgHeight;
573     private int imgStride;
574     private int imgFormat;
575     private int imgGamut;
576     private int imgTransfer;
577     private int imgRange;
578 
579     /**
580      * decoded image fields. Filled by {@link UltraHDRDecoder#getDecodedGainMapImageNative()}
581      */
582     private byte[] decodedGainMapDataNativeOrder;
583     private int[] decodedGainMapDataInt32;
584     private int gainmapWidth;
585     private int gainmapHeight;
586     private int gainmapStride;
587     private int gainmapFormat;
588 
589     static {
590         System.loadLibrary("uhdrjni");
591     }
592 }
593