Skip to content

Commit c7e368b

Browse files
committed
Added speed parameter for avif encoding
1 parent 7d10bb7 commit c7e368b

File tree

5 files changed

+84
-21
lines changed

5 files changed

+84
-21
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import android.util.Log
3333
import android.util.Size
3434
import androidx.appcompat.app.AppCompatActivity
3535
import androidx.lifecycle.lifecycleScope
36+
import com.radzivon.bartoshyk.avif.coder.AvifSpeed
3637
import com.radzivon.bartoshyk.avif.coder.HeifCoder
3738
import com.radzivon.bartoshyk.avif.coder.PreciseMode
3839
import com.radzivon.bartoshyk.avif.coder.PreferredColorConfig
@@ -95,7 +96,7 @@ class MainActivity : AppCompatActivity() {
9596
val coder = HeifCoder()
9697
val buffer = assets.open("screenshot_test.png").source().buffer().readByteArray()
9798
val bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.size)
98-
val encoded = coder.encodeAvif(bitmap, quality = 99, preciseMode = PreciseMode.LOSSLESS)
99+
val encoded = coder.encodeAvif(bitmap, quality = 99, preciseMode = PreciseMode.LOSSLESS, speed = AvifSpeed.ONE)
99100
val decoded = coder.decode(encoded)
100101
lifecycleScope.launch(Dispatchers.Main) {
101102
val imageView = BindingImageViewBinding.inflate(layoutInflater, binding.scrollViewContainer, false)

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,6 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
114114
throwException(env, str);
115115
return static_cast<jbyteArray>(nullptr);
116116
}
117-
if(speed > -1 && speed <= 10){
118-
result = heif_encoder_set_parameter_string(encoder.get(), "speed", speed)
119-
if (result.code != heif_error_Ok) {
120-
std::string choke(result.message);
121-
std::string str = "Can't set speed/effort: " + choke;
122-
throwException(env, str);
123-
return static_cast<jbyteArray>(nullptr);
124-
}
125-
}
126117
}
127118
} else if (qualityMode == AVIF_LOSELESS_MODE) {
128119
result = heif_encoder_set_lossless(encoder.get(), true);
@@ -141,6 +132,16 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
141132
}
142133
}
143134

135+
if (speed > -1 && speed <= 9) {
136+
result = heif_encoder_set_parameter_integer(encoder.get(), "speed", speed);
137+
if (result.code != heif_error_Ok) {
138+
std::string choke(result.message);
139+
std::string str = "Can't set speed/effort: " + choke;
140+
throwException(env, str);
141+
return static_cast<jbyteArray>(nullptr);
142+
}
143+
}
144+
144145
AndroidBitmapInfo info;
145146
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
146147
throwPixelsException(env);
@@ -369,7 +370,7 @@ Java_com_radzivon_bartoshyk_avif_coder_HeifCoder_encodeHeicImpl(JNIEnv *env, job
369370
bitmap,
370371
heif_compression_HEVC,
371372
quality,
372-
0,
373+
-1,
373374
dataSpace,
374375
static_cast<AvifQualityMode>(qualityMode));
375376
} catch (std::bad_alloc &err) {

avif-coder/src/main/cpp/algo/fast_math-inl.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ using hwy::HWY_NAMESPACE::Sub;
4444
using hwy::HWY_NAMESPACE::Xor;
4545
using hwy::HWY_NAMESPACE::Vec;
4646
using hwy::float32_t;
47+
using namespace hwy;
48+
using namespace hwy;
4749

4850
template<class DF, class V>
4951
HWY_FAST_MATH_INLINE V TaylorPolyExp(const DF df, V x, const V *coeffs) {
@@ -57,6 +59,37 @@ HWY_FAST_MATH_INLINE V TaylorPolyExp(const DF df, V x, const V *coeffs) {
5759
return res;
5860
}
5961

62+
template<class DF, class V, HWY_IF_F16_D(DF)>
63+
HWY_FAST_MATH_INLINE V
64+
Exp2f(const DF df, V x) {
65+
using VF = Vec<decltype(df)>;
66+
using T = hwy::HWY_NAMESPACE::TFromD<DF>;
67+
static const VF expTab[8] =
68+
{
69+
Set(df, hwy::F16FromF32(1.f)),
70+
Set(df, hwy::F16FromF32(0.0416598916054f)),
71+
Set(df, hwy::F16FromF32(0.500000596046f)),
72+
Set(df, hwy::F16FromF32(0.0014122662833f)),
73+
Set(df, hwy::F16FromF32(1.00000011921f)),
74+
Set(df, hwy::F16FromF32(0.00833693705499f)),
75+
Set(df, hwy::F16FromF32(0.166665703058f)),
76+
Set(df, hwy::F16FromF32(0.000195780929062f)),
77+
};
78+
Rebind<int32_t, decltype(df)> s32;
79+
using VS32 = Vec<decltype(s32)>;
80+
Rebind<float, decltype(s32)> fs32;
81+
static const VF CONST_LN2 = Set(df, static_cast<T>(0.6931471805f)); // ln(2)
82+
static const VF CONST_INV_LN2 = Set(df, static_cast<T>(1.4426950408f)); // 1/ln(2)
83+
// Perform range reduction [-log(2),log(2)]
84+
VS32 m = ConvertTo(s32, Mul(x, CONST_INV_LN2));
85+
auto val = NegMulAdd(ConvertTo(fs32, m), CONST_LN2, x);
86+
// Polynomial Approximation
87+
auto poly = TaylorPolyExp(df, val, &expTab[0]);
88+
// Reconstruct
89+
poly = BitCast(fs32, Add(BitCast(s32, poly), ShiftLeft<23>(m)));
90+
return poly;
91+
}
92+
6093
template<class DF, class V>
6194
HWY_FAST_MATH_INLINE V
6295
Exp2f(const DF df, V x) {

avif-coder/src/main/java/com/radzivon/bartoshyk/avif/coder/AvifSpeed.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
package com.radzivon.bartoshyk.avif.coder
3030

3131
/**
32-
* Enum representing speed values from 0 to 10 (slowest-fastest).
32+
* Enum representing speed values from 0 to 9 (slowest-fastest). Where 9 is almost realtime encoding speed with worse compression ration
3333
* Default is 6.
3434
* https://github.com/AOMediaCodec/libavif/blob/main/doc/avifenc.1.md
3535
*/
@@ -44,8 +44,4 @@ enum class AvifSpeed(internal val value: Int) {
4444
SEVEN(7), // Speed 7
4545
EIGHT(8), // Speed 8
4646
NINE(9), // Speed 9
47-
TEN(10) // Speed 10
48-
49-
// Not using speed parameter, fallback to use x265 instead of aom
50-
USE_X265(-1)
5147
}

avif-coder/src/main/java/com/radzivon/bartoshyk/avif/coder/HeifCoder.kt

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,18 +116,38 @@ class HeifCoder(
116116
)
117117
}
118118

119-
fun encodeAvif(bitmap: Bitmap, quality: Int = 80, speed: AvifSpeed = AvifSpeed.SIX, preciseMode: PreciseMode = PreciseMode.LOSSY): ByteArray {
119+
/**
120+
* @param quality must be in range 0..100
121+
* @param preciseMode - LOSSY or LOSELESS compression mode
122+
* @param speed - compression speed for detailed documentation see [AvifSpeed]
123+
*/
124+
fun encodeAvif(
125+
bitmap: Bitmap,
126+
quality: Int = 80,
127+
preciseMode: PreciseMode = PreciseMode.LOSSY,
128+
speed: AvifSpeed = AvifSpeed.SIX
129+
): ByteArray {
120130
require(quality in 0..100) {
121131
throw IllegalStateException("Quality should be in 0..100 range")
122132
}
123133
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
124-
encodeAvifImpl(bitmap, quality, speed.value, bitmap.colorSpace?.dataSpace ?: -1, preciseMode.value)
134+
encodeAvifImpl(
135+
bitmap,
136+
quality,
137+
speed.value,
138+
bitmap.colorSpace?.dataSpace ?: -1,
139+
preciseMode.value
140+
)
125141
} else {
126142
encodeAvifImpl(bitmap, quality, speed.value, -1, preciseMode.value)
127143
}
128144
}
129145

130-
fun encodeHeic(bitmap: Bitmap, quality: Int = 80, preciseMode: PreciseMode = PreciseMode.LOSSY): ByteArray {
146+
fun encodeHeic(
147+
bitmap: Bitmap,
148+
quality: Int = 80,
149+
preciseMode: PreciseMode = PreciseMode.LOSSY
150+
): ByteArray {
131151
require(quality in 0..100) {
132152
throw IllegalStateException("Quality should be in 0..100 range")
133153
}
@@ -163,8 +183,20 @@ class HeifCoder(
163183
toneMapper: Int,
164184
): Bitmap
165185

166-
private external fun encodeAvifImpl(bitmap: Bitmap, quality: Int, speed: Int, dataSpace: Int, qualityMode: Int): ByteArray
167-
private external fun encodeHeicImpl(bitmap: Bitmap, quality: Int, speed: Int, dataSpace: Int, qualityMode: Int): ByteArray
186+
private external fun encodeAvifImpl(
187+
bitmap: Bitmap,
188+
quality: Int,
189+
speed: Int,
190+
dataSpace: Int,
191+
qualityMode: Int
192+
): ByteArray
193+
194+
private external fun encodeHeicImpl(
195+
bitmap: Bitmap,
196+
quality: Int,
197+
dataSpace: Int,
198+
qualityMode: Int
199+
): ByteArray
168200

169201
@SuppressLint("ObsoleteSdkInt")
170202
companion object {

0 commit comments

Comments
 (0)