Come rendere l'image della videocamera YUV-NV21 Android sullo background in libgdx con OpenGLES 2.0 in tempo reale?

A differenza di Android, sono relativamente nuovo a GL / libgdx. Il task che devo risolvere, vale a dire rendere l'image di anteprima YUV-NV21 della camera Android allo background dello schermo all'interno di libgdx in tempo reale è sfaccettata. Ecco le principali preoccupazioni:

  1. L'image di anteprima della camera Android è garantita solo nello spazio YUV-NV21 (e nello stesso spazio YV12 in cui i canali U e V non sono interlacciati ma raggruppati). Supponendo che i più moderni dispositivi forniranno la conversione implicita RGB è MOLTO sbagliato, ad esempio la versione più recente di Samsung Note 10.1 2014 fornisce solo i formati YUV. Dal momento che niente può essere disegnato allo schermo in OpenGL a less che non sia in RGB, lo spazio colore deve in qualche modo essere convertito.

  2. Controllare il numero di ingresso è memorizzato nell'elenco Contatti o non android
  3. Scattare foto in android each 5 secondi
  4. Errore di Qucikblox: per l'accesso a Twitter in Android
  5. Imansible get la posizione dell'utente
  6. il metodo onActivityResult non si chiama Android
  7. Simulare i controlli di touch attraverso il codice
  8. L'esempio nella documentazione libgdx ( Integrazione di libgdx e la camera del dispositivo ) utilizza una vista di superficie Android che è al di sotto di tutto per disegnare l'image con GLES 1.1. Dall'inizio di marzo 2014, il supporto OpenGLES 1.x viene rimosso da libgdx a causa di essere obsoleto e quasi tutti i dispositivi che supportno ora GLES 2.0. Se si tenta lo stesso campione con GLES 2.0, gli oggetti 3D che si disegnano sull'image saranno semi trasparenti. Poiché la superficie dietro non ha niente a che fare con GL, questo non può veramente essere controllato. La distriggerszione di BLENDING / TRANSLUCENCY non funziona. Pertanto, rendere questa image deve essere fatta puramente in GL.

  9. Questo deve essere fatto in tempo reale, quindi la conversione dello spazio colore deve essere MOLTO veloce. La conversione del software con bitmap di Android probabilmente sarà troppo lenta.

  10. Come caratteristica laterale, l'image della camera deve essere accessibile dal codice Android per eseguire altre attività che disegnarlo sullo schermo, ad esempio inviandolo a un processre di immagini nativo tramite JNI.

La domanda è, come è compiuto questo task correttamente e quanto più veloce ansible?

  • È una buona idea rimuovere id per i dispositivi di test per AdMob quando pubblica un'applicazione?
  • Invia un'image da Android a un servizio Web ASP.NET
  • Come puoi visualizzare il text a testa in giù con una visualizzazione di text in Android?
  • Zbar con Android: la visualizzazione della camera dello scanner rimane intriggers e nera dopo aver mostrato l'url nel browser
  • AVD molto lento su Android 4.0
  • La nuova libreria di supporto delle preferenze non è corretto durante il runtime
  • 2 Solutions collect form web for “Come rendere l'image della videocamera YUV-NV21 Android sullo background in libgdx con OpenGLES 2.0 in tempo reale?”

    La breve risposta consiste nel caricare i file delle immagini della telecamera (Y, UV) in texture e disegnare queste texture su una maglia usando un shader di frammento personalizzato che farà la conversione dello spazio colore per noi. Poiché questo shader sarà in esecuzione sulla GPU, sarà molto più veloce della CPU e certamente molto più veloce del codice Java. Poiché questa maglia fa parte di GL, qualsiasi altra forma 3D o sprites 3D può essere disegnata in sicurezza o sotto di essa.

    Ho risolto il problema a partire da questa risposta https://stackoverflow.com/a/17615696/1525238 . Ho compreso il metodo generale utilizzando il seguente collegamento: Come utilizzare la visualizzazione della camera con OpenGL ES , è scritta per Bada, ma i principi sono gli stessi. Le formule di conversione erano un po 'strano e ho sostituito quelle con quelle dell'articolo YUV Conversione da Wikipedia a / da RGB .

    Di seguito sono riportti i passaggi che portno alla soluzione:

    Spiegazione YUV-NV21

    Le immagini in diretta dalla camera Android sono immagini di anteprima. Lo spazio di colore predefinito (e uno dei due spazi di colore garantiti) è YUV-NV21 per l'anteprima della telecamera. La spiegazione di questo formato è molto sparso, perciò vi spiegherò qui brevemente:

    I dati dell'image sono fatti di (width x altezza) x 3/2 byte. Le prime larghezze x byte di altezza sono il canale Y, 1 byte di luminosità per each pixel. La seguente (width / 2) x (altezza / 2) x 2 = width x altezza / 2 byte è il piano UV. Ogni due byte consecutivi sono i byte dei chromati V, U (in quell'ordine secondo la specifica NV21) per i pixel originali 2 x 2 = 4 . In altre parole, il piano UV è di size (width / 2) x (altezza / 2) e viene scandito da un fattore di 2 in each dimensione. Inoltre, i byte cromatici U, V vengono interleati.

    Ecco un'image molto bella che spiega la YUV-NV12, NV21 è solo U, V byte invertiti:

    YUV-NV12

    Come convertire questo formato in RGB?

    Come indicato nella domanda, questa conversione richiederebbe troppo tempo per essere live se eseguita all'interno del codice Android. Fortunatamente, può essere fatto all'interno di un shader GL, che funziona sulla GPU. Questo permetterà di eseguire MOLTO veloce.

    L'idea generale è quella di passare i canali della nostra image come texture allo shader e renderli in un modo che fa la conversione RGB. Per questo, dobbiamo prima copiare i canali nella nostra image a buffer che possono essere passati a texture:

    byte[] image; ByteBuffer yBuffer, uvBuffer; ... yBuffer.put(image, 0, width*height); yBuffer.position(0); uvBuffer.put(image, width*height, width*height/2); uvBuffer.position(0); 

    Quindi, passeremo questi buffer a texture GL effettive:

     /* * Prepare the Y channel texture */ //Set texture slot 0 as active and bind our texture object to it Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0); yTexture.bind(); //Y texture is (width*height) in size and each pixel is one byte; //by setting GL_LUMINANCE, OpenGL puts this byte into R,G and B //components of the texture Gdx.gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_LUMINANCE, width, height, 0, GL20.GL_LUMINANCE, GL20.GL_UNSIGNED_BYTE, yBuffer); //Use linear interpolation when magnifying/minifying the texture to //areas larger/smaller than the texture size Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_LINEAR); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_LINEAR); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_EDGE); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_EDGE); /* * Prepare the UV channel texture */ //Set texture slot 1 as active and bind our texture object to it Gdx.gl.glActiveTexture(GL20.GL_TEXTURE1); uvTexture.bind(); //UV texture is (width/2*height/2) in size (downsampled by 2 in //both dimensions, each pixel corresponds to 4 pixels of the Y channel) //and each pixel is two bytes. By setting GL_LUMINANCE_ALPHA, OpenGL //puts first byte (V) into R,G and B components and of the texture //and the second byte (U) into the A component of the texture. That's //why we find U and V at A and R respectively in the fragment shader code. //Note that we could have also found V at G or B as well. Gdx.gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_LUMINANCE_ALPHA, width/2, height/2, 0, GL20.GL_LUMINANCE_ALPHA, GL20.GL_UNSIGNED_BYTE, uvBuffer); //Use linear interpolation when magnifying/minifying the texture to //areas larger/smaller than the texture size Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_LINEAR); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_LINEAR); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_EDGE); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_EDGE); 

    Successivamente, rendiamo la maglia che abbiamo preparato in precedenza (copre l'integer schermo). Lo shader si prenderà cura di rendere le textures vincolate sulla mesh:

     shader.begin(); //Set the uniform y_texture object to the texture at slot 0 shader.setUniformi("y_texture", 0); //Set the uniform uv_texture object to the texture at slot 1 shader.setUniformi("uv_texture", 1); mesh.render(shader, GL20.GL_TRIANGLES); shader.end(); 

    Infine, lo shader assume il task di rendere le nostre strutture alla mesh. Lo shader del frammento che realizza la conversione effettiva è il seguente:

     String fragmentShader = "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "varying vec2 v_texCoord;\n" + "uniform sampler2D y_texture;\n" + "uniform sampler2D uv_texture;\n" + "void main (void){\n" + " float r, g, b, y, u, v;\n" + //We had put the Y values of each pixel to the R,G,B components by //GL_LUMINANCE, that's why we're pulling it from the R component, //we could also use G or B " y = texture2D(y_texture, v_texCoord).r;\n" + //We had put the U and V values of each pixel to the A and R,G,B //components of the texture respectively using GL_LUMINANCE_ALPHA. //Since U,V bytes are interspread in the texture, this is probably //the fastest way to use them in the shader " u = texture2D(uv_texture, v_texCoord).a - 0.5;\n" + " v = texture2D(uv_texture, v_texCoord).r - 0.5;\n" + //The numbers are just YUV to RGB conversion constants " r = y + 1.13983*v;\n" + " g = y - 0.39465*u - 0.58060*v;\n" + " b = y + 2.03211*u;\n" + //We finally set the RGB color of our pixel " gl_FragColor = vec4(r, g, b, 1.0);\n" + "}\n"; 

    Si prega di notare che stiamo accedendo alle texture Y e UV usando la stessa variabile coordinata v_texCoord , dovuta a v_texCoord tra -1.0 e 1.0 che scala da un'estremità della texture all'altra invece delle coordinate di pixel effettive della texture. Questa è una delle caratteristiche più belle dei shader.

    Il codice sorgente completo

    Dal momento che la libgdx è cross-platform, abbiamo bisogno di un object che può essere esteso diversamente in diverse piattaforms che gestisce la camera del dispositivo e rendering. Ad esempio, si potrebbe desiderare di ignorare completamente la conversione shader YUV-RGB se è ansible get l'hardware per fornire immagini RGB. Per questo motivo, abbiamo bisogno di un'interface di controllo della camera per dispositivi che verrà implementata da each piattaforma diversa:

     public interface PlatformDependentCameraController { void init(); void renderBackground(); void destroy(); } 

    La versione Android di questa interface è la seguente (l'image della videocamera in diretta si suppone sia di 1280×720 pixel):

     public class AndroidDependentCameraController implements PlatformDependentCameraController, Camera.PreviewCallback { private static byte[] image; //The image buffer that will hold the camera image when preview callback arrives private Camera camera; //The camera object //The Y and UV buffers that will pass our image channel data to the textures private ByteBuffer yBuffer; private ByteBuffer uvBuffer; ShaderProgram shader; //Our shader Texture yTexture; //Our Y texture Texture uvTexture; //Our UV texture Mesh mesh; //Our mesh that we will draw the texture on public AndroidDependentCameraController(){ //Our YUV image is 12 bits per pixel image = new byte[1280*720/8*12]; } @Override public void init(){ /* * Initialize the OpenGL/libgdx stuff */ //Do not enforce power of two texture sizes Texture.setEnforcePotImages(false); //Allocate textures yTexture = new Texture(1280,720,Format.Intensity); //A 8-bit per pixel format uvTexture = new Texture(1280/2,720/2,Format.LuminanceAlpha); //A 16-bit per pixel format //Allocate buffers on the native memory space, not inside the JVM heap yBuffer = ByteBuffer.allocateDirect(1280*720); uvBuffer = ByteBuffer.allocateDirect(1280*720/2); //We have (width/2*height/2) pixels, each pixel is 2 bytes yBuffer.order(ByteOrder.nativeOrder()); uvBuffer.order(ByteOrder.nativeOrder()); //Our vertex shader code; nothing special String vertexShader = "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "varying vec2 v_texCoord; \n" + "void main(){ \n" + " gl_Position = a_position; \n" + " v_texCoord = a_texCoord; \n" + "} \n"; //Our fragment shader code; takes Y,U,V values for each pixel and calculates R,G,B colors, //Effectively making YUV to RGB conversion String fragmentShader = "#ifdef GL_ES \n" + "precision highp float; \n" + "#endif \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D y_texture; \n" + "uniform sampler2D uv_texture; \n" + "void main (void){ \n" + " float r, g, b, y, u, v; \n" + //We had put the Y values of each pixel to the R,G,B components by GL_LUMINANCE, //that's why we're pulling it from the R component, we could also use G or B " y = texture2D(y_texture, v_texCoord).r; \n" + //We had put the U and V values of each pixel to the A and R,G,B components of the //texture respectively using GL_LUMINANCE_ALPHA. Since U,V bytes are interspread //in the texture, this is probably the fastest way to use them in the shader " u = texture2D(uv_texture, v_texCoord).a - 0.5; \n" + " v = texture2D(uv_texture, v_texCoord).r - 0.5; \n" + //The numbers are just YUV to RGB conversion constants " r = y + 1.13983*v; \n" + " g = y - 0.39465*u - 0.58060*v; \n" + " b = y + 2.03211*u; \n" + //We finally set the RGB color of our pixel " gl_FragColor = vec4(r, g, b, 1.0); \n" + "} \n"; //Create and compile our shader shader = new ShaderProgram(vertexShader, fragmentShader); //Create our mesh that we will draw on, it has 4 vertices corresponding to the 4 corners of the screen mesh = new Mesh(true, 4, 6, new VertexAttribute(Usage.Position, 2, "a_position"), new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord")); //The vertices include the screen coordinates (between -1.0 and 1.0) and texture coordinates (between 0.0 and 1.0) float[] vertices = { -1.0f, 1.0f, // Position 0 0.0f, 0.0f, // TexCoord 0 -1.0f, -1.0f, // Position 1 0.0f, 1.0f, // TexCoord 1 1.0f, -1.0f, // Position 2 1.0f, 1.0f, // TexCoord 2 1.0f, 1.0f, // Position 3 1.0f, 0.0f // TexCoord 3 }; //The indices come in trios of vertex indices that describe the triangles of our mesh short[] indices = {0, 1, 2, 0, 2, 3}; //Set vertices and indices to our mesh mesh.setVertices(vertices); mesh.setIndices(indices); /* * Initialize the Android camera */ camera = Camera.open(0); //We set the buffer ourselves that will be used to hold the preview image camera.setPreviewCallbackWithBuffer(this); //Set the camera parameters Camera.Parameters params = camera.getParameters(); params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); params.setPreviewSize(1280,720); camera.setParameters(params); //Start the preview camera.startPreview(); //Set the first buffer, the preview doesn't start unless we set the buffers camera.addCallbackBuffer(image); } @Override public void onPreviewFrame(byte[] data, Camera camera) { //Send the buffer reference to the next preview so that a new buffer is not allocated and we use the same space camera.addCallbackBuffer(image); } @Override public void renderBackground() { /* * Because of Java's limitations, we can't reference the middle of an arrays and * we must copy the channels in our byte arrays into buffers before setting them to textures */ //Copy the Y channel of the image into its buffer, the first (width*height) bytes are the Y channel yBuffer.put(image, 0, 1280*720); yBuffer.position(0); //Copy the UV channels of the image into their buffer, the following (width*height/2) bytes are the UV channel; the U and V bytes are interspread uvBuffer.put(image, 1280*720, 1280*720/2); uvBuffer.position(0); /* * Prepare the Y channel texture */ //Set texture slot 0 as active and bind our texture object to it Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0); yTexture.bind(); //Y texture is (width*height) in size and each pixel is one byte; by setting GL_LUMINANCE, OpenGL puts this byte into R,G and B components of the texture Gdx.gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_LUMINANCE, 1280, 720, 0, GL20.GL_LUMINANCE, GL20.GL_UNSIGNED_BYTE, yBuffer); //Use linear interpolation when magnifying/minifying the texture to areas larger/smaller than the texture size Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_LINEAR); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_LINEAR); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_EDGE); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_EDGE); /* * Prepare the UV channel texture */ //Set texture slot 1 as active and bind our texture object to it Gdx.gl.glActiveTexture(GL20.GL_TEXTURE1); uvTexture.bind(); //UV texture is (width/2*height/2) in size (downsampled by 2 in both dimensions, each pixel corresponds to 4 pixels of the Y channel) //and each pixel is two bytes. By setting GL_LUMINANCE_ALPHA, OpenGL puts first byte (V) into R,G and B components and of the texture //and the second byte (U) into the A component of the texture. That's why we find U and V at A and R respectively in the fragment shader code. //Note that we could have also found V at G or B as well. Gdx.gl.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_LUMINANCE_ALPHA, 1280/2, 720/2, 0, GL20.GL_LUMINANCE_ALPHA, GL20.GL_UNSIGNED_BYTE, uvBuffer); //Use linear interpolation when magnifying/minifying the texture to areas larger/smaller than the texture size Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_LINEAR); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_LINEAR); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, GL20.GL_CLAMP_TO_EDGE); Gdx.gl.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, GL20.GL_CLAMP_TO_EDGE); /* * Draw the textures onto a mesh using our shader */ shader.begin(); //Set the uniform y_texture object to the texture at slot 0 shader.setUniformi("y_texture", 0); //Set the uniform uv_texture object to the texture at slot 1 shader.setUniformi("uv_texture", 1); //Render our mesh using the shader, which in turn will use our textures to render their content on the mesh mesh.render(shader, GL20.GL_TRIANGLES); shader.end(); } @Override public void destroy() { camera.stopPreview(); camera.setPreviewCallbackWithBuffer(null); camera.release(); } } 

    La parte principale dell'applicazione assicura solo che init() sia chiamata una volta all'inizio, renderBackground() viene chiamato each ciclo di rendering e destroy() viene chiamato una volta alla fine:

     public class YourApplication implements ApplicationListener { private final PlatformDependentCameraController deviceCameraControl; public YourApplication(PlatformDependentCameraController cameraControl) { this.deviceCameraControl = cameraControl; } @Override public void create() { deviceCameraControl.init(); } @Override public void render() { Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); //Render the background that is the live camera image deviceCameraControl.renderBackground(); /* * Render anything here (sprites/models etc.) that you want to go on top of the camera image */ } @Override public void dispose() { deviceCameraControl.destroy(); } @Override public void resize(int width, int height) { } @Override public void pause() { } @Override public void resume() { } } 

    L'unica altra parte specifica per Android è il seguente codice Android estremamente corto, è sufficiente creare un nuovo gestore della camera specifica per Android e passarlo all'object principale libgdx:

     public class MainActivity extends AndroidApplication { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration(); cfg.useGL20 = true; //This line is obsolete in the newest libgdx version cfg.a = 8; cfg.b = 8; cfg.g = 8; cfg.r = 8; PlatformDependentCameraController cameraControl = new AndroidDependentCameraController(); initialize(new YourApplication(cameraControl), cfg); graphics.getView().setKeepScreenOn(true); } } 

    Quanto è veloce?

    Ho provato questa routine su due dispositivi. Mentre le misurazioni non sono costanti tra i fotogrammi, è ansible osservare un profilo generale:

    1. Samsung Galaxy Note II LTE – (GT-N7105): ha GPU ARM Mali-400 MP4.

      • Rendering di un fotogramma richiede circa 5-6 ms, con salti occasionali a circa 15 ms each due secondi
      • La linea effettiva di rendering ( mesh.render(shader, GL20.GL_TRIANGLES); ) richiede costantemente 0-1 ms
      • La creazione e il legame di entrambe le texture costantemente richiedono 1-3 ms in totale
      • Le copie ByteBuffer generalmente prendono 1-3 ms in totale, ma saltano a circa 7ms occasionalmente, probabilmente a causa del buffer di image spostato nel mucchio di JVM
    2. Samsung Galaxy Note 10.1 2014 – (SM-P600): ha GPU ARM Mali-T628.

      • Rendering di un fotogramma richiede circa 2-4 ms, con salti rari a circa 6-10 ms
      • La linea effettiva di rendering ( mesh.render(shader, GL20.GL_TRIANGLES); ) richiede costantemente 0-1 ms
      • La creazione e il binding di entrambe le texture richiedono 1-3 ms in totale ma salta a circa 6-9 ms each due secondi
      • Copie ByteBuffer generalmente richiedono 0-2 ms in totale, ma saltano a circa 6ms molto raramente

    Non esitate a condividere se pensate che questi profili possono essere resi più veloci con altri methods. Spero che questo piccolo tutorial abbia aiutato.

    Per il modo più veloce e ottimizzato, utilizza semplicemente la GL Extention comune

     //Fragment Shader #extension GL_OES_EGL_image_external : require uniform samplerExternalOES u_Texture; 

    Che in Java

     surfaceTexture = new SurfaceTexture(textureIDs[0]); try { someCamera.setPreviewTexture(surfaceTexture); } catch (IOException t) { Log.e(TAG, "Cannot set preview texture target!"); } someCamera.startPreview(); private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; 

    In Java GL Thread

     GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureIDs[0]); GLES20.glUniform1i(uTextureHandle, 0); 

    La conversione del colore è già stata eseguita per te. Puoi fare quello che vuoi proprio nel shader del frammento.

    In assoluto la sua soluzione nessuna Libgdx poiché è dipendente dalla piattaforma. È ansible inizializzare la roba dipendente dalla piattaforma nel wraper e inviarla all'attività di Libgdx.

    Spero che ti risparmia un po 'di tempo nella tua ricerca.

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.