Skip to content

Instantly share code, notes, and snippets.

@Mercandj
Last active November 23, 2022 12:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mercandj/0a9faf62aa4f1b7a18a1756d31a6f67d to your computer and use it in GitHub Desktop.
Save Mercandj/0a9faf62aa4f1b7a18a1756d31a6f67d to your computer and use it in GitHub Desktop.
How to improve stylus performances on Android using AndroidX

Android drawing stylus performance

Goal, increase stylus performances on Android: mobile, tablet, Chromebooks...

AndroidX.graphics

Requirements: OpenGL architecture must let you draw "incrementally" (delta) or "fully"

Dependency

implementation("androidx.graphics:graphics-core:1.0.0-alpha02")

Skeleton

package com.mercandalli.android.sdk.draw_stylus

import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.SurfaceView
import androidx.graphics.lowlatency.GLFrontBufferedRenderer
import androidx.graphics.opengl.egl.EGLManager

class DrawStylusView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : SurfaceView(context, attrs, defStyleAttr) {

    private val frontBufferedRenderer = createGLFrontBufferedRenderer()

    init {
        setOnTouchListener(createOnTouchListener())
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun createOnTouchListener(): OnTouchListener {
        var previousX = -1.0f
        var previousY = -1.0f
        return OnTouchListener { _, event ->
            val touchData = FrontAndBuffedLayerInputData(
                motionEvent = event,
                previousX = previousX,
                previousY = previousY
            )
            when (event.action) {
                MotionEvent.ACTION_MOVE -> frontBufferedRenderer.renderFrontBufferedLayer(touchData)
                MotionEvent.ACTION_UP -> frontBufferedRenderer.commit()
            }
            previousX = event.x
            previousY = event.y
            return@OnTouchListener true
        }
    }

    private fun createGLFrontBufferedRenderer(): GLFrontBufferedRenderer<FrontAndBuffedLayerInputData> {
        return GLFrontBufferedRenderer(
            this,
            object : GLFrontBufferedRenderer.Callback<FrontAndBuffedLayerInputData> {
                override fun onDrawFrontBufferedLayer(
                    eglManager: EGLManager,
                    bufferWidth: Int,
                    bufferHeight: Int,
                    transform: FloatArray,
                    param: FrontAndBuffedLayerInputData
                ) {
                    // OpenGL code to render delta of stroke
                    // To render only on a the front layer over
                    Log.d("jm/debug", "DrawStylusView.onDrawFrontBufferedLayer | ")
                }

                override fun onDrawDoubleBufferedLayer(
                    eglManager: EGLManager,
                    bufferWidth: Int,
                    bufferHeight: Int,
                    transform: FloatArray,
                    params: Collection<FrontAndBuffedLayerInputData>
                ) {
                    // OpenGL code to render entire stroke
                    Log.d("jm/debug", "DrawStylusView.onDrawDoubleBufferedLayer | ${params.size}")
                }
            }
        )
    }

    private data class FrontAndBuffedLayerInputData(
        val motionEvent: MotionEvent,
        /** Negative means no previous */
        val previousX: Float,
        /** Negative means no previous */
        val previousY: Float
    )
}

Sources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment