Skip to content

Commit

Permalink
Added speed parameter for avif encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
awxkee committed Apr 6, 2024
1 parent 7d10bb7 commit c7e368b
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import android.util.Log
import android.util.Size
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.radzivon.bartoshyk.avif.coder.AvifSpeed
import com.radzivon.bartoshyk.avif.coder.HeifCoder
import com.radzivon.bartoshyk.avif.coder.PreciseMode
import com.radzivon.bartoshyk.avif.coder.PreferredColorConfig
Expand Down Expand Up @@ -95,7 +96,7 @@ class MainActivity : AppCompatActivity() {
val coder = HeifCoder()
val buffer = assets.open("screenshot_test.png").source().buffer().readByteArray()
val bitmap = BitmapFactory.decodeByteArray(buffer, 0, buffer.size)
val encoded = coder.encodeAvif(bitmap, quality = 99, preciseMode = PreciseMode.LOSSLESS)
val encoded = coder.encodeAvif(bitmap, quality = 99, preciseMode = PreciseMode.LOSSLESS, speed = AvifSpeed.ONE)
val decoded = coder.decode(encoded)
lifecycleScope.launch(Dispatchers.Main) {
val imageView = BindingImageViewBinding.inflate(layoutInflater, binding.scrollViewContainer, false)
Expand Down
21 changes: 11 additions & 10 deletions avif-coder/src/main/cpp/JniEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,6 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
throwException(env, str);
return static_cast<jbyteArray>(nullptr);
}
if(speed > -1 && speed <= 10){
result = heif_encoder_set_parameter_string(encoder.get(), "speed", speed)
if (result.code != heif_error_Ok) {
std::string choke(result.message);
std::string str = "Can't set speed/effort: " + choke;
throwException(env, str);
return static_cast<jbyteArray>(nullptr);
}
}
}
} else if (qualityMode == AVIF_LOSELESS_MODE) {
result = heif_encoder_set_lossless(encoder.get(), true);
Expand All @@ -141,6 +132,16 @@ jbyteArray encodeBitmap(JNIEnv *env, jobject thiz,
}
}

if (speed > -1 && speed <= 9) {
result = heif_encoder_set_parameter_integer(encoder.get(), "speed", speed);
if (result.code != heif_error_Ok) {
std::string choke(result.message);
std::string str = "Can't set speed/effort: " + choke;
throwException(env, str);
return static_cast<jbyteArray>(nullptr);
}
}

AndroidBitmapInfo info;
if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
throwPixelsException(env);
Expand Down Expand Up @@ -369,7 +370,7 @@ Java_com_radzivon_bartoshyk_avif_coder_HeifCoder_encodeHeicImpl(JNIEnv *env, job
bitmap,
heif_compression_HEVC,
quality,
0,
-1,
dataSpace,
static_cast<AvifQualityMode>(qualityMode));
} catch (std::bad_alloc &err) {
Expand Down
33 changes: 33 additions & 0 deletions avif-coder/src/main/cpp/algo/fast_math-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ using hwy::HWY_NAMESPACE::Sub;
using hwy::HWY_NAMESPACE::Xor;
using hwy::HWY_NAMESPACE::Vec;
using hwy::float32_t;
using namespace hwy;
using namespace hwy;

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

template<class DF, class V, HWY_IF_F16_D(DF)>
HWY_FAST_MATH_INLINE V
Exp2f(const DF df, V x) {
using VF = Vec<decltype(df)>;
using T = hwy::HWY_NAMESPACE::TFromD<DF>;
static const VF expTab[8] =
{
Set(df, hwy::F16FromF32(1.f)),
Set(df, hwy::F16FromF32(0.0416598916054f)),
Set(df, hwy::F16FromF32(0.500000596046f)),
Set(df, hwy::F16FromF32(0.0014122662833f)),
Set(df, hwy::F16FromF32(1.00000011921f)),
Set(df, hwy::F16FromF32(0.00833693705499f)),
Set(df, hwy::F16FromF32(0.166665703058f)),
Set(df, hwy::F16FromF32(0.000195780929062f)),
};
Rebind<int32_t, decltype(df)> s32;
using VS32 = Vec<decltype(s32)>;
Rebind<float, decltype(s32)> fs32;
static const VF CONST_LN2 = Set(df, static_cast<T>(0.6931471805f)); // ln(2)
static const VF CONST_INV_LN2 = Set(df, static_cast<T>(1.4426950408f)); // 1/ln(2)
// Perform range reduction [-log(2),log(2)]
VS32 m = ConvertTo(s32, Mul(x, CONST_INV_LN2));
auto val = NegMulAdd(ConvertTo(fs32, m), CONST_LN2, x);
// Polynomial Approximation
auto poly = TaylorPolyExp(df, val, &expTab[0]);
// Reconstruct
poly = BitCast(fs32, Add(BitCast(s32, poly), ShiftLeft<23>(m)));
return poly;
}

template<class DF, class V>
HWY_FAST_MATH_INLINE V
Exp2f(const DF df, V x) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
package com.radzivon.bartoshyk.avif.coder

/**
* Enum representing speed values from 0 to 10 (slowest-fastest).
* Enum representing speed values from 0 to 9 (slowest-fastest). Where 9 is almost realtime encoding speed with worse compression ration
* Default is 6.
* https://github.com/AOMediaCodec/libavif/blob/main/doc/avifenc.1.md
*/
Expand All @@ -44,8 +44,4 @@ enum class AvifSpeed(internal val value: Int) {
SEVEN(7), // Speed 7
EIGHT(8), // Speed 8
NINE(9), // Speed 9
TEN(10) // Speed 10

// Not using speed parameter, fallback to use x265 instead of aom
USE_X265(-1)
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,18 +116,38 @@ class HeifCoder(
)
}

fun encodeAvif(bitmap: Bitmap, quality: Int = 80, speed: AvifSpeed = AvifSpeed.SIX, preciseMode: PreciseMode = PreciseMode.LOSSY): ByteArray {
/**
* @param quality must be in range 0..100
* @param preciseMode - LOSSY or LOSELESS compression mode
* @param speed - compression speed for detailed documentation see [AvifSpeed]
*/
fun encodeAvif(
bitmap: Bitmap,
quality: Int = 80,
preciseMode: PreciseMode = PreciseMode.LOSSY,
speed: AvifSpeed = AvifSpeed.SIX
): ByteArray {
require(quality in 0..100) {
throw IllegalStateException("Quality should be in 0..100 range")
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
encodeAvifImpl(bitmap, quality, speed.value, bitmap.colorSpace?.dataSpace ?: -1, preciseMode.value)
encodeAvifImpl(
bitmap,
quality,
speed.value,
bitmap.colorSpace?.dataSpace ?: -1,
preciseMode.value
)
} else {
encodeAvifImpl(bitmap, quality, speed.value, -1, preciseMode.value)
}
}

fun encodeHeic(bitmap: Bitmap, quality: Int = 80, preciseMode: PreciseMode = PreciseMode.LOSSY): ByteArray {
fun encodeHeic(
bitmap: Bitmap,
quality: Int = 80,
preciseMode: PreciseMode = PreciseMode.LOSSY
): ByteArray {
require(quality in 0..100) {
throw IllegalStateException("Quality should be in 0..100 range")
}
Expand Down Expand Up @@ -163,8 +183,20 @@ class HeifCoder(
toneMapper: Int,
): Bitmap

private external fun encodeAvifImpl(bitmap: Bitmap, quality: Int, speed: Int, dataSpace: Int, qualityMode: Int): ByteArray
private external fun encodeHeicImpl(bitmap: Bitmap, quality: Int, speed: Int, dataSpace: Int, qualityMode: Int): ByteArray
private external fun encodeAvifImpl(
bitmap: Bitmap,
quality: Int,
speed: Int,
dataSpace: Int,
qualityMode: Int
): ByteArray

private external fun encodeHeicImpl(
bitmap: Bitmap,
quality: Int,
dataSpace: Int,
qualityMode: Int
): ByteArray

@SuppressLint("ObsoleteSdkInt")
companion object {
Expand Down

0 comments on commit c7e368b

Please sign in to comment.