Skip to content

Commit 5e7fb3b

Browse files
committed
Improved encoding, improved decoding, added loseless chroma encoding mode
1 parent de7bdd4 commit 5e7fb3b

24 files changed

+456
-1240
lines changed

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

+11-9
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,9 @@ class MainActivity : AppCompatActivity() {
122122
var allFiles = mutableListOf<String>()
123123
allFiles.addAll(allFiles2)
124124
allFiles.addAll(allFiles1)
125-
allFiles = allFiles.filter { it.contains("wide_gamut.avif") || it.contains("IMG_0199_rr.avif") || it.contains("bt_2020_pq.avif") }.toMutableList()
125+
// allFiles = allFiles.filter { it.contains("wide_gamut.avif") || it.contains("IMG_0199_rr.avif") || it.contains("bt_2020_pq.avif") }.toMutableList()
126126
// allFiles = allFiles.filter { it.contains("bbb_alpha_inverted.avif") }.toMutableList()
127+
// allFiles = allFiles.filter { it.contains("bt_2020_pq.avif") }.toMutableList()
127128
for (file in allFiles) {
128129
try {
129130
Log.d("AVIF", "start processing $file")
@@ -148,14 +149,15 @@ class MainActivity : AppCompatActivity() {
148149

149150
Log.d("AVIFFFF", "Decode time ${System.currentTimeMillis() - start}")
150151

151-
// val encode = coder.encodeAvif(bitmap0, avifChromaSubsampling = AvifChromaSubsampling.YUV400)
152+
val encode = coder.encodeAvif(bitmap0, avifChromaSubsampling = AvifChromaSubsampling.YUV422)
153+
val roundTripped = coder.decode(encode)
152154
//
153-
// val cachedFile = File(cacheDir, "yuv400.avif")
154-
// FileOutputStream(cachedFile).use {
155-
// val bf = it.sink().buffer()
156-
// bf.write(encode)
157-
// bf.flush()
158-
// }
155+
val cachedFile = File(cacheDir, "yuv400.avif")
156+
FileOutputStream(cachedFile).use {
157+
val bf = it.sink().buffer()
158+
bf.write(encode)
159+
bf.flush()
160+
}
159161
//
160162
// val round = coder.decode(
161163
// byteArray = encode,
@@ -170,7 +172,7 @@ class MainActivity : AppCompatActivity() {
170172
binding.scrollViewContainer,
171173
false
172174
)
173-
imageView.root.setImageBitmap(bitmap0)
175+
imageView.root.setImageBitmap(roundTripped)
174176
binding.scrollViewContainer.addView(imageView.root)
175177
}
176178
// lifecycleScope.launch(Dispatchers.Main) {

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ AvifImageFrame AvifDecoderController::getFrame(uint32_t frame,
290290
throw std::runtime_error(str);
291291
}
292292

293+
float intensityTarget =
294+
decoder->image->clli.maxCLL == 0 ? 1000.0f : static_cast<float>(decoder->image->clli.maxCLL);
295+
293296
aligned_uint8_vector iccProfile(0);
294297
if (decoder->image->icc.data && decoder->image->icc.size) {
295298
iccProfile.resize(decoder->image->icc.size);
@@ -409,11 +412,11 @@ AvifImageFrame AvifDecoderController::getFrame(uint32_t frame,
409412
forwardTrc,
410413
TransferFunction::Srgb,
411414
toneMapper,
412-
coeffs);
415+
coeffs, intensityTarget);
413416
} else {
414417
applyColorMatrix(reinterpret_cast<uint8_t *>(imageStore.data()), stride, imageWidth,
415418
imageHeight, matrix, forwardTrc, TransferFunction::Srgb, toneMapper,
416-
coeffs);
419+
coeffs, intensityTarget);
417420
}
418421

419422
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ add_library(coder SHARED JniEncoder.cpp JniException.cpp SizeScaler.cpp
2020
colorspace/Rec2408ToneMapper.cpp colorspace/LogarithmicToneMapper.cpp
2121
colorspace/ColorMatrix.cpp imagebits/ScanAlpha.cpp imagebits/Rgba16.cpp
2222
AvifDecoderController.cpp HeifImageDecoder.cpp JniAnimatedController.cpp
23-
colorspace/FilmicToneMapper.cpp)
23+
colorspace/FilmicToneMapper.cpp colorspace/AcesToneMapper.cpp)
2424

2525
add_library(libheif SHARED IMPORTED)
2626
add_library(libyuv STATIC IMPORTED)

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ AvifImageFrame HeifImageDecoder::getFrame(std::vector<uint8_t> &srcBuffer,
8989
heif_image_release(im);
9090
});
9191

92+
float intensityTarget = 1000.0f;
93+
94+
if (heif_image_has_content_light_level(img.get())) {
95+
heif_content_light_level light_level = {0};
96+
heif_image_get_content_light_level(img.get(), &light_level);
97+
if (light_level.max_content_light_level != 0) {
98+
intensityTarget = light_level.max_content_light_level;
99+
}
100+
}
101+
92102
std::vector<uint8_t> profile;
93103
std::string colorProfile;
94104

@@ -231,11 +241,11 @@ AvifImageFrame HeifImageDecoder::getFrame(std::vector<uint8_t> &srcBuffer,
231241
forwardTrc,
232242
TransferFunction::Srgb,
233243
toneMapper,
234-
coeffs);
244+
coeffs, intensityTarget);
235245
} else {
236246
applyColorMatrix(reinterpret_cast<uint8_t *>(dstARGB.data()), stride, imageWidth,
237247
imageHeight, matrix, forwardTrc, TransferFunction::Srgb, toneMapper,
238-
coeffs);
248+
coeffs, intensityTarget);
239249
}
240250
}
241251

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

+26-13
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "avif/avif_cxx.h"
5454
#include <libyuv.h>
5555
#include "AvifDecoderController.h"
56+
#include "avifweaver.h"
5657

5758
using namespace std;
5859

@@ -74,7 +75,8 @@ enum AvifChromaSubsampling {
7475
AVIF_CHROMA_YUV_420,
7576
AVIF_CHROMA_YUV_422,
7677
AVIF_CHROMA_YUV_444,
77-
AVIF_CHROMA_YUV_400
78+
AVIF_CHROMA_YUV_400,
79+
AVIF_CHROMA_LOSELESS
7880
};
7981

8082
struct heif_error writeHeifData(struct heif_context *ctx,
@@ -319,7 +321,7 @@ jbyteArray encodeBitmapHevc(JNIEnv *env,
319321
vStride,
320322
info.width,
321323
info.height,
322-
profile->full_range_flag ? YuvRange::Full : YuvRange::Tv,
324+
profile->full_range_flag ? YuvRange::Pc : YuvRange::Tv,
323325
matrix);
324326

325327
if (nclxResult) {
@@ -456,10 +458,17 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
456458
}
457459
} else if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_YUV_422) {
458460
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV422;
459-
} else if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_YUV_444) {
461+
} else if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_YUV_444
462+
|| preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_LOSELESS) {
460463
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV444;
461464
} else if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_YUV_400) {
462465
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV400;
466+
} else if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_YUV_420) {
467+
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV420;
468+
} else {
469+
std::string str = "Unknown chroma subsampling";
470+
throwException(env, str);
471+
return static_cast<jbyteArray>(nullptr);
463472
}
464473
avif::ImagePtr image(avifImageCreate(info.width, info.height, 8, pixelFormat));
465474

@@ -595,7 +604,7 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
595604
vStride,
596605
info.width,
597606
info.height,
598-
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
607+
yuvRange == AVIF_RANGE_FULL ? YuvRange::Pc : YuvRange::Tv,
599608
matrix);
600609
} else if (pixelFormat == AVIF_PIXEL_FORMAT_YUV422) {
601610
RgbaToYuv422(imageStore.data(),
@@ -608,9 +617,13 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
608617
vStride,
609618
info.width,
610619
info.height,
611-
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
620+
yuvRange == AVIF_RANGE_FULL ? YuvRange::Pc : YuvRange::Tv,
612621
matrix);
613622
} else if (pixelFormat == AVIF_PIXEL_FORMAT_YUV444) {
623+
if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_LOSELESS) {
624+
matrix = YuvMatrix::Identity;
625+
yuvRange = AVIF_RANGE_FULL;
626+
}
614627
RgbaToYuv444(imageStore.data(),
615628
stride,
616629
yPlane,
@@ -621,7 +634,7 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
621634
vStride,
622635
info.width,
623636
info.height,
624-
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
637+
yuvRange == AVIF_RANGE_FULL ? YuvRange::Pc : YuvRange::Tv,
625638
matrix);
626639
} else if (pixelFormat == AVIF_PIXEL_FORMAT_YUV400) {
627640
RgbaToYuv400(imageStore.data(),
@@ -630,17 +643,17 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
630643
yStride,
631644
info.width,
632645
info.height,
633-
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
646+
yuvRange == AVIF_RANGE_FULL ? YuvRange::Pc : YuvRange::Tv,
634647
matrix);
635648
}
636649

650+
image->matrixCoefficients = matrixCoefficients;
651+
image->colorPrimaries = colorPrimaries;
652+
image->transferCharacteristics = transferCharacteristics;
653+
image->yuvRange = yuvRange;
654+
637655
if (nclxResult) {
638-
if (iccProfile.empty()) {
639-
image->matrixCoefficients = matrixCoefficients;
640-
image->colorPrimaries = colorPrimaries;
641-
image->transferCharacteristics = transferCharacteristics;
642-
image->yuvRange = yuvRange;
643-
} else {
656+
if (!iccProfile.empty()) {
644657
result = avifImageSetProfileICC(image.get(), iccProfile.data(), iccProfile.size());
645658
if (result != AVIF_RESULT_OK) {
646659
std::string str = "Can't set required color profile";

0 commit comments

Comments
 (0)