diff --git a/AndroidApp/app/build.gradle b/AndroidApp/app/build.gradle
index 390b7f1..e1b8c49 100644
--- a/AndroidApp/app/build.gradle
+++ b/AndroidApp/app/build.gradle
@@ -51,6 +51,7 @@ dependencies {
implementation 'com.google.android.material:material:1.1.0'
implementation "androidx.documentfile:documentfile:1.0.1"
implementation 'androidx.preference:preference:1.1.0'
+ implementation 'commons-io:commons-io:2.6'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
diff --git a/AndroidApp/app/src/main/AndroidManifest.xml b/AndroidApp/app/src/main/AndroidManifest.xml
index 8268750..e528c6e 100644
--- a/AndroidApp/app/src/main/AndroidManifest.xml
+++ b/AndroidApp/app/src/main/AndroidManifest.xml
@@ -13,10 +13,10 @@
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
- android:roundIcon="@mipmap/ic_launcher_round"
+ android:roundIcon="@drawable/icon"
android:supportsRtl="true"
android:theme="@style/AppTheme">
-
+
diff --git a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/EmulatorActivity.java b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/EmulatorActivity.java
index 97b6700..7871c7c 100644
--- a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/EmulatorActivity.java
+++ b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/EmulatorActivity.java
@@ -377,7 +377,7 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(this /* Activity context */);
- Boolean skipBios = sharedPreferences.getBoolean("skip_bios", false);
+ boolean skipBios = sharedPreferences.getBoolean("skip_bios", false);
if (null != savedInstanceState && (saveFilePath = savedInstanceState.getString("saveFile")) != null) {
final EmulatorActivity thisActivity = this;
@@ -451,6 +451,10 @@ public class EmulatorActivity extends AppCompatActivity implements View.OnClickL
case R.id.action_save_snapshot:
doSaveSnapshot();
return true;
+ case R.id.action_settings:
+ Intent intent = new Intent(this, SettingsActivity.class);
+ startActivity(intent);
+ return true;
default:
return super.onOptionsItemSelected(item);
}
diff --git a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/ScreenRenderer.java b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/ScreenRenderer.java
index d0a3bce..036f4cd 100644
--- a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/ScreenRenderer.java
+++ b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/ScreenRenderer.java
@@ -1,13 +1,24 @@
package com.mrmichel.rustdroid_emu.ui;
+import android.content.Context;
+import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
+import androidx.preference.PreferenceManager;
+
+import com.mrmichel.rustdroid_emu.R;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
+import java.nio.charset.StandardCharsets;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -16,12 +27,53 @@ public class ScreenRenderer implements GLSurfaceView.Renderer {
private ScreenTexture texture;
private boolean ready = false;
+ private Context context;
+
+ public ScreenRenderer(Context context) {
+ this.context = context;
+ }
+
+ public void updateTexture(int[] frameBuffer) {
+ this.texture.update(frameBuffer);
+ }
+
+ public void initTextureIfNotInitialized() {
+ if (this.texture == null) {
+ this.texture = new ScreenTexture(this.context);
+ }
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ initTextureIfNotInitialized();
+ ready = true;
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ gl.glViewport(0, 0, width, height);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ this.texture.render();
+ }
+
+ public boolean isReady() {
+ return ready;
+ }
+
+ public void setColorCorrection(boolean colorCorrection) {
+ this.texture.setColorCorrection(colorCorrection);
+ }
/**
* Private class to manage the screen texture rendering
*/
private class ScreenTexture {
- int shaderProgram;
+ int normalShaderProgram;
+ int colorCorrectionShaderProgram;
+ int currentShaderProgram;
int positionHandle;
int texCoordHandle;
int samplerHandle;
@@ -32,6 +84,7 @@ public class ScreenRenderer implements GLSurfaceView.Renderer {
private ByteBuffer indicesBuffer;
private Bitmap bitmap;
+ private Context context;
// square vertices
private float[] vertices = {
@@ -55,26 +108,75 @@ public class ScreenRenderer implements GLSurfaceView.Renderer {
0, 2, 3
};
- private static final String VERTEX_SHADER_CODE =
- "attribute vec4 a_position; \n" +
- "attribute vec2 a_texCoord; \n" +
- "varying vec2 v_texCoord; \n" +
- "void main() \n" +
- "{ \n" +
- " gl_Position = a_position; \n" +
- " v_texCoord = a_texCoord; \n" +
- "} \n";
+ public ScreenTexture(Context context) {
+ this.context = context;
+ this.bitmap = Bitmap.createBitmap(240, 160, Bitmap.Config.RGB_565);
- private static final String FRAGMENT_SHADER_CODE =
- "precision mediump float; \n" +
- "varying vec2 v_texCoord; \n" +
- "uniform sampler2D s_texture; \n" +
- "void main() \n" +
- "{ \n" +
- " vec4 color = texture2D( s_texture, v_texCoord ); \n" +
- " gl_FragColor = color; \n" +
- "} \n";
+ GLES20.glEnable(GLES20.GL_TEXTURE_2D);
+ // create vertex array
+ vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ vertexBuffer.put(vertices);
+ vertexBuffer.position(0);
+
+ // create texture coordinate array
+ textureBuffer = ByteBuffer.allocateDirect(textureVertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
+ textureBuffer.put(textureVertices);
+ textureBuffer.position(0);
+
+ // create triangle index array
+ indicesBuffer = ByteBuffer.allocateDirect(indicies.length).order(ByteOrder.nativeOrder());
+ indicesBuffer.put(indicies);
+ indicesBuffer.position(0);
+
+ textureId = createTexture();
+
+ String vertexShader = readShaderResource(R.raw.screen_texture_vertex_shader);
+ String normalFragmentShader = readShaderResource(R.raw.screen_texture_fragment_shader);
+ String colorCorrectionFragmentShader = readShaderResource(R.raw.screen_texture_color_correction_fragment_shader);
+
+ normalShaderProgram = createShaderProgram(vertexShader, normalFragmentShader);
+ colorCorrectionShaderProgram = createShaderProgram(vertexShader, colorCorrectionFragmentShader);
+
+ SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.context);
+ boolean colorCorrection = sharedPreferences.getBoolean("color_correction", false);
+ setColorCorrection(colorCorrection);
+
+ GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ }
+
+ private void setColorCorrection(boolean colorCorrection) {
+
+ if (colorCorrection) {
+ currentShaderProgram = colorCorrectionShaderProgram;
+ } else {
+ currentShaderProgram = normalShaderProgram;
+ }
+
+ // use the program
+ GLES20.glUseProgram(currentShaderProgram);
+
+ positionHandle = GLES20.glGetAttribLocation(currentShaderProgram, "a_position");
+
+ texCoordHandle = GLES20.glGetAttribLocation(currentShaderProgram, "a_texCoord");
+
+ samplerHandle = GLES20.glGetUniformLocation(currentShaderProgram, "s_texture");
+
+
+ // load the vertex position
+ GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);
+ GLES20.glEnableVertexAttribArray(positionHandle);
+ // load texture coordinate
+ GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
+ GLES20.glEnableVertexAttribArray(texCoordHandle);
+
+
+ GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);
+ GLES20.glEnableVertexAttribArray(positionHandle);
+ // load texture coordinate
+ GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
+ GLES20.glEnableVertexAttribArray(texCoordHandle);
+ }
private int compileShader(int type, String code) {
int shader = GLES20.glCreateShader(type);
@@ -123,58 +225,28 @@ public class ScreenRenderer implements GLSurfaceView.Renderer {
return texturesIds[0];
}
- public ScreenTexture() {
- this.bitmap = Bitmap.createBitmap(240, 160, Bitmap.Config.RGB_565);
-
- GLES20.glEnable(GLES20.GL_TEXTURE_2D);
-
- // create vertex array
- vertexBuffer = ByteBuffer.allocateDirect(vertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
- vertexBuffer.put(vertices);
- vertexBuffer.position(0);
-
- // create texture coordinate array
- textureBuffer = ByteBuffer.allocateDirect(textureVertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
- textureBuffer.put(textureVertices);
- textureBuffer.position(0);
-
- // create triangle index array
- indicesBuffer = ByteBuffer.allocateDirect(indicies.length).order(ByteOrder.nativeOrder());
- indicesBuffer.put(indicies);
- indicesBuffer.position(0);
-
- textureId = createTexture();
-
- shaderProgram = createShaderProgram(VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE);
-
- // use the program
- GLES20.glUseProgram(shaderProgram);
-
- positionHandle = GLES20.glGetAttribLocation(shaderProgram, "a_position");
-
- texCoordHandle = GLES20.glGetAttribLocation(shaderProgram, "a_texCoord");
-
- samplerHandle = GLES20.glGetUniformLocation(shaderProgram, "s_texture");
-
-
- // load the vertex position
- GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);
- GLES20.glEnableVertexAttribArray(positionHandle);
- // load texture coordinate
- GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
- GLES20.glEnableVertexAttribArray(texCoordHandle);
-
-
- GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ private String readShaderResource(int resourceId) {
+ InputStream in = context.getResources().openRawResource(resourceId);
+ String code;
+ try {
+ code = IOUtils.toString(in, StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ code = "";
+ }
+ return code;
}
- protected void destroy(){
- GLES20.glDeleteProgram(shaderProgram);
+ protected void destroy() {
+ GLES20.glDeleteProgram(normalShaderProgram);
+ GLES20.glDeleteProgram(colorCorrectionShaderProgram);
int[] textures = {textureId};
GLES20.glDeleteTextures(1, textures, 0);
}
public void render() {
+ // use the shader program
+ GLES20.glUseProgram(currentShaderProgram);
+
// clear the color buffer
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
@@ -190,34 +262,4 @@ public class ScreenRenderer implements GLSurfaceView.Renderer {
GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, 6, GLES20.GL_UNSIGNED_BYTE, indicesBuffer);
}
}
-
- public void updateTexture(int[] frameBuffer) {
- this.texture.update(frameBuffer);
- }
-
- public void initTextureIfNotInitialized() {
- if (this.texture == null) {
- this.texture = new ScreenTexture();
- }
- }
-
- @Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- initTextureIfNotInitialized();
- ready = true;
- }
-
- @Override
- public void onSurfaceChanged(GL10 gl, int width, int height) {
- gl.glViewport(0, 0, width, height);
- }
-
- @Override
- public void onDrawFrame(GL10 gl) {
- this.texture.render();
- }
-
- public boolean isReady() {
- return ready;
- }
}
diff --git a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/ScreenView.java b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/ScreenView.java
index a4a9a86..6aa3ff0 100644
--- a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/ScreenView.java
+++ b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/ScreenView.java
@@ -1,10 +1,13 @@
package com.mrmichel.rustdroid_emu.ui;
import android.content.Context;
+import android.content.SharedPreferences;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
-public class ScreenView extends GLSurfaceView {
+import androidx.preference.PreferenceManager;
+
+public class ScreenView extends GLSurfaceView implements SharedPreferences.OnSharedPreferenceChangeListener {
private ScreenRenderer mRenderer;
public ScreenView(Context context) {
@@ -21,7 +24,11 @@ public class ScreenView extends GLSurfaceView {
this.setEGLContextClientVersion(2);
this.setPreserveEGLContextOnPause(true);
- mRenderer = new ScreenRenderer();
+ SharedPreferences sharedPreferences =
+ PreferenceManager.getDefaultSharedPreferences(getContext());
+ sharedPreferences.registerOnSharedPreferenceChangeListener(this);
+
+ mRenderer = new ScreenRenderer(getContext());
this.setRenderer(mRenderer);
this.setRenderMode(RENDERMODE_WHEN_DIRTY);
}
@@ -34,4 +41,12 @@ public class ScreenView extends GLSurfaceView {
public ScreenRenderer getRenderer() {
return mRenderer;
}
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (key.equals("color_correction")) {
+ boolean colorCorrection = sharedPreferences.getBoolean("color_correction", false);
+ mRenderer.setColorCorrection(colorCorrection);
+ }
+ }
}
diff --git a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/SettingsActivity.java b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/SettingsActivity.java
index 36df4e2..8223c37 100644
--- a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/SettingsActivity.java
+++ b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/SettingsActivity.java
@@ -2,18 +2,32 @@ package com.mrmichel.rustdroid_emu.ui;
import android.os.Bundle;
+import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
+import androidx.preference.PreferenceFragmentCompat;
import com.mrmichel.rustdroid_emu.R;
public class SettingsActivity extends AppCompatActivity {
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
getSupportFragmentManager()
.beginTransaction()
- .replace(R.id.settings_container, new SettingsFragment())
+ .replace(R.id.settings, new SettingsFragment())
.commit();
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
}
-}
+
+ public static class SettingsFragment extends PreferenceFragmentCompat {
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.app_preferences, rootKey);
+ }
+ }
+}
\ No newline at end of file
diff --git a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/library/RomListActivity.java b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/library/RomListActivity.java
index c78d319..b6edb45 100644
--- a/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/library/RomListActivity.java
+++ b/AndroidApp/app/src/main/java/com/mrmichel/rustdroid_emu/ui/library/RomListActivity.java
@@ -13,7 +13,6 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.webkit.MimeTypeMap;
import android.widget.AdapterView;
import android.widget.GridView;
diff --git a/AndroidApp/app/src/main/res/layout/activity_settings.xml b/AndroidApp/app/src/main/res/layout/activity_settings.xml
index a9f3452..50e64b2 100644
--- a/AndroidApp/app/src/main/res/layout/activity_settings.xml
+++ b/AndroidApp/app/src/main/res/layout/activity_settings.xml
@@ -1,13 +1,9 @@
-
+ android:layout_height="match_parent">
-
-
+ android:layout_height="match_parent" />
\ No newline at end of file
diff --git a/AndroidApp/app/src/main/res/raw/screen_texture_color_correction_fragment_shader.glsl b/AndroidApp/app/src/main/res/raw/screen_texture_color_correction_fragment_shader.glsl
new file mode 100644
index 0000000..2692b64
--- /dev/null
+++ b/AndroidApp/app/src/main/res/raw/screen_texture_color_correction_fragment_shader.glsl
@@ -0,0 +1,21 @@
+/*
+ Port of byuu's color correction shader as described in https://byuu.net/video/color-emulation
+ */
+
+precision mediump float;
+
+varying vec2 v_texCoord;
+uniform sampler2D s_texture;
+
+void main()
+{
+ float lcdGamma = 4.0, outGamma = 2.2;
+
+ vec4 color = texture2D( s_texture, v_texCoord );
+
+ color.rgb = pow(color.rgb, vec3(lcdGamma));
+ gl_FragColor.r = pow(( 0.0 * color.b + 50.0 * color.g + 255.0 * color.r) / 255.0, 1.0 / outGamma) * 255.0 / 280.0;
+ gl_FragColor.g = pow(( 30.0 * color.b + 230.0 * color.g + 10.0 * color.r) / 255.0, 1.0 / outGamma) * 255.0 / 280.0;
+ gl_FragColor.b = pow((220.0 * color.b + 10.0 * color.g + 50.0 * color.r) / 255.0, 1.0 / outGamma) * 255.0 / 280.0;
+ gl_FragColor.a = 1.0;
+}
\ No newline at end of file
diff --git a/AndroidApp/app/src/main/res/raw/screen_texture_fragment_shader.glsl b/AndroidApp/app/src/main/res/raw/screen_texture_fragment_shader.glsl
new file mode 100644
index 0000000..e77b894
--- /dev/null
+++ b/AndroidApp/app/src/main/res/raw/screen_texture_fragment_shader.glsl
@@ -0,0 +1,8 @@
+precision mediump float;
+varying vec2 v_texCoord;
+uniform sampler2D s_texture;
+void main()
+{
+ vec4 color = texture2D( s_texture, v_texCoord );
+ gl_FragColor = color;
+}
\ No newline at end of file
diff --git a/AndroidApp/app/src/main/res/raw/screen_texture_vertex_shader.glsl b/AndroidApp/app/src/main/res/raw/screen_texture_vertex_shader.glsl
new file mode 100644
index 0000000..77742ce
--- /dev/null
+++ b/AndroidApp/app/src/main/res/raw/screen_texture_vertex_shader.glsl
@@ -0,0 +1,8 @@
+attribute vec4 a_position;
+attribute vec2 a_texCoord;
+varying vec2 v_texCoord;
+void main()
+{
+ gl_Position = a_position;
+ v_texCoord = a_texCoord;
+}
\ No newline at end of file
diff --git a/AndroidApp/app/src/main/res/values/strings.xml b/AndroidApp/app/src/main/res/values/strings.xml
index 87b094b..8687a6b 100644
--- a/AndroidApp/app/src/main/res/values/strings.xml
+++ b/AndroidApp/app/src/main/res/values/strings.xml
@@ -16,4 +16,9 @@
Delete
Snapshot Manager
+
+ Apply color correction shader
+
+ Makes colors look closer to the real GBA LCD screen.
+
diff --git a/AndroidApp/app/src/main/res/xml/app_preferences.xml b/AndroidApp/app/src/main/res/xml/app_preferences.xml
index 26b0f1f..955e9d8 100644
--- a/AndroidApp/app/src/main/res/xml/app_preferences.xml
+++ b/AndroidApp/app/src/main/res/xml/app_preferences.xml
@@ -1,8 +1,18 @@
-
+
+
+
+
+
+
+
\ No newline at end of file