From 7856fafb3a8b3bff997174eb83bfbd92893a6653 Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:30:59 -0800 Subject: [PATCH 01/10] Upgrade gradle, dependencies and android SDK --- Kotlin/build.gradle | 4 ++-- Kotlin/demo/build.gradle | 19 +++++++++++++------ Kotlin/gradle.properties | 11 ++++++++--- .../gradle/wrapper/gradle-wrapper.properties | 4 ++-- Kotlin/lib/build.gradle | 11 ++++++----- 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Kotlin/build.gradle b/Kotlin/build.gradle index b971d828..6e2b8b1f 100644 --- a/Kotlin/build.gradle +++ b/Kotlin/build.gradle @@ -1,7 +1,7 @@ buildscript { - ext.kotlin_version = '1.3.72' + ext.kotlin_version = '1.5.31' repositories { google() @@ -9,7 +9,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:7.0.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } diff --git a/Kotlin/demo/build.gradle b/Kotlin/demo/build.gradle index 6ccb0378..4db68381 100644 --- a/Kotlin/demo/build.gradle +++ b/Kotlin/demo/build.gradle @@ -1,19 +1,26 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-kapt' +} android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { applicationId "com.wolt.blurhash" minSdkVersion 14 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 1 versionName "1.0" } + buildFeatures { + dataBinding true + } + + buildTypes { release { minifyEnabled false @@ -25,5 +32,5 @@ android { dependencies { implementation project(path: ':lib') - implementation 'androidx.appcompat:appcompat:1.1.0' + implementation "androidx.appcompat:appcompat:1.3.1" } diff --git a/Kotlin/gradle.properties b/Kotlin/gradle.properties index 8de50581..dc6984dd 100644 --- a/Kotlin/gradle.properties +++ b/Kotlin/gradle.properties @@ -6,10 +6,15 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -android.enableJetifier=true -android.useAndroidX=true -org.gradle.jvmargs=-Xmx1536m +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +android.enableJetifier=true \ No newline at end of file diff --git a/Kotlin/gradle/wrapper/gradle-wrapper.properties b/Kotlin/gradle/wrapper/gradle-wrapper.properties index 032d0433..db4c5393 100644 --- a/Kotlin/gradle/wrapper/gradle-wrapper.properties +++ b/Kotlin/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Jul 01 10:02:38 EEST 2019 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/Kotlin/lib/build.gradle b/Kotlin/lib/build.gradle index 35dd930e..54db51a6 100644 --- a/Kotlin/lib/build.gradle +++ b/Kotlin/lib/build.gradle @@ -1,14 +1,13 @@ apply plugin: 'com.android.library' -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 14 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 1 versionName "1.0" @@ -27,6 +26,8 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - androidTestImplementation 'junit:junit:4.13' - androidTestImplementation 'androidx.test:runner:1.2.0' + + testImplementation "junit:junit:4.13.2" + androidTestImplementation "androidx.test.ext:junit:1.1.3" + androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0" } From 7691cb3cf5c7ff432ef181f1a26bad1a658b8d3a Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:31:33 -0800 Subject: [PATCH 02/10] Add tag for use databinding --- .../src/main/res/layout/activity_main.xml | 98 ++++++++++--------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/Kotlin/demo/src/main/res/layout/activity_main.xml b/Kotlin/demo/src/main/res/layout/activity_main.xml index 3b22c57d..100d12fa 100644 --- a/Kotlin/demo/src/main/res/layout/activity_main.xml +++ b/Kotlin/demo/src/main/res/layout/activity_main.xml @@ -1,53 +1,57 @@ - + + + + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="24dp"> - + - + - - + + + + + \ No newline at end of file From 8a4c27097820b2b6ff7d7f94357f6ee772d90c1d Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:32:55 -0800 Subject: [PATCH 03/10] Add android:exported="true" Flag indicating whether the given application component is available to other applications. If false, it can only be accessed by applications with its same user id (which usually means only by code in its own package). If true, it can be invoked by external entities. --- Kotlin/demo/src/main/AndroidManifest.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Kotlin/demo/src/main/AndroidManifest.xml b/Kotlin/demo/src/main/AndroidManifest.xml index 3be7349e..759897a6 100644 --- a/Kotlin/demo/src/main/AndroidManifest.xml +++ b/Kotlin/demo/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="com.wolt.blurhashapp"> - + - + - + From 34280bac04e1b36c2462fc27f9f1024573d2e1db Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:33:18 -0800 Subject: [PATCH 04/10] Implementing databinding --- .../java/com/wolt/blurhashapp/MainActivity.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Kotlin/demo/src/main/java/com/wolt/blurhashapp/MainActivity.kt b/Kotlin/demo/src/main/java/com/wolt/blurhashapp/MainActivity.kt index 3d48edba..5402ee91 100644 --- a/Kotlin/demo/src/main/java/com/wolt/blurhashapp/MainActivity.kt +++ b/Kotlin/demo/src/main/java/com/wolt/blurhashapp/MainActivity.kt @@ -4,21 +4,27 @@ import android.graphics.Bitmap import android.os.Bundle import android.os.SystemClock import androidx.appcompat.app.AppCompatActivity +import com.wolt.blurhashapp.databinding.ActivityMainBinding import com.wolt.blurhashkt.BlurHashDecoder -import kotlinx.android.synthetic.main.activity_main.* + class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - tvDecode.setOnClickListener { + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.tvDecode.setOnClickListener { var bitmap: Bitmap? = null val time = timed { - bitmap = BlurHashDecoder.decode(etInput.text.toString(), 20, 12) + bitmap = BlurHashDecoder.decode(binding.etInput.text.toString(), 20, 12) } - ivResult.setImageBitmap(bitmap) - ivResultTime.text = "Time: $time ms" + binding.ivResult.setImageBitmap(bitmap) + binding.ivResultTime.text = "Time: $time ms" } } From 61a93aaa67be2e5b3edad303fa924158fb62dcd7 Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:48:33 -0800 Subject: [PATCH 05/10] Change lib name --- Kotlin/settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Kotlin/settings.gradle b/Kotlin/settings.gradle index 42198e65..7af76dcc 100644 --- a/Kotlin/settings.gradle +++ b/Kotlin/settings.gradle @@ -1 +1 @@ -include ':demo', ':lib' +include ':demo', ':blurhashkt-lib' From 70f18714340cac1d1b7b6ada30f52deae5578ee3 Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:49:39 -0800 Subject: [PATCH 06/10] Change name blurhash for blurhashkt to specify this is a kotlin lib. --- Kotlin/{lib => blurhashkt-lib}/src/main/AndroidManifest.xml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Kotlin/{lib => blurhashkt-lib}/src/main/AndroidManifest.xml (100%) diff --git a/Kotlin/lib/src/main/AndroidManifest.xml b/Kotlin/blurhashkt-lib/src/main/AndroidManifest.xml similarity index 100% rename from Kotlin/lib/src/main/AndroidManifest.xml rename to Kotlin/blurhashkt-lib/src/main/AndroidManifest.xml From aca4d565b51c1ea1f94aec0df3762845c2607f90 Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:50:08 -0800 Subject: [PATCH 07/10] Add maven dependencies and configuration for jitpack.io --- Kotlin/blurhashkt-lib/build.gradle | 46 +++++++++++++++++++ .../proguard-rules.pro | 0 .../wolt/blurhashkt/BlurHashDecoderTest.kt | 0 .../com/wolt/blurhashkt/BlurHashDecoder.kt | 0 Kotlin/demo/build.gradle | 6 ++- Kotlin/lib/build.gradle | 33 ------------- 6 files changed, 50 insertions(+), 35 deletions(-) create mode 100644 Kotlin/blurhashkt-lib/build.gradle rename Kotlin/{lib => blurhashkt-lib}/proguard-rules.pro (100%) rename Kotlin/{lib => blurhashkt-lib}/src/androidTest/java/com/wolt/blurhashkt/BlurHashDecoderTest.kt (100%) rename Kotlin/{lib => blurhashkt-lib}/src/main/java/com/wolt/blurhashkt/BlurHashDecoder.kt (100%) delete mode 100644 Kotlin/lib/build.gradle diff --git a/Kotlin/blurhashkt-lib/build.gradle b/Kotlin/blurhashkt-lib/build.gradle new file mode 100644 index 00000000..43fd9fc5 --- /dev/null +++ b/Kotlin/blurhashkt-lib/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'com.android.library' + id 'kotlin-android' + id 'kotlin-kapt' + id 'maven-publish' +} + +android { + + compileSdkVersion 31 + + defaultConfig { + minSdkVersion 14 + targetSdkVersion 31 + versionCode 2 + versionName "1.0.$versionCode" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + testImplementation "junit:junit:4.13.2" +} + + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + from components.release + groupId = 'com.github.wolt' + artifactId = 'blurhash-lib' + version = '1.0.2' + } + } + } +} \ No newline at end of file diff --git a/Kotlin/lib/proguard-rules.pro b/Kotlin/blurhashkt-lib/proguard-rules.pro similarity index 100% rename from Kotlin/lib/proguard-rules.pro rename to Kotlin/blurhashkt-lib/proguard-rules.pro diff --git a/Kotlin/lib/src/androidTest/java/com/wolt/blurhashkt/BlurHashDecoderTest.kt b/Kotlin/blurhashkt-lib/src/androidTest/java/com/wolt/blurhashkt/BlurHashDecoderTest.kt similarity index 100% rename from Kotlin/lib/src/androidTest/java/com/wolt/blurhashkt/BlurHashDecoderTest.kt rename to Kotlin/blurhashkt-lib/src/androidTest/java/com/wolt/blurhashkt/BlurHashDecoderTest.kt diff --git a/Kotlin/lib/src/main/java/com/wolt/blurhashkt/BlurHashDecoder.kt b/Kotlin/blurhashkt-lib/src/main/java/com/wolt/blurhashkt/BlurHashDecoder.kt similarity index 100% rename from Kotlin/lib/src/main/java/com/wolt/blurhashkt/BlurHashDecoder.kt rename to Kotlin/blurhashkt-lib/src/main/java/com/wolt/blurhashkt/BlurHashDecoder.kt diff --git a/Kotlin/demo/build.gradle b/Kotlin/demo/build.gradle index 4db68381..23b1cca2 100644 --- a/Kotlin/demo/build.gradle +++ b/Kotlin/demo/build.gradle @@ -2,6 +2,7 @@ plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-kapt' + id 'maven-publish' } android { @@ -12,7 +13,7 @@ android { applicationId "com.wolt.blurhash" minSdkVersion 14 targetSdkVersion 31 - versionCode 1 + versionCode 2 versionName "1.0" } @@ -31,6 +32,7 @@ android { } dependencies { - implementation project(path: ':lib') + implementation project(path: ':blurhashkt-lib') implementation "androidx.appcompat:appcompat:1.3.1" } + diff --git a/Kotlin/lib/build.gradle b/Kotlin/lib/build.gradle deleted file mode 100644 index 54db51a6..00000000 --- a/Kotlin/lib/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - - compileSdkVersion 31 - - defaultConfig { - minSdkVersion 14 - targetSdkVersion 31 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - - testImplementation "junit:junit:4.13.2" - androidTestImplementation "androidx.test.ext:junit:1.1.3" - androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0" -} From 2a9a72655f7004e85b5eb3750a0e27881c331389 Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:52:15 -0800 Subject: [PATCH 08/10] Add jitpack.yml file configuration for jitpack.io issue Error build with gradle 7.0.2 https://github.com/jitpack/jitpack.io/issues/4824# --- Kotlin/jitpack.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Kotlin/jitpack.yml diff --git a/Kotlin/jitpack.yml b/Kotlin/jitpack.yml new file mode 100644 index 00000000..1bfc0d7d --- /dev/null +++ b/Kotlin/jitpack.yml @@ -0,0 +1,4 @@ +jdk: + - openjdk11 +before_install: + - ./scripts/prepareJitpackEnvironment.sh \ No newline at end of file From 41e8e849077b1d2befe99970daecb58ab1bac8ff Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 06:56:43 -0800 Subject: [PATCH 09/10] FIXED Test dependencies. --- Kotlin/blurhashkt-lib/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Kotlin/blurhashkt-lib/build.gradle b/Kotlin/blurhashkt-lib/build.gradle index 43fd9fc5..a29454aa 100644 --- a/Kotlin/blurhashkt-lib/build.gradle +++ b/Kotlin/blurhashkt-lib/build.gradle @@ -20,7 +20,7 @@ android { buildTypes { release { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -28,7 +28,7 @@ android { } dependencies { - testImplementation "junit:junit:4.13.2" + androidTestImplementation "junit:junit:4.13.2" } From df95c2d459fb0ee4d2494fa206ce27b97e83fe7a Mon Sep 17 00:00:00 2001 From: Orlando Novas Rodriguez <38017009+orlando-dev-code@users.noreply.github.com> Date: Tue, 9 Nov 2021 07:22:38 -0800 Subject: [PATCH 10/10] Add BlurHashEncoder.kt from branch blur-hash-encoder-kotlin untested --- .../com/wolt/blurhashkt/BlurHashEncoder.kt | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 Kotlin/blurhashkt-lib/src/main/java/com/wolt/blurhashkt/BlurHashEncoder.kt diff --git a/Kotlin/blurhashkt-lib/src/main/java/com/wolt/blurhashkt/BlurHashEncoder.kt b/Kotlin/blurhashkt-lib/src/main/java/com/wolt/blurhashkt/BlurHashEncoder.kt new file mode 100644 index 00000000..5772e7ff --- /dev/null +++ b/Kotlin/blurhashkt-lib/src/main/java/com/wolt/blurhashkt/BlurHashEncoder.kt @@ -0,0 +1,169 @@ +package com.wolt.blurhashkt + +import android.graphics.Bitmap +import java.lang.Math.* +import java.nio.IntBuffer +import kotlin.math.PI +import kotlin.math.pow + +object BlurHashEncoder { + + fun blurHash(bitmap: Bitmap, components: Pair): String? { + if (components.first !in 1..9 || components.second !in 1..9) { + return null + } + + + val width = bitmap.width + val height = bitmap.height + val bytesPerRow = bitmap.rowBytes + var pixels: IntArray? = null + bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height) + + if (pixels == null) { + return null + } + + val factors: MutableList> = mutableListOf() + for (y in 0 until components.second) { + for (x in 0 until components.first) { + val normalisation: Float = if (x == 0 && y == 0) 1f else 2f + val factor = + multiplyBasisFunction(pixels, width, height, bytesPerRow, 4, 0) { a, b -> + (normalisation * kotlin.math.cos(PI * x * a / width) * kotlin.math.cos(PI * y * b / height)).toFloat() + } + factors.add(factor) + } + } + + val dc = factors.removeAt(0) + val ac = factors + + var hash = "" + + val sizeFlag = (components.first - 1) + (components.second - 1) * 9 + hash += sizeFlag.encode83(1) + + val maximumValue: Float + if (ac.size > 0) { + val actualMaximumValue = ac.map { it.maxOrNull() }.maxByOrNull { it!! }!! + val quantisedMaximumValue = + 0.0.coerceAtLeast(82.0.coerceAtMost(kotlin.math.floor(actualMaximumValue * 166 - 0.5))) + .toInt() + maximumValue = (quantisedMaximumValue + 1) / 166.0f + hash += quantisedMaximumValue.encode83(1) + } else { + maximumValue = 1f + hash += 0.encode83(1) + } + + hash += encodeDC(dc).encode83(4) + + for (factor in ac) { + hash += encodeAC(factor, maximumValue).encode83(2) + } + + return hash + } + + private fun multiplyBasisFunction( + pixels: IntArray, + width: Int, + height: Int, + bytesPerRow: Int, + bytesPerPixel: Int, + pixelOffset: Int, + basisFunction: (Float, Float) -> Float + ): Array { + var r = 0f + var g = 0f + var b = 0f + + val buffer = IntBuffer.wrap(pixels, pixels.size, height * bytesPerRow) + + for (x in 0 until width) { + for (y in 0 until height) { + val basis = basisFunction(x.toFloat(), y.toFloat()) + r += basis * sRgbToLinear(buffer[bytesPerPixel * x + pixelOffset + 0 + y * bytesPerRow]) + g += basis * sRgbToLinear(buffer[bytesPerPixel * x + pixelOffset + 1 + y * bytesPerRow]) + b += basis * sRgbToLinear(buffer[bytesPerPixel * x + pixelOffset + 2 + y * bytesPerRow]) + } + } + + val scale = 1 / (width * height).toFloat() + + return arrayOf(r * scale, g * scale, b * scale) + } + + + private val encodeCharacters: List = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~".map { it.toString() } + + private fun encodeDC(value: Array): Int { + val roundedR = linearToSRgb(value[0]) + val roundedG = linearToSRgb(value[1]) + val roundedB = linearToSRgb(value[2]) + return (roundedR shl 16) + (roundedG shl 8) + roundedB + } + + private fun encodeAC(value: Array, maximumValue: Float): Int { + 0.0.coerceAtLeast( + 18.0.coerceAtMost( + kotlin.math.floor( + (value[0] / maximumValue.toDouble()).pow(0.5) * 9 + 9.5 + ) + ) + ) + val quantR = 0.0.coerceAtLeast( + 18.0.coerceAtMost( + kotlin.math.floor( + (value[0] / maximumValue.toDouble()).pow(0.5) * 9 + 9.5 + ) + ) + ).toInt() + val quantG = 0.0.coerceAtLeast( + 18.0.coerceAtMost( + kotlin.math.floor( + (value[1] / maximumValue.toDouble()).pow(0.5) * 9 + 9.5 + ) + ) + ).toInt() + val quantB = 0.0.coerceAtLeast( + 18.0.coerceAtMost( + kotlin.math.floor( + (value[2] / maximumValue.toDouble()).pow(0.5) * 9 + 9.5 + ) + ) + ).toInt() + + return quantR * 19 * 19 + quantG * 19 + quantB + } + + private fun sRgbToLinear(value: Int): Float { + val v = value / 255f + return if (v <= 0.04045) (v / 12.92f) else (pow((v + 0.055) / 1.055, 2.4).toFloat()) + } + + private fun linearToSRgb(value: Float): Int { + val v = 0f.coerceAtLeast(1f.coerceAtMost(value)) + return if (v <= 0.0031308f) { + (v * 12.92f * 255 + 0.5f).toInt() + } else { + ((1.055f * v.toDouble().pow(1 / 2.4) - 0.055f) * 255 + 0.5f).toInt() + } + } + + + private fun Int.encode83(length: Int): String { + var result = "" + for (i in 1..length) { + val digit = (this / myPow(83, (length - i))) % 83 + result += encodeCharacters[digit] + } + return result + } + + private fun myPow(base: Int, exponent: Int): Int { + return (0 until exponent).fold(1) { acc, _ -> acc * base } + } +}