Skip to content

Commit 1236a75

Browse files
committed
Big rework to support SVT, libavif, kvazaar
1 parent a1be950 commit 1236a75

File tree

21 files changed

+2242
-108
lines changed

21 files changed

+2242
-108
lines changed

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

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import androidx.appcompat.app.AppCompatActivity
3737
import androidx.core.graphics.scale
3838
import androidx.lifecycle.lifecycleScope
3939
import com.radzivon.bartoshyk.avif.coder.AvifAnimatedDecoder
40+
import com.radzivon.bartoshyk.avif.coder.AvifChromaSubsampling
4041
import com.radzivon.bartoshyk.avif.coder.HeifCoder
4142
import com.radzivon.bartoshyk.avif.coder.PreciseMode
4243
import com.radzivon.bartoshyk.avif.coder.PreferredColorConfig
@@ -120,7 +121,7 @@ class MainActivity : AppCompatActivity() {
120121
var allFiles = mutableListOf<String>()
121122
allFiles.addAll(allFiles2)
122123
allFiles.addAll(allFiles1)
123-
allFiles = allFiles.filter { it.contains("test_1.avif") }.toMutableList()
124+
allFiles = allFiles.filter { it.contains("bt_2020_pq.avif") }.toMutableList()
124125
// allFiles = allFiles.filter { it.contains("bbb_alpha_inverted.avif") }.toMutableList()
125126
for (file in allFiles) {
126127
try {
@@ -138,12 +139,12 @@ class MainActivity : AppCompatActivity() {
138139
// ScaleMode.RESIZE
139140
// )
140141

141-
val start = System.currentTimeMillis()
142+
var start = System.currentTimeMillis()
142143

143144
var bitmap0 = coder.decodeSampled(
144145
byteArray = buffer,
145-
scaledWidth = size.width / 5,
146-
scaledHeight = size.height / 5,
146+
scaledWidth = size.width,
147+
scaledHeight = size.height,
147148
preferredColorConfig = PreferredColorConfig.RGBA_8888,
148149
scaleMode = ScaleMode.FILL,
149150
scaleQuality = ScalingQuality.HIGH,
@@ -153,19 +154,14 @@ class MainActivity : AppCompatActivity() {
153154

154155
Log.i("AVIF", "Decoding time ${System.currentTimeMillis() - start}")
155156

156-
val bmp1 = bitmap0.scale(
157-
if (bitmap0.width % 2 != 0) {
158-
bitmap0.width + 1
159-
} else {
160-
bitmap0.width
161-
}, if (bitmap0.height % 2 != 0) {
162-
bitmap0.height + 1
163-
} else {
164-
bitmap0.height
165-
}
166-
)
157+
start = System.currentTimeMillis()
158+
159+
Log.i("AVIFFFF", "Starts encoding")
160+
161+
val encode = coder.encodeAvif(bitmap = bitmap0, quality = 55, avifChromaSubsampling = AvifChromaSubsampling.YUV420)
162+
163+
Log.i("AVIFFFF", "Encoding time ${System.currentTimeMillis() - start}, encoded size ${encode.size}")
167164

168-
val encode = coder.encodeAvif(bitmap = bmp1, quality = 75)
169165
val bitmap = coder.decode(encode)
170166

171167
lifecycleScope.launch(Dispatchers.Main) {

avif-coder/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ android {
7272
"-std=c++20",
7373
)
7474
)
75+
// abiFilters += setOf("arm64-v8a")
7576
abiFilters += setOf( "arm64-v8a", "armeabi-v7a", "x86_64", "x86")
7677
}
7778
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ add_library(libyuv STATIC IMPORTED)
2727
add_library(libde265 SHARED IMPORTED)
2828
add_library(libdav1d SHARED IMPORTED)
2929
add_library(libsharpyuv STATIC IMPORTED)
30-
add_library(svt SHARED IMPORTED)
30+
add_library(aom SHARED IMPORTED)
3131
add_library(avifweaver SHARED IMPORTED)
3232

3333
set_target_properties(libkvazaar PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/${ANDROID_ABI}/libkvazaar.so)
@@ -36,7 +36,7 @@ set_target_properties(libyuv PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/li
3636
set_target_properties(libde265 PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/${ANDROID_ABI}/libde265.so)
3737
set_target_properties(libdav1d PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/${ANDROID_ABI}/libdav1d.so)
3838
set_target_properties(libsharpyuv PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/${ANDROID_ABI}/libsharpyuv.a)
39-
set_target_properties(svt PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/${ANDROID_ABI}/libSvtAv1Enc.so)
39+
set_target_properties(aom PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/${ANDROID_ABI}/libaom.so)
4040
set_target_properties(avifweaver PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lib/${ANDROID_ABI}/libavifweaver.so)
4141

4242
add_library(cpufeatures STATIC ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
@@ -83,5 +83,5 @@ add_subdirectory(avif)
8383

8484
target_link_libraries( # Specifies the target library.
8585
coder PRIVATE
86-
${log-lib} libheif cpufeatures libyuv -ljnigraphics libkvazaar svt avif_shared
86+
${log-lib} libheif cpufeatures libyuv -ljnigraphics libkvazaar aom avif_shared
8787
libde265 libdav1d libsharpyuv ${android-lib} avifweaver)

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

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ enum AvifEncodingSurface {
6969
RGB, RGBA, AUTO
7070
};
7171

72+
enum AvifChromaSubsampling {
73+
AVIF_CHROMA_AUTO, AVIF_CHROMA_YUV_420, AVIF_CHROMA_YUV_422, AVIF_CHROMA_YUV_444, AVIF_CHROMA_YUV_400
74+
};
75+
7276
struct heif_error writeHeifData(struct heif_context *ctx,
7377
const void *data,
7478
size_t size,
@@ -369,7 +373,9 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
369373
const int quality,
370374
const int dataSpace,
371375
const AvifQualityMode qualityMode,
372-
const AvifEncodingSurface surface) {
376+
const AvifEncodingSurface surface,
377+
const int speed,
378+
const AvifChromaSubsampling preferredChromaSubsampling) {
373379
avif::EncoderPtr encoder(avifEncoderCreate());
374380
if (encoder == nullptr) {
375381
std::string str = "Can't create encoder";
@@ -385,6 +391,8 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
385391
encoder->qualityAlpha = 100;
386392
}
387393

394+
encoder->speed = std::clamp(speed, AVIF_SPEED_SLOWEST, AVIF_SPEED_FASTEST);
395+
388396
AndroidBitmapInfo info;
389397
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
390398
throwPixelsException(env);
@@ -417,6 +425,19 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
417425
AndroidBitmap_unlockPixels(env, bitmap);
418426

419427
avifPixelFormat pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV420;
428+
if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_AUTO) {
429+
if (qualityMode == AVIF_LOSELESS_MODE || quality > 93) {
430+
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV444;
431+
} else if (quality > 65) {
432+
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV422;
433+
}
434+
} else if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_YUV_422) {
435+
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV422;
436+
} else if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_YUV_444) {
437+
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV444;
438+
} else if (preferredChromaSubsampling == AvifChromaSubsampling::AVIF_CHROMA_YUV_400) {
439+
pixelFormat = avifPixelFormat::AVIF_PIXEL_FORMAT_YUV400;
440+
}
420441
avif::ImagePtr image(avifImageCreate(info.width, info.height, 8, pixelFormat));
421442

422443
if (image.get() == nullptr) {
@@ -499,20 +520,28 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
499520
return static_cast<jbyteArray>(nullptr);
500521
}
501522

502-
uint32_t uStride = image->yuvRowBytes[1];
503-
uint8_t *uPlane = image->yuvPlanes[1];
504-
if (uPlane == nullptr) {
505-
std::string str = "Can't add U plane to an image";
506-
throwException(env, str);
507-
return static_cast<jbyteArray>(nullptr);
508-
}
523+
uint32_t uStride = 0;
524+
uint8_t *uPlane = nullptr;
509525

510-
uint32_t vStride = image->yuvRowBytes[2];
511-
uint8_t *vPlane = image->yuvPlanes[2];
512-
if (vPlane == nullptr) {
513-
std::string str = "Can't add V plane to an image";
514-
throwException(env, str);
515-
return static_cast<jbyteArray>(nullptr);
526+
uint32_t vStride = 0;
527+
uint8_t * vPlane = nullptr;
528+
529+
if (pixelFormat != AVIF_PIXEL_FORMAT_YUV400) {
530+
uStride = image->yuvRowBytes[1];
531+
uPlane = image->yuvPlanes[1];
532+
if (uPlane == nullptr) {
533+
std::string str = "Can't add U plane to an image";
534+
throwException(env, str);
535+
return static_cast<jbyteArray>(nullptr);
536+
}
537+
538+
vStride = image->yuvRowBytes[2];
539+
vPlane = image->yuvPlanes[2];
540+
if (vPlane == nullptr) {
541+
std::string str = "Can't add V plane to an image";
542+
throwException(env, str);
543+
return static_cast<jbyteArray>(nullptr);
544+
}
516545
}
517546

518547
std::vector<uint8_t> iccProfile(0);
@@ -532,18 +561,55 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
532561
iccProfile,
533562
matrix);
534563

535-
RgbaToYuv420(imageStore.data(),
536-
stride,
537-
yPlane,
538-
yStride,
539-
uPlane,
540-
uStride,
541-
vPlane,
542-
vStride,
543-
info.width,
544-
info.height,
545-
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
546-
matrix);
564+
if (pixelFormat == AVIF_PIXEL_FORMAT_YUV420) {
565+
RgbaToYuv420(imageStore.data(),
566+
stride,
567+
yPlane,
568+
yStride,
569+
uPlane,
570+
uStride,
571+
vPlane,
572+
vStride,
573+
info.width,
574+
info.height,
575+
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
576+
matrix);
577+
} else if (pixelFormat == AVIF_PIXEL_FORMAT_YUV422) {
578+
RgbaToYuv422(imageStore.data(),
579+
stride,
580+
yPlane,
581+
yStride,
582+
uPlane,
583+
uStride,
584+
vPlane,
585+
vStride,
586+
info.width,
587+
info.height,
588+
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
589+
matrix);
590+
} else if (pixelFormat == AVIF_PIXEL_FORMAT_YUV444) {
591+
RgbaToYuv444(imageStore.data(),
592+
stride,
593+
yPlane,
594+
yStride,
595+
uPlane,
596+
uStride,
597+
vPlane,
598+
vStride,
599+
info.width,
600+
info.height,
601+
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
602+
matrix);
603+
} else if (pixelFormat == AVIF_PIXEL_FORMAT_YUV400) {
604+
RgbaToYuv400(imageStore.data(),
605+
stride,
606+
yPlane,
607+
yStride,
608+
info.width,
609+
info.height,
610+
yuvRange == AVIF_RANGE_FULL ? YuvRange::Full : YuvRange::Tv,
611+
matrix);
612+
}
547613

548614
if (nclxResult) {
549615
if (iccProfile.empty()) {
@@ -570,7 +636,7 @@ jbyteArray encodeBitmapAvif(JNIEnv *env,
570636
return static_cast<jbyteArray>(nullptr);
571637
}
572638

573-
avifRWData data;
639+
avifRWData data = AVIF_DATA_EMPTY;
574640
result = avifEncoderFinish(encoder.get(), &data);
575641
if (result != AVIF_RESULT_OK) {
576642
[[maybe_unused]] auto erelease = encoder.release();
@@ -598,21 +664,34 @@ Java_com_radzivon_bartoshyk_avif_coder_HeifCoder_encodeAvifImpl(JNIEnv *env,
598664
jint quality,
599665
jint dataSpace,
600666
jint qualityMode,
601-
jint surfaceMode) {
667+
jint surfaceMode,
668+
jint speed,
669+
jint chromaSubsampling) {
602670
try {
603671
AvifEncodingSurface surface = AvifEncodingSurface::AUTO;
604672
if (surfaceMode == 1) {
605673
surface = AvifEncodingSurface::RGB;
606674
} else if (surfaceMode == 2) {
607675
surface = AvifEncodingSurface::RGBA;
608676
}
677+
AvifChromaSubsampling mChromaSubsampling = AvifChromaSubsampling::AVIF_CHROMA_AUTO;
678+
if (chromaSubsampling == 1) {
679+
mChromaSubsampling = AvifChromaSubsampling::AVIF_CHROMA_YUV_420;
680+
} else if (chromaSubsampling == 2) {
681+
mChromaSubsampling = AvifChromaSubsampling::AVIF_CHROMA_YUV_422;
682+
} else if (chromaSubsampling == 3) {
683+
mChromaSubsampling = AvifChromaSubsampling::AVIF_CHROMA_YUV_444;
684+
} else if (chromaSubsampling == 4) {
685+
mChromaSubsampling = AvifChromaSubsampling::AVIF_CHROMA_YUV_400;
686+
}
609687
return encodeBitmapAvif(env,
610688
thiz,
611689
bitmap,
612690
quality,
613691
dataSpace,
614692
static_cast<AvifQualityMode>(qualityMode),
615-
surface);
693+
surface, speed,
694+
mChromaSubsampling);
616695
} catch (std::bad_alloc &err) {
617696
std::string exception = "Not enough memory to encode this image";
618697
throwException(env, exception);

0 commit comments

Comments
 (0)