Skip to content

Commit 91f0c9f

Browse files
committed
Updates, color encoding
1 parent 3bcf5b5 commit 91f0c9f

File tree

16 files changed

+311
-483
lines changed

16 files changed

+311
-483
lines changed

app/src/main/java/com/radzivon/bartoshyk/avif/MainActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ class MainActivity : AppCompatActivity() {
9393

9494
// HDR EXAMPLES - https://us.zonerama.com/williamskeaguidingphotography/Photo/1000120226/1004888131
9595
lifecycleScope.launch(Dispatchers.IO) {
96-
val buffer = this@MainActivity.assets.open("test_1.jpg").source().buffer().readByteArray()
96+
val buffer = this@MainActivity.assets.open("test_2.jpg").source().buffer().readByteArray()
9797
var bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.size)
9898

9999
bitmap = bitmap.scale(bitmap.width / 2, bitmap.height / 2)
100-
.copy(Bitmap.Config.RGBA_F16, true)
100+
.copy(Bitmap.Config.RGBA_1010102, true)
101101
lifecycleScope.launch(Dispatchers.Main) {
102102
val imageView = BindingImageViewBinding.inflate(layoutInflater, binding.scrollViewContainer, false)
103103
imageView.root.setImageBitmap(bitmap)

avif-coder/src/main/cpp/JniDecoder.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ jobject decodeImplementationNative(JNIEnv *env, jobject thiz,
206206

207207
float gamma = 1.0f;
208208

209+
if (nclx->transfer_characteristics !=
210+
heif_transfer_characteristic_ITU_R_BT_2100_0_HLG &&
211+
nclx->transfer_characteristics != heif_transfer_characteristic_ITU_R_BT_2100_0_PQ) {
212+
toneMapper = TONE_SKIP;
213+
}
214+
209215
if (nclx->transfer_characteristics ==
210216
heif_transfer_characteristic_ITU_R_BT_2100_0_HLG) {
211217
function = HLG;
@@ -228,9 +234,9 @@ jobject decodeImplementationNative(JNIEnv *env, jobject thiz,
228234
} else if (nclx->transfer_characteristics ==
229235
heif_transfer_characteristic_ITU_R_BT_601_6) {
230236
function = EOTF_BT601;
231-
} else if (
232-
nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_709_5 ||
233-
nclx->transfer_characteristics ==
237+
} else if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_709_5) {
238+
function = EOTF_SRGB;
239+
} else if (nclx->transfer_characteristics ==
234240
heif_transfer_characteristic_ITU_R_BT_2020_2_10bit ||
235241
nclx->transfer_characteristics ==
236242
heif_transfer_characteristic_ITU_R_BT_2020_2_12bit) {

avif-coder/src/main/cpp/JniException.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Copyright (c) 2023 Radzivon Bartoshyk
55
* avif-coder [https://github.com/awxkee/avif-coder]
66
*
7-
* Created by Radzivon Bartoshyk on 01/1/2023
7+
* Created by Radzivon Bartoshyk on 01/01/2023
88
*
99
* Permission is hereby granted, free of charge, to any person obtaining a copy
1010
* of this software and associated documentation files (the "Software"), to deal

avif-coder/src/main/cpp/Support.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,14 @@ bool checkDecodePreconditions(JNIEnv *env, jint javaColorspace, PreferredColorCo
7070

7171
auto mScaleMode = static_cast<ScaleMode>(javaScaleMode);
7272
if (!mScaleMode) {
73-
std::string errorString =
74-
"Invalid Scale Mode was passed";
73+
std::string errorString = "Invalid Scale Mode was passed";
7574
throwException(env, errorString);
7675
return false;
7776
}
7877

7978
auto mToneMapper = static_cast<CurveToneMapper>(javaToneMapper);
8079
if (!mScaleMode) {
81-
std::string errorString =
82-
"Invalid Tone mapper was requestedd";
80+
std::string errorString = "Invalid Tone mapper was requestedd";
8381
throwException(env, errorString);
8482
return false;
8583
}

avif-coder/src/main/cpp/coder.cpp

Lines changed: 39 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "colorspace/HDRTransferAdapter.h"
4949
#include "imagebits/RGBAlpha.h"
5050
#include "imagebits/Rgb565.h"
51+
#include "DataSpaceToNCLX.hpp"
5152

5253
using namespace std;
5354

@@ -72,7 +73,7 @@ struct heif_error writeHeifData(struct heif_context *ctx,
7273

7374
jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
7475
jobject bitmap, heif_compression_format heifCompressionFormat,
75-
int quality) {
76+
int quality, int dataSpace) {
7677
std::shared_ptr<heif_context> ctx(heif_context_alloc(),
7778
[](heif_context *c) { heif_context_free(c); });
7879
if (!ctx) {
@@ -135,15 +136,31 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
135136
heifCompressionFormat == heif_compression_AV1)) {
136137
chroma = heif_chroma_interleaved_RRGGBBAA_LE;
137138
}
138-
result = heif_image_create((int) info.width, (int) info.height, heif_colorspace_RGB,
139-
chroma, &image);
139+
140+
result = heif_image_create((int) info.width, (int) info.height,
141+
heif_colorspace_RGB, chroma, &image);
140142
if (result.code != heif_error_Ok) {
141143
std::string choke(result.message);
142144
std::string str = "Can't create encoded image with exception: " + choke;
143145
throwException(env, str);
144146
return static_cast<jbyteArray>(nullptr);
145147
}
146148

149+
std::shared_ptr<heif_color_profile_nclx> profile(heif_nclx_color_profile_alloc(), [](auto x) {
150+
heif_nclx_color_profile_free(x);
151+
});
152+
153+
bool nclxResult = coder::nclxFromDataSpace(dataSpace, profile.get());
154+
if (nclxResult) {
155+
result = heif_image_set_nclx_color_profile(image, profile.get());
156+
if (result.code != heif_error_Ok) {
157+
std::string choke(result.message);
158+
std::string str = "Can't set required color profiel: " + choke;
159+
throwException(env, str);
160+
return static_cast<jbyteArray>(nullptr);
161+
}
162+
}
163+
147164
int bitDepth = 8;
148165
if (info.format == ANDROID_BITMAP_FORMAT_RGB_565) {
149166
bitDepth = 8;
@@ -174,21 +191,24 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
174191
imgData, stride, (int) info.width, (int) info.height, 8, 255);
175192
} else if (info.format == ANDROID_BITMAP_FORMAT_RGBA_1010102) {
176193
if (heifCompressionFormat == heif_compression_HEVC) {
177-
RGBA1010102ToU8(reinterpret_cast<uint8_t *>(sourceData.data()), (int) info.stride,
178-
reinterpret_cast<uint8_t *>(imgData), stride, (int) info.width,
179-
(int) info.height);
194+
coder::RGBA1010102ToUnsigned(reinterpret_cast<const uint8_t *>(sourceData.data()),
195+
(int) info.stride,
196+
reinterpret_cast<uint8_t *>(imgData), stride,
197+
(int) info.width, (int) info.height, 8);
198+
heif_image_set_premultiplied_alpha(image, true);
180199
} else {
181-
RGBA1010102ToU16(reinterpret_cast<uint8_t *>(sourceData.data()), (int) info.stride,
182-
reinterpret_cast<uint16_t *>(imgData), stride, (int) info.width,
183-
(int) info.height);
200+
coder::RGBA1010102ToUnsigned(reinterpret_cast<uint8_t *>(sourceData.data()),
201+
(int) info.stride,
202+
reinterpret_cast<uint16_t *>(imgData), stride,
203+
(int) info.width, (int) info.height, 10);
184204
}
185205
} else if (info.format == ANDROID_BITMAP_FORMAT_RGBA_F16) {
186206
if (heifCompressionFormat == heif_compression_AV1) {
187207
coder::RGBAF16BitToNBitU16(reinterpret_cast<const uint16_t *>(sourceData.data()),
188-
(int) info.stride,
189-
reinterpret_cast<uint16_t *>(imgData), stride,
190-
(int) info.width,
191-
(int) info.height, 10);
208+
(int) info.stride,
209+
reinterpret_cast<uint16_t *>(imgData), stride,
210+
(int) info.width,
211+
(int) info.height, 10);
192212
} else {
193213
coder::RGBAF16BitToNBitU8(reinterpret_cast<const uint16_t *>(sourceData.data()),
194214
(int) info.stride,
@@ -205,59 +225,11 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
205225
});
206226
options->version = 5;
207227
options->image_orientation = heif_orientation_normal;
208-
// options->output_nclx_profile = nullptr;
209-
// options->save_two_colr_boxes_when_ICC_and_nclx_available = false;
210228

211229
result = heif_context_encode_image(ctx.get(), image, encoder.get(), options.get(), &handle);
212230
options.reset();
213231
if (handle && result.code == heif_error_Ok) {
214232
heif_context_set_primary_image(ctx.get(), handle);
215-
//
216-
// std::time_t currentTime = std::time(nullptr);
217-
// std::tm *timeInfo = std::localtime(&currentTime);
218-
//
219-
// // Format the date and time
220-
// char formattedTime[20]; // Buffer for the formatted time
221-
// std::strftime(formattedTime, sizeof(formattedTime), "%Y:%m:%d %H:%M:%S", timeInfo);
222-
// std::string dateTime(formattedTime);
223-
//
224-
// std::string format(heifCompressionFormat == heif_compression_AV1 ? "AVIF" : "HEIC");
225-
//
226-
// std::string xmpMetadata = "<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>"
227-
// "<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP Core 5.5.0'>"
228-
// "<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>"
229-
// "<rdf:Description rdf:about='' xmlns:dc='http://purl.org/dc/elements/1.1/'>"
230-
// "<dc:title>Generated image by avif-coder</dc:title>"
231-
// "<dc:creator>avif-coder</dc:creator>"
232-
// "<dc:description>A image was created by avif-coder (https://github.com/awxkee/avif-coder)</dc:description>"
233-
// "<dc:date>" + dateTime + "</dc:date>\n"
234-
// "<dc:publisher>https://github.com/awxkee/avif-coder>"
235-
// "<dc:format>" + format + "</dc:format>"
236-
// "</rdf:Description>"
237-
// "<rdf:Description rdf:about='' xmlns:exif='http://ns.adobe.com/exif/1.0/'>\n"
238-
// "<exif:ColorSpace>sRGB</exif:ColorSpace>\n"
239-
// "<exif:ColorProfile>sRGB IEC61966-2.1</exif:ColorProfile>\n"
240-
// "</rdf:Description>\n"
241-
// "<rdf:Description rdf:about='' xmlns:xmp='http://ns.adobe.com/xap/1.0/'>\n"
242-
// "<xmp:CreatorTool>avif-coder (https://github.com/awxkee/avif-coder)</xmp:CreatorTool>\n"
243-
// "<xmp:ModifyDate>" +
244-
// dateTime +
245-
// "</xmp:ModifyDate>\n"
246-
// "</rdf:Description>\n"
247-
// "</rdf:RDF>"
248-
// "</x:xmpmeta>"
249-
// "<?xpacket end='w'?>";
250-
//
251-
// result = heif_context_add_XMP_metadata(ctx.get(), handle,
252-
// reinterpret_cast<const void *>(xmpMetadata.data()),
253-
// static_cast<int>(xmpMetadata.size()));
254-
// if (result.code != heif_error_Ok) {
255-
// heif_image_handle_release(handle);
256-
// heif_image_release(image);
257-
// throwCantEncodeImageException(env, result.message);
258-
// return static_cast<jbyteArray>(nullptr);
259-
// }
260-
261233
heif_image_handle_release(handle);
262234
}
263235
heif_image_release(image);
@@ -293,9 +265,10 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
293265
extern "C"
294266
JNIEXPORT jbyteArray JNICALL
295267
Java_com_radzivon_bartoshyk_avif_coder_HeifCoder_encodeAvifImpl(JNIEnv *env, jobject thiz,
296-
jobject bitmap, jint quality) {
268+
jobject bitmap, jint quality,
269+
jint dataSpace) {
297270
try {
298-
return encodeBitmap(env, thiz, bitmap, heif_compression_AV1, quality);
271+
return encodeBitmap(env, thiz, bitmap, heif_compression_AV1, quality, dataSpace);
299272
} catch (std::bad_alloc &err) {
300273
std::string exception = "Not enough memory to encode this image";
301274
throwException(env, exception);
@@ -306,9 +279,10 @@ Java_com_radzivon_bartoshyk_avif_coder_HeifCoder_encodeAvifImpl(JNIEnv *env, job
306279
extern "C"
307280
JNIEXPORT jbyteArray JNICALL
308281
Java_com_radzivon_bartoshyk_avif_coder_HeifCoder_encodeHeicImpl(JNIEnv *env, jobject thiz,
309-
jobject bitmap, jint quality) {
282+
jobject bitmap, jint quality,
283+
jint dataSpace) {
310284
try {
311-
return encodeBitmap(env, thiz, bitmap, heif_compression_HEVC, quality);
285+
return encodeBitmap(env, thiz, bitmap, heif_compression_HEVC, quality, dataSpace);
312286
} catch (std::bad_alloc &err) {
313287
std::string exception = "Not enough memory to encode this image";
314288
throwException(env, exception);
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2023 Radzivon Bartoshyk
5+
* avif-coder [https://github.com/awxkee/avif-coder]
6+
*
7+
* Created by Radzivon Bartoshyk on 07/03/2024
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in all
17+
* copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25+
* SOFTWARE.
26+
*
27+
*/
28+
29+
#pragma once
30+
31+
#include <android/data_space.h>
32+
#include "libheif/heif.h"
33+
#include "Eigen/Eigen"
34+
#include "ColorSpaceProfile.h"
35+
36+
namespace coder {
37+
bool nclxFromDataSpace(int dataSpace, heif_color_profile_nclx *profile) {
38+
if (dataSpace == -1) {
39+
return false;
40+
}
41+
42+
bool isResolved = false;
43+
44+
if (dataSpace == ADataSpace::ADATASPACE_UNKNOWN) {
45+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_709_5;
46+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_709_5;
47+
profile->color_primaries = heif_color_primaries_ITU_R_BT_709_5;
48+
isResolved = true;
49+
} else if (dataSpace == ADataSpace::ADATASPACE_BT709) {
50+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_709_5;
51+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_709_5;
52+
profile->color_primaries = heif_color_primaries_ITU_R_BT_709_5;
53+
isResolved = true;
54+
} else if (dataSpace == ADataSpace::ADATASPACE_SRGB) {
55+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_709_5;
56+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_709_5;
57+
profile->color_primaries = heif_color_primaries_ITU_R_BT_709_5;
58+
isResolved = true;
59+
profile->full_range_flag = true;
60+
} else if (dataSpace == ADataSpace::ADATASPACE_BT2020) {
61+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_2020_2_10bit;
62+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance;
63+
profile->color_primaries = heif_color_primaries_ITU_R_BT_2020_2_and_2100_0;
64+
isResolved = true;
65+
} else if (dataSpace == ADataSpace::ADATASPACE_DISPLAY_P3) {
66+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_709_5;
67+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance;
68+
profile->color_primaries = heif_color_primaries_SMPTE_EG_432_1;
69+
profile->full_range_flag = true;
70+
isResolved = true;
71+
} else if (dataSpace == ADataSpace::ADATASPACE_DCI_P3) {
72+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G;
73+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance;
74+
profile->color_primaries = heif_color_primaries_SMPTE_EG_432_1;
75+
profile->full_range_flag = true;
76+
isResolved = true;
77+
} else if (dataSpace == ADataSpace::ADATASPACE_SCRGB_LINEAR) {
78+
profile->transfer_characteristics = heif_transfer_characteristic_linear;
79+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_709_5;
80+
profile->color_primaries = heif_color_primaries_ITU_R_BT_709_5;
81+
isResolved = true;
82+
} else if ((dataSpace == ADataSpace::ADATASPACE_BT2020_ITU_PQ ||
83+
dataSpace == ADataSpace::ADATASPACE_BT2020_HLG ||
84+
dataSpace == ADataSpace::ADATASPACE_BT2020_ITU_HLG)) {
85+
heif_transfer_characteristics transfer = heif_transfer_characteristic_ITU_R_BT_2100_0_PQ;
86+
if (dataSpace == ADataSpace::ADATASPACE_BT2020_HLG ||
87+
dataSpace == ADataSpace::ADATASPACE_BT2020_ITU_HLG) {
88+
transfer = heif_transfer_characteristic_ITU_R_BT_2100_0_HLG;
89+
}
90+
profile->transfer_characteristics = transfer;
91+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance;
92+
profile->color_primaries = heif_color_primaries_ITU_R_BT_2020_2_and_2100_0;
93+
isResolved = true;
94+
} else if (dataSpace == ADataSpace::ADATASPACE_ADOBE_RGB) {
95+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_709_5;
96+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_709_5;
97+
profile->color_primaries = heif_color_primaries_unspecified;
98+
isResolved = true;
99+
} else if (dataSpace == ADataSpace::ADATASPACE_BT601_525 ||
100+
dataSpace == ADataSpace::ADATASPACE_BT601_625 ||
101+
dataSpace == ADataSpace::ADATASPACE_JFIF) {
102+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_601_6;
103+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6;
104+
profile->color_primaries = heif_color_primaries_ITU_R_BT_601_6;
105+
isResolved = true;
106+
} else if (dataSpace & ADataSpace::STANDARD_BT470M) {
107+
profile->transfer_characteristics = heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G;
108+
profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G;
109+
profile->color_primaries = heif_color_primaries_ITU_R_BT_470_6_System_M;
110+
isResolved = true;
111+
}
112+
113+
return isResolved;
114+
}
115+
}

0 commit comments

Comments
 (0)