android: Add color correction shader setting
Former-commit-id: 894a0d6d6a07b16d3333739d565bba853ce13fc4
This commit is contained in:
parent
2aa0eec95e
commit
3db61d1804
|
@ -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'
|
||||
|
|
|
@ -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">
|
||||
<activity android:name=".ui.snapshots.SnapshotPickerActivity"></activity>
|
||||
<activity android:name=".ui.snapshots.SnapshotPickerActivity" />
|
||||
<activity
|
||||
android:name=".ui.snapshots.SnapshotListFragment"
|
||||
android:label="@string/title_activity_snapshot" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/settings_container"
|
||||
android:id="@+id/settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
</FrameLayout>
|
||||
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -16,4 +16,9 @@
|
|||
<string name="action_delete">Delete</string>
|
||||
|
||||
<string name="title_activity_snapshot">Snapshot Manager</string>
|
||||
|
||||
<string name="color_correction_setting">Apply color correction shader</string>
|
||||
<string name="color_correction_summary">
|
||||
Makes colors look closer to the real GBA LCD screen.
|
||||
</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
<PreferenceScreen
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<CheckBoxPreference
|
||||
app:key="skip_bios"
|
||||
app:title="Skip bios boot animation"/>
|
||||
<PreferenceCategory app:title="Emulation">
|
||||
<SwitchPreferenceCompat
|
||||
app:key="skip_bios"
|
||||
app:title="Skip bios boot animation"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="Graphics">
|
||||
<SwitchPreferenceCompat
|
||||
app:key="color_correction"
|
||||
app:title="@string/color_correction_setting"
|
||||
app:summary="@string/color_correction_summary"
|
||||
/>
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
Reference in a new issue