mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-06-06 08:49:14 +02:00
Increasing the speed of the Robohash SVG to buffer function
This commit is contained in:
parent
0429c4dc3a
commit
4b9a55e178
@ -30,9 +30,10 @@ import coil.fetch.Fetcher
|
|||||||
import coil.fetch.SourceResult
|
import coil.fetch.SourceResult
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import coil.request.Options
|
import coil.request.Options
|
||||||
|
import com.vitorpamplona.amethyst.commons.Robohash
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
import com.vitorpamplona.quartz.utils.Robohash
|
import okio.buffer
|
||||||
import okio.Buffer
|
import okio.source
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
@ -43,14 +44,10 @@ class HashImageFetcher(
|
|||||||
) : Fetcher {
|
) : Fetcher {
|
||||||
override suspend fun fetch(): SourceResult {
|
override suspend fun fetch(): SourceResult {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
val source =
|
val source =
|
||||||
try {
|
try {
|
||||||
val buffer = Buffer()
|
Robohash.assemble(data.toString(), isLightTheme).byteInputStream(Charset.defaultCharset()).source().buffer()
|
||||||
buffer.writeString(
|
|
||||||
Robohash.assemble(data.toString(), isLightTheme),
|
|
||||||
Charset.defaultCharset(),
|
|
||||||
)
|
|
||||||
buffer
|
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,16 +18,20 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.benchmark
|
package com.vitorpamplona.amethyst.benchmark
|
||||||
|
|
||||||
import androidx.benchmark.junit4.BenchmarkRule
|
import androidx.benchmark.junit4.BenchmarkRule
|
||||||
import androidx.benchmark.junit4.measureRepeated
|
import androidx.benchmark.junit4.measureRepeated
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.utils.Robohash
|
import com.vitorpamplona.amethyst.commons.Robohash
|
||||||
import junit.framework.TestCase.assertEquals
|
import junit.framework.TestCase.assertEquals
|
||||||
|
import okio.Buffer
|
||||||
|
import okio.buffer
|
||||||
|
import okio.source
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Benchmark, which will execute on an Android device.
|
* Benchmark, which will execute on an Android device.
|
||||||
@ -41,7 +45,7 @@ class RobohashBenchmark {
|
|||||||
|
|
||||||
val warmHex = "f4f016c739b8ec0d6313540a8b12cf48a72b485d38338627ec9d427583551f9a"
|
val warmHex = "f4f016c739b8ec0d6313540a8b12cf48a72b485d38338627ec9d427583551f9a"
|
||||||
val testHex = "48a72b485d38338627ec9d427583551f9af4f016c739b8ec0d6313540a8b12cf"
|
val testHex = "48a72b485d38338627ec9d427583551f9af4f016c739b8ec0d6313540a8b12cf"
|
||||||
val resultingSVG =
|
val expectedTestSVG =
|
||||||
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 300 300\">" +
|
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 300 300\">" +
|
||||||
"<defs>" +
|
"<defs>" +
|
||||||
"<style>" +
|
"<style>" +
|
||||||
@ -196,7 +200,26 @@ class RobohashBenchmark {
|
|||||||
Robohash.assemble(warmHex, true)
|
Robohash.assemble(warmHex, true)
|
||||||
benchmarkRule.measureRepeated {
|
benchmarkRule.measureRepeated {
|
||||||
val result = Robohash.assemble(testHex, true)
|
val result = Robohash.assemble(testHex, true)
|
||||||
assertEquals(resultingSVG, result)
|
assertEquals(expectedTestSVG, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createSVGInBufferCopy() {
|
||||||
|
// warm up
|
||||||
|
Robohash.assemble(warmHex, true)
|
||||||
|
benchmarkRule.measureRepeated {
|
||||||
|
val buffer = Buffer()
|
||||||
|
buffer.writeString(Robohash.assemble(testHex, true), Charset.defaultCharset())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createSVGInBufferViaInputStream() {
|
||||||
|
// warm up
|
||||||
|
Robohash.assemble(warmHex, true)
|
||||||
|
benchmarkRule.measureRepeated {
|
||||||
|
Robohash.assemble(testHex, true).byteInputStream(Charset.defaultCharset()).source().buffer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -35,6 +35,8 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation project(path: ':quartz')
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||||
|
@ -18,10 +18,9 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.utils
|
package com.vitorpamplona.amethyst.commons
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Immutable
|
|
||||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||||
import com.vitorpamplona.quartz.encoders.Hex
|
import com.vitorpamplona.quartz.encoders.Hex
|
||||||
import com.vitorpamplona.quartz.encoders.HexValidator
|
import com.vitorpamplona.quartz.encoders.HexValidator
|
||||||
@ -55,9 +54,7 @@ object Robohash {
|
|||||||
private fun reduce(
|
private fun reduce(
|
||||||
start: Int,
|
start: Int,
|
||||||
channel: Byte,
|
channel: Byte,
|
||||||
): Int {
|
) = (start + (channel.toUByte().toInt() * 0.3906f)).toInt()
|
||||||
return (start + (channel.toUByte().toInt() * 0.3906f)).toInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bytesToRGB(
|
private fun bytesToRGB(
|
||||||
r: Byte,
|
r: Byte,
|
||||||
@ -85,6 +82,7 @@ object Robohash {
|
|||||||
Log.w("Robohash", "$msg is not a hex")
|
Log.w("Robohash", "$msg is not a hex")
|
||||||
CryptoUtils.sha256(msg.toByteArray())
|
CryptoUtils.sha256(msg.toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
val bgColor = bytesToRGB(hash[0], hash[1], hash[2], isLightTheme)
|
val bgColor = bytesToRGB(hash[0], hash[1], hash[2], isLightTheme)
|
||||||
val fgColor = bytesToRGB(hash[3], hash[4], hash[5], !isLightTheme)
|
val fgColor = bytesToRGB(hash[3], hash[4], hash[5], !isLightTheme)
|
||||||
val body = bodies[byteMod10(hash[6])]
|
val body = bodies[byteMod10(hash[6])]
|
||||||
@ -110,41 +108,42 @@ object Robohash {
|
|||||||
BACKGROUND.length +
|
BACKGROUND.length +
|
||||||
END.length
|
END.length
|
||||||
|
|
||||||
val result = StringBuilder(capacity)
|
val result =
|
||||||
|
buildString(capacity) {
|
||||||
|
append(HEADER)
|
||||||
|
|
||||||
result.append(HEADER)
|
append(".cls-bg{fill:")
|
||||||
|
append(bgColor)
|
||||||
|
append(";}.cls-fill-1{fill:")
|
||||||
|
append(fgColor)
|
||||||
|
append(";}.cls-fill-2{fill:")
|
||||||
|
append(fgColor)
|
||||||
|
append(";}")
|
||||||
|
|
||||||
result.append(".cls-bg{fill:")
|
append(body.style)
|
||||||
result.append(bgColor)
|
append(face.style)
|
||||||
result.append(";}.cls-fill-1{fill:")
|
append(eye.style)
|
||||||
result.append(fgColor)
|
append(mouth.style)
|
||||||
result.append(";}.cls-fill-2{fill:")
|
append(accessory.style)
|
||||||
result.append(fgColor)
|
|
||||||
result.append(";}")
|
|
||||||
|
|
||||||
result.append(body.style)
|
append(MID)
|
||||||
result.append(face.style)
|
|
||||||
result.append(eye.style)
|
|
||||||
result.append(mouth.style)
|
|
||||||
result.append(accessory.style)
|
|
||||||
|
|
||||||
result.append(MID)
|
append(BACKGROUND)
|
||||||
|
append(body.paths)
|
||||||
|
append(face.paths)
|
||||||
|
append(eye.paths)
|
||||||
|
append(mouth.paths)
|
||||||
|
append(accessory.paths)
|
||||||
|
|
||||||
result.append(BACKGROUND)
|
append(END)
|
||||||
result.append(body.paths)
|
|
||||||
result.append(face.paths)
|
|
||||||
result.append(eye.paths)
|
|
||||||
result.append(mouth.paths)
|
|
||||||
result.append(accessory.paths)
|
|
||||||
|
|
||||||
result.append(END)
|
|
||||||
|
|
||||||
val resultStr = result.toString()
|
|
||||||
check(resultStr.length == capacity) { "${resultStr.length} was different from $capacity" }
|
|
||||||
return resultStr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable private data class Part(val style: String, val paths: String)
|
check(result.length == capacity) { "${result.length} was different from $capacity" }
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class Part(val style: String, val paths: String)
|
||||||
|
|
||||||
const val HEADER =
|
const val HEADER =
|
||||||
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 300 300\"><defs><style>"
|
"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 300 300\"><defs><style>"
|
Loading…
x
Reference in New Issue
Block a user