Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
808b0b2
Add tests
Rodrigodd Nov 7, 2024
d95d285
Add benchmark module
Rodrigodd Dec 1, 2024
53221ea
Update to Gradle 8.9 and NDK 26.3
Rodrigodd Dec 7, 2024
d591161
Refactor CMake project to use ExternalProject
Rodrigodd Dec 7, 2024
51810e3
ci: Inspect and archive library file
Rodrigodd Oct 27, 2024
0650d26
Install more recent version of meson
Rodrigodd Nov 10, 2024
6c0fa4a
Refactor CMake project to use ExternalProject
Rodrigodd Oct 26, 2024
6b03a3d
Add documentation to decoder_base
Rodrigodd Nov 16, 2024
07ec50c
Add decoder libvips
Rodrigodd Nov 8, 2024
e4bccd3
Remove all decoders minus libvips
Rodrigodd Nov 30, 2024
98871cd
Resize full image and then decode region
Rodrigodd Nov 30, 2024
ac21786
Write to Bitmap pixels directer
Rodrigodd Nov 30, 2024
df5e52e
Add documentation and minor refactors
Rodrigodd Nov 30, 2024
43f07fd
Remove loops from tests
Rodrigodd Nov 30, 2024
63165cc
Rename package to `dev.mihon.image.decoder`
Rodrigodd Nov 30, 2024
c196d56
Remove ImageInfo struct
Rodrigodd Dec 1, 2024
b40a998
Rename `java/` to `kotlin/`
Rodrigodd Dec 1, 2024
95e6a19
Add .cache to gitignore
Rodrigodd Dec 1, 2024
218f3f3
lmcs2 for rgb, cmyk, no profile
wwww-wwww Dec 1, 2024
94ad0de
Fix test path
Rodrigodd Dec 1, 2024
121b012
fix: Initialize VIPS_INIT only once
Rodrigodd Dec 5, 2024
863efc9
ci: Run tests on emulator
Rodrigodd Dec 7, 2024
f7dea5a
ci: Remove build step before running emulator
Rodrigodd Dec 8, 2024
3b374c9
ci: Free disk space
Rodrigodd Dec 8, 2024
be01ad1
ci: remove autoremove
Rodrigodd Dec 8, 2024
0e34112
ci: Setup Android SDK
Rodrigodd Dec 8, 2024
a71fc01
ci: Run :benchmark in dryRunMode
Rodrigodd Dec 8, 2024
e78dbd8
Try to cache CMake output
Rodrigodd Dec 8, 2024
fa68e4b
Fix typo in ./gradlew -P arg
Rodrigodd Dec 8, 2024
cb69e0a
ci: Give up on testing bench on CI
Rodrigodd Dec 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 79 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ jobs:
- name: Clone repo
uses: actions/checkout@v4

# AVD needs a lot of disk space, so we need to free up some space
# See: https://github.com/ReactiveCircus/android-emulator-runner/issues/390#issuecomment-2323948660
- name: Free disk space
run: |
sudo apt purge -yq $(dpkg -l | grep '^ii' | awk '{ print $2 }' | grep -P '(aspnetcore|cabal-|dotnet-|ghc-|libmono|mongodb-|mysql-|php)') \
firefox google-chrome-stable google-cloud-cli microsoft-edge-stable mono-devel mono-runtime-common monodoc-manual powershell ruby
sudo apt autoremove -yq
sudo apt clean
sudo rm -fr /opt/ghc /opt/hostedtoolcache /usr/lib/node_modules /usr/local/share/boost /usr/share/dotnet /usr/share/swift

- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v2

Expand All @@ -28,22 +38,85 @@ jobs:
java-version: 17
distribution: adopt

- name: Setup Android SDK
uses: android-actions/setup-android@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install native build dependencies
run: |
sudo apt update
sudo apt install \
build-essential \
meson \
ninja-build \
nasm
python -m pip install --upgrade pip
python -m pip install meson

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v3

- name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.14
- name: CMake Cache
uses: actions/cache@v4
id: cmake-cache
with:
path: |
library/.cxx/*
key: cmake-cache

- name: Build Library
run: ./gradlew :library:assemble

- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

- name: AVD cache
uses: actions/cache@v4
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-29

- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
cmake-version: '3.22.1'
api-level: 29
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
script: echo "Generated AVD snapshot for caching."

- name: Android Emulator Runner
uses: ReactiveCircus/android-emulator-runner@v2.33.0
with:
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
api-level: 29
ndk: 26.3.11579264
cmake: 3.31.1
script: |
./gradlew :library:connectedAndroidTest


# Archive the generated AAR file
- name: Archive AAR
uses: actions/upload-artifact@v4
with:
path: library/build/outputs/aar/*.aar

- name: Check Library Stats
run: |
ls -lh library/build/intermediates/merged_native_libs/debug/*/out/lib/*/*
readelf -d library/build/intermediates/merged_native_libs/debug/*/out/lib/*/*



- name: Build app
run: ./gradlew assemble
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
.externalNativeBuild
.cxx
local.properties
.cache
1 change: 1 addition & 0 deletions benchmark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
37 changes: 37 additions & 0 deletions benchmark/benchmark-proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

-dontobfuscate

-ignorewarnings

-keepattributes *Annotation*

-dontnote junit.framework.**
-dontnote junit.runner.**

-dontwarn androidx.test.**
-dontwarn org.junit.**
-dontwarn org.hamcrest.**
-dontwarn com.squareup.javawriter.JavaWriter

-keepclasseswithmembers @org.junit.runner.RunWith public class *
53 changes: 53 additions & 0 deletions benchmark/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
plugins {
id 'com.android.library'
id 'androidx.benchmark'
id 'org.jetbrains.kotlin.android'
}

android {
namespace 'dev.mihon.image.decoder.benchmark'
compileSdk 34

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = "1.8"
}

defaultConfig {
minSdk 23
targetSdk 34

testInstrumentationRunner 'androidx.benchmark.junit4.AndroidBenchmarkRunner'
}

testBuildType = "release"
buildTypes {
debug {
// Since debuggable can"t be modified by gradle for library modules,
// it must be done in a manifest - see src/androidTest/AndroidManifest.xml
minifyEnabled true
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "benchmark-proguard-rules.pro"
}
release {
isDefault = true
}
}
}

dependencies {
implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
androidTestImplementation 'androidx.test:runner:1.6.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.2.4'
androidTestImplementation(project(":library"))
// Add your dependencies here. Note that you cannot benchmark code
// in an app module this way - you will need to move any code you
// want to benchmark to a library module:
// https://developer.android.com/studio/projects/android-library#Convert

}
16 changes: 16 additions & 0 deletions benchmark/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<!--
Important: disable debugging for accurate performance results

In a com.android.library project, this flag must be disabled from this
manifest, as it is not possible to override this flag from Gradle.
-->
<application
android:debuggable="false"
android:requestLegacyExternalStorage="true"
tools:ignore="HardcodedDebugMode"
tools:replace="android:debuggable" />
</manifest>
Binary file added benchmark/src/androidTest/assets/image.avif
Binary file not shown.
Binary file added benchmark/src/androidTest/assets/image.heif
Binary file not shown.
Binary file added benchmark/src/androidTest/assets/image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added benchmark/src/androidTest/assets/image.jxl
Binary file not shown.
Binary file added benchmark/src/androidTest/assets/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added benchmark/src/androidTest/assets/image.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package dev.mihon.image.decoder.benchmark

import android.content.Context
import android.graphics.Rect
import dev.mihon.image.decoder.ImageDecoder
import android.util.Log
import androidx.benchmark.junit4.BenchmarkRule
import androidx.benchmark.junit4.measureRepeated
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.random.Random

/**
* Benchmark, which will execute on an Android device.
*
* The body of [BenchmarkRule.measureRepeated] is measured in a loop, and Studio will
* output the result. Modify your code to see how it affects performance.
*/
@RunWith(AndroidJUnit4::class)
class ExampleBenchmark {

@get:Rule
val benchmarkRule = BenchmarkRule()

private lateinit var context: Context

@Before
fun setUp() {
// Initialize context for asset loading
context = ApplicationProvider.getApplicationContext()
}

@Test
fun decodePng() {
benchmarkDecoder("image.png")
}

@Test
fun decodeJpg() {
benchmarkDecoder("image.jpg")
}

@Test
fun decodeHeif() {
benchmarkDecoder("image.heif")
}

@Test
fun decodeAvif() {
benchmarkDecoder("image.avif")
}

@Test
fun decodeJxl() {
benchmarkDecoder("image.jxl")
}

@Test
fun decodeWebp() {
benchmarkDecoder("image.webp")
}

private fun benchmarkDecoder(imageName: String) {

Log.i("Benchmark", "benchmarking $imageName")

// fix seed to make tests deterministic
val rng = Random(99)
val decoder = getDecoder(imageName)
benchmarkRule.measureRepeated {
randomDecode(decoder, rng)
}
}

private fun getDecoder(imageName: String): ImageDecoder {
return context.assets.open(imageName).use { stream ->
return@use ImageDecoder.newInstance(stream)!!
}
}

private fun randomDecode(decoder: ImageDecoder, rng: Random) {
var randomWidth = (0 until decoder.width).random(rng)
var randomHeight = (0 until decoder.height).random(rng)

// increase the sample size until the image have a reasonable size
var sampleSize = 1
while (randomWidth > 1000 || randomHeight > 1000) {
randomWidth /= 2
randomHeight /= 2
sampleSize *= 2
}

if (randomWidth == 0) randomWidth = 1
if (randomHeight == 0) randomHeight = 1

val randomX = (0 until decoder.width - randomWidth).random(rng)
val randomY = (0 until decoder.height - randomHeight).random(rng)

val region = Rect(randomX, randomY, randomX + randomWidth, randomY + randomHeight)

decoder.decode(region, sampleSize)!!
}
}
2 changes: 2 additions & 0 deletions benchmark/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ buildscript {
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.3.1"
classpath "com.android.tools.build:gradle:8.7.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.21"
classpath 'androidx.benchmark:benchmark-gradle-plugin:1.2.4'
}
}

Expand Down
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#Sun Oct 20 20:31:18 BRT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading