Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added app/debug/app-debug.apk
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
package com.lebaillyapp.dynamicvisualeffectsagsl

import android.os.Bundle
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import com.lebaillyapp.dynamicvisualeffectsagsl.navigation.AppNavHost
import com.lebaillyapp.dynamicvisualeffectsagsl.ui.theme.DynamicVisualEffectsAGSLTheme


class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Enable modern edge-to-edge support
enableEdgeToEdge()

// 1. Allow the window to use the full screen including the notch/cutout area
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

// 2. Use FLAG_LAYOUT_NO_LIMITS to allow the window to extend beyond standard screen decorations
// This ensures the visualisation fills the entire display without being constrained by system bar areas.
window.setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
)

// 3. Hide the system bars (Status Bar and Navigation Bar)
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())

setContent {
DynamicVisualEffectsAGSLTheme {
AppNavHost()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.toSize
import java.io.File
import kotlin.math.PI

@Composable
fun OptimizedHolographicCardEffect(
modifier: Modifier = Modifier,
bitmap: ImageBitmap,
@RawRes shaderResId: Int,
shaderName: String,

// === PARAMÈTRES D'ENTRÉE CONTROLLABLES EXTERNEMENT ===
hologramStrength: Float = 1.5f, // Opacité de l’effet holographique (sur les zones sombres)
Expand Down Expand Up @@ -96,11 +98,22 @@ fun OptimizedHolographicCardEffect(
}

// === LECTURE DU SHADER CODE ===
val shaderCode = remember {
context.resources.openRawResource(shaderResId)
.bufferedReader().use { it.readText() }
val file = remember(shaderName) { File(context.filesDir, "$shaderName.agsl") }
val shaderCode = remember(shaderName, file.lastModified()) {
if (file.exists()) {
file.readText()
} else {
context.resources.openRawResource(shaderResId).bufferedReader().use { it.readText() }
}
}
val shader = remember(shaderCode) {
try {
RuntimeShader(shaderCode)
} catch (_: Exception) {
val defaultCode = context.resources.openRawResource(shaderResId).bufferedReader().use { it.readText() }
RuntimeShader(defaultCode)
}
}
val shader = remember { RuntimeShader(shaderCode) }

// === MESURE DU COMPOSANT POUR SET uResolution ===
var composableSize by remember { mutableStateOf(Size.Zero) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.toSize
import kotlinx.coroutines.delay
import java.io.File
import kotlin.math.PI
import android.util.Log

Expand All @@ -39,6 +40,7 @@ fun UltraRealisticHolographicEffectShader(
modifier: Modifier = Modifier,
bitmap: ImageBitmap,
@RawRes shaderResId: Int,
shaderName: String,

// Effets principaux (simplifiés)
effectIntensity: Float = 0.8f,
Expand Down Expand Up @@ -126,10 +128,23 @@ fun UltraRealisticHolographicEffectShader(
}
}

val shaderCode = remember {
context.resources.openRawResource(shaderResId).bufferedReader().use { it.readText() }
// Load shader code: check internal storage first, then fallback to resources
val file = remember(shaderName) { File(context.filesDir, "$shaderName.agsl") }
val shaderCode = remember(shaderName, file.lastModified()) {
if (file.exists()) {
file.readText()
} else {
context.resources.openRawResource(shaderResId).bufferedReader().use { it.readText() }
}
}
val shader = remember(shaderCode) {
try {
RuntimeShader(shaderCode)
} catch (_: Exception) {
val defaultCode = context.resources.openRawResource(shaderResId).bufferedReader().use { it.readText() }
RuntimeShader(defaultCode)
}
}
val shader = remember { RuntimeShader(shaderCode) }

var composableSize by remember { mutableStateOf(Size.Zero) }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,53 @@
package com.lebaillyapp.dynamicvisualeffectsagsl.navigation

import android.app.ActivityManager
import android.content.Context
import android.graphics.BitmapFactory
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.lebaillyapp.dynamicvisualeffectsagsl.R
import com.lebaillyapp.dynamicvisualeffectsagsl.holographicEffect.composition.HolographicEffectBitmapShader
import com.lebaillyapp.dynamicvisualeffectsagsl.navigation.ShaderEditorScreen
import com.lebaillyapp.dynamicvisualeffectsagsl.holographicEffect.composition.OptimizedHolographicCardEffect
import com.lebaillyapp.dynamicvisualeffectsagsl.holographicEffect.composition.UltraRealisticHolographicEffectShader
import com.lebaillyapp.dynamicvisualeffectsagsl.topographicflowEffect.TopographicFlowShader
import com.lebaillyapp.dynamicvisualeffectsagsl.topographicflowEffect.TopographicFlowShaderWithControls
import com.lebaillyapp.dynamicvisualeffectsagsl.waterEffect.composition.WaterEffectBitmapShader

@Composable
fun AppNavHost() {
val navController = rememberNavController()
val context = LocalContext.current

var userSelectedBitmap by remember { mutableStateOf<ImageBitmap?>(null) }
val pickMedia = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
val inputStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(inputStream)
userSelectedBitmap = bitmap?.asImageBitmap()
}
}

NavHost(
navController = navController,
Expand All @@ -29,65 +56,120 @@ fun AppNavHost() {

composable("menu") {
EffectsMenu(
onSelect = { route -> navController.navigate(route) }
onSelect = { route -> navController.navigate(route) },
onEdit = { shaderName -> navController.navigate("editor/$shaderName") }
)
}

composable("water") {
val bitmap = ImageBitmap.imageResource(R.drawable.demopic_e)
WaterEffectBitmapShader(
modifier = Modifier.fillMaxSize().background(Color.Black),
bitmap = bitmap,
shaderResId = R.raw.water_shader
composable(
"editor/{shaderName}",
arguments = listOf(navArgument("shaderName") { type = NavType.StringType })
) { backStackEntry ->
val shaderName = backStackEntry.arguments?.getString("shaderName") ?: ""
ShaderEditorScreen(
shaderName = shaderName,
onBack = { navController.popBackStack() }
)
}

composable("holo_base") {
val bitmap = ImageBitmap.imageResource(R.drawable.demopic_d)
OptimizedHolographicCardEffect(
modifier = Modifier.fillMaxSize(),
bitmap = bitmap,
shaderResId = R.raw.holographic_rainbow,
microDetailScale = 2f // more = unzoom , less = zoom (on the effect...)

composable("water") {
val defaultBitmap = ImageBitmap.imageResource(R.drawable.demopic_e)
EffectContainer(onPickImage = { pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) }) {
WaterEffectBitmapShader(
modifier = Modifier.fillMaxSize().background(Color.Black),
bitmap = userSelectedBitmap ?: defaultBitmap,
shaderResId = R.raw.water_shader,
shaderName = "water_shader"
)
}
}

)
composable("holo_base") {
val defaultBitmap = ImageBitmap.imageResource(R.drawable.demopic_d)
EffectContainer(onPickImage = { pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) }) {
OptimizedHolographicCardEffect(
modifier = Modifier.fillMaxSize(),
bitmap = userSelectedBitmap ?: defaultBitmap,
shaderResId = R.raw.holographic_rainbow,
shaderName = "holographic_rainbow",
microDetailScale = 2f
)
}
}

composable("holo_Iridescent") {
val bitmap = ImageBitmap.imageResource(R.drawable.demopic_e)
UltraRealisticHolographicEffectShader(
modifier = Modifier.fillMaxSize(),
bitmap = bitmap,
shaderResId = R.raw.holographic_realistic_shader,
effectIntensity = 6.8f,
fresnelPower = 6.0f,
rainbowScale = 1.2f,
rainbowOffset = 0.2f,
normalStrength = 2.0f,
microDetailScale = 45.0f
)
val defaultBitmap = ImageBitmap.imageResource(R.drawable.demopic_e)
EffectContainer(onPickImage = { pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) }) {
UltraRealisticHolographicEffectShader(
modifier = Modifier.fillMaxSize(),
bitmap = userSelectedBitmap ?: defaultBitmap,
shaderResId = R.raw.holographic_realistic_shader,
shaderName = "holographic_realistic_shader",
effectIntensity = 6.8f,
fresnelPower = 6.0f,
rainbowScale = 1.2f,
rainbowOffset = 0.2f,
normalStrength = 2.0f,
microDetailScale = 45.0f
)
}
}

composable("holo_card") {
val bitmap = ImageBitmap.imageResource(R.drawable.de2)
OptimizedHolographicCardEffect(
modifier = Modifier.fillMaxSize(),
bitmap = bitmap,
shaderResId = R.raw.holographic_card_shader
)
val defaultBitmap = ImageBitmap.imageResource(R.drawable.de2)
EffectContainer(onPickImage = { pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) }) {
OptimizedHolographicCardEffect(
modifier = Modifier.fillMaxSize(),
bitmap = userSelectedBitmap ?: defaultBitmap,
shaderResId = R.raw.holographic_card_shader,
shaderName = "holographic_card_shader"
)
}
}

composable("topo") {
TopographicFlowShader(modifier = Modifier.fillMaxSize())
TopographicFlowShader(
modifier = Modifier.fillMaxSize(),
shaderResId = R.raw.topographicflow_shader,
shaderName = "topographicflow_shader"
)
}

composable("topo_controls") {
TopographicFlowShaderWithControls(modifier = Modifier.fillMaxSize())
TopographicFlowShaderWithControls(
modifier = Modifier.fillMaxSize(),
shaderResId = R.raw.topographicflow_shader,
shaderName = "topographicflow_shader"
)
}

composable("fire") {
//later !
//later !
}
}
}

@Composable
fun EffectContainer(onPickImage: () -> Unit, content: @Composable () -> Unit) {
val context = LocalContext.current
val isPinned = remember {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.lockTaskModeState != ActivityManager.LOCK_TASK_MODE_NONE
}

Box(modifier = Modifier.fillMaxSize()) {
content()
if (!isPinned) {
FloatingActionButton(
onClick = onPickImage,
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(32.dp),
containerColor = Color.Black.copy(alpha = 0.6f),
contentColor = Color.White
) {
Icon(Icons.Default.Star, contentDescription = "Pick Image")
}
}
}
}
Loading