mirror of
https://github.com/T8RIN/ImageToolbox.git
synced 2025-05-17 21:45:59 +08:00
Added Noise generator by #948
This commit is contained in:
@ -21,7 +21,7 @@ import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.tech.imageresizershrinker.core.data.dispatchers.DispatchersHolderImpl
|
||||
import ru.tech.imageresizershrinker.core.data.dispatchers.AndroidDispatchersHolder
|
||||
import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -32,7 +32,7 @@ internal interface DispatchersModule {
|
||||
@Binds
|
||||
@Singleton
|
||||
fun dispatchersHolder(
|
||||
dispatchers: DispatchersHolderImpl
|
||||
dispatchers: AndroidDispatchersHolder
|
||||
): DispatchersHolder
|
||||
|
||||
}
|
@ -26,7 +26,7 @@ import ru.tech.imageresizershrinker.core.di.UiDispatcher
|
||||
import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder
|
||||
import javax.inject.Inject
|
||||
|
||||
internal data class DispatchersHolderImpl @Inject constructor(
|
||||
internal data class AndroidDispatchersHolder @Inject constructor(
|
||||
@UiDispatcher override val uiDispatcher: CoroutineDispatcher,
|
||||
@IoDispatcher override val ioDispatcher: CoroutineDispatcher,
|
||||
@EncodingDispatcher override val encodingDispatcher: CoroutineDispatcher,
|
@ -31,6 +31,11 @@ data class IntegerSize(
|
||||
value
|
||||
}.getOrNull() ?: 1f
|
||||
|
||||
val safeAspectRatio: Float
|
||||
get() = aspectRatio
|
||||
.coerceAtLeast(0.005f)
|
||||
.coerceAtMost(1000f)
|
||||
|
||||
operator fun times(i: Float): IntegerSize = IntegerSize(
|
||||
width = (width * i).toInt(),
|
||||
height = (height * i).toInt()
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.core.filters.domain.model
|
||||
package ru.tech.imageresizershrinker.core.domain.utils
|
||||
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
@ -17,10 +17,10 @@
|
||||
|
||||
package ru.tech.imageresizershrinker.core.filters.presentation.model
|
||||
|
||||
import ru.tech.imageresizershrinker.core.domain.utils.NEAREST_ODD_ROUNDING
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.Filter
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.FilterParam
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.LinearGaussianParams
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.NEAREST_ODD_ROUNDING
|
||||
import ru.tech.imageresizershrinker.core.resources.R
|
||||
|
||||
class UiLinearGaussianBlurFilter(
|
||||
|
@ -17,9 +17,9 @@
|
||||
|
||||
package ru.tech.imageresizershrinker.core.filters.presentation.model
|
||||
|
||||
import ru.tech.imageresizershrinker.core.domain.utils.NEAREST_ODD_ROUNDING
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.Filter
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.FilterParam
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.NEAREST_ODD_ROUNDING
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.TransferFunc
|
||||
import ru.tech.imageresizershrinker.core.resources.R
|
||||
|
||||
|
@ -17,9 +17,9 @@
|
||||
|
||||
package ru.tech.imageresizershrinker.core.filters.presentation.model
|
||||
|
||||
import ru.tech.imageresizershrinker.core.domain.utils.NEAREST_ODD_ROUNDING
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.Filter
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.FilterParam
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.NEAREST_ODD_ROUNDING
|
||||
import ru.tech.imageresizershrinker.core.resources.R
|
||||
|
||||
class UiTentBlurFilter(
|
||||
|
@ -49,6 +49,7 @@ import androidx.compose.ui.unit.dp
|
||||
import ru.tech.imageresizershrinker.core.domain.model.ColorModel
|
||||
import ru.tech.imageresizershrinker.core.domain.model.FileModel
|
||||
import ru.tech.imageresizershrinker.core.domain.model.ImageModel
|
||||
import ru.tech.imageresizershrinker.core.domain.utils.roundTo
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.BlurEdgeMode
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.BokehParams
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.ClaheParams
|
||||
@ -65,7 +66,6 @@ import ru.tech.imageresizershrinker.core.filters.domain.model.RadialTiltShiftPar
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.SideFadeParams
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.TransferFunc
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.WaterParams
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.roundTo
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.wrap
|
||||
import ru.tech.imageresizershrinker.core.filters.presentation.model.UiColorFilter
|
||||
import ru.tech.imageresizershrinker.core.filters.presentation.model.UiFilter
|
||||
|
@ -1387,4 +1387,19 @@
|
||||
<string name="show_all">Show All</string>
|
||||
<string name="hide_nav_bar">Hide Nav Bar</string>
|
||||
<string name="hide_status_bar">Hide Status Bar</string>
|
||||
<string name="noise_generation">Noise Generation</string>
|
||||
<string name="noise_generation_sub">Generate different noises like Perlin or other types</string>
|
||||
<string name="frequency">Frequency</string>
|
||||
<string name="noise_type">Noise Type</string>
|
||||
<string name="rotation_type">Rotation Type</string>
|
||||
<string name="fractal_type">Fractal Type</string>
|
||||
<string name="octaves">Octaves</string>
|
||||
<string name="lacunarity">Lacunarity</string>
|
||||
<string name="gain">Gain</string>
|
||||
<string name="weighted_strength">Weighted Strength</string>
|
||||
<string name="ping_pong_strength">Ping Pong Strength</string>
|
||||
<string name="distance_function">Distance Function</string>
|
||||
<string name="return_type">Return Type</string>
|
||||
<string name="jitter">Jitter</string>
|
||||
<string name="domain_warp">Domain Warp</string>
|
||||
</resources>
|
@ -36,6 +36,7 @@ import androidx.compose.material.icons.outlined.FilterHdr
|
||||
import androidx.compose.material.icons.outlined.FolderZip
|
||||
import androidx.compose.material.icons.outlined.GifBox
|
||||
import androidx.compose.material.icons.outlined.Gradient
|
||||
import androidx.compose.material.icons.outlined.Grain
|
||||
import androidx.compose.material.icons.outlined.Photo
|
||||
import androidx.compose.material.icons.outlined.PictureAsPdf
|
||||
import androidx.compose.material.icons.outlined.QrCode
|
||||
@ -116,6 +117,7 @@ sealed class Screen(
|
||||
is ImageSplitting -> "Image_Splitting"
|
||||
is ColorTools -> "Color_Tools"
|
||||
is WebpTools -> "WEBP_Tools"
|
||||
is NoiseGeneration -> "Noise_Generation"
|
||||
}
|
||||
|
||||
val icon: ImageVector?
|
||||
@ -156,6 +158,7 @@ sealed class Screen(
|
||||
is ImageSplitting -> Icons.Outlined.ContentCut
|
||||
ColorTools -> Icons.Outlined.ColorLens
|
||||
is WebpTools -> Icons.Rounded.WebpBox
|
||||
NoiseGeneration -> Icons.Outlined.Grain
|
||||
}
|
||||
|
||||
data object Settings : Screen(
|
||||
@ -698,6 +701,12 @@ sealed class Screen(
|
||||
}
|
||||
}
|
||||
|
||||
data object NoiseGeneration : Screen(
|
||||
id = 32,
|
||||
title = R.string.noise_generation,
|
||||
subtitle = R.string.noise_generation_sub
|
||||
)
|
||||
|
||||
companion object {
|
||||
val typedEntries by lazy {
|
||||
listOf(
|
||||
@ -750,6 +759,7 @@ sealed class Screen(
|
||||
JxlTools(),
|
||||
ApngTools(),
|
||||
Cipher(),
|
||||
NoiseGeneration,
|
||||
Zip(),
|
||||
WebpTools()
|
||||
) to Triple(
|
||||
@ -763,6 +773,6 @@ sealed class Screen(
|
||||
typedEntries.flatMap { it.first }.sortedBy { it.id }
|
||||
}
|
||||
|
||||
const val FEATURES_COUNT = 51
|
||||
const val FEATURES_COUNT = 52
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ import ru.tech.imageresizershrinker.feature.filters.di.FilterInteractorDataStore
|
||||
import javax.inject.Inject
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
internal class FavoriteFiltersInteractorImpl @Inject constructor(
|
||||
internal class AndroidFavoriteFiltersInteractor @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
@FilterInteractorDataStore private val dataStore: DataStore<Preferences>,
|
||||
private val fileController: FileController
|
@ -21,10 +21,10 @@ import android.graphics.Bitmap
|
||||
import com.awxkee.aire.Aire
|
||||
import ru.tech.imageresizershrinker.core.domain.model.IntegerSize
|
||||
import ru.tech.imageresizershrinker.core.domain.transformation.Transformation
|
||||
import ru.tech.imageresizershrinker.core.domain.utils.NEAREST_ODD_ROUNDING
|
||||
import ru.tech.imageresizershrinker.core.domain.utils.roundTo
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.Filter
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.LinearGaussianParams
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.NEAREST_ODD_ROUNDING
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.model.roundTo
|
||||
import ru.tech.imageresizershrinker.feature.filters.data.utils.toEdgeMode
|
||||
import ru.tech.imageresizershrinker.feature.filters.data.utils.toFunc
|
||||
|
||||
|
@ -33,9 +33,9 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.FavoriteFiltersInteractor
|
||||
import ru.tech.imageresizershrinker.core.filters.domain.FilterProvider
|
||||
import ru.tech.imageresizershrinker.feature.filters.data.AndroidFavoriteFiltersInteractor
|
||||
import ru.tech.imageresizershrinker.feature.filters.data.AndroidFilterMaskApplier
|
||||
import ru.tech.imageresizershrinker.feature.filters.data.AndroidFilterProvider
|
||||
import ru.tech.imageresizershrinker.feature.filters.data.FavoriteFiltersInteractorImpl
|
||||
import ru.tech.imageresizershrinker.feature.filters.domain.FilterMaskApplier
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -72,7 +72,7 @@ internal interface FilterModule {
|
||||
@Singleton
|
||||
@Binds
|
||||
fun favoriteFiltersInteractor(
|
||||
interactor: FavoriteFiltersInteractorImpl
|
||||
interactor: AndroidFavoriteFiltersInteractor
|
||||
): FavoriteFiltersInteractor
|
||||
|
||||
}
|
1
feature/noise-generation/.gitignore
vendored
Normal file
1
feature/noise-generation/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
8
feature/noise-generation/build.gradle.kts
Normal file
8
feature/noise-generation/build.gradle.kts
Normal file
@ -0,0 +1,8 @@
|
||||
plugins {
|
||||
alias(libs.plugins.image.toolbox.library)
|
||||
alias(libs.plugins.image.toolbox.feature)
|
||||
alias(libs.plugins.image.toolbox.hilt)
|
||||
alias(libs.plugins.image.toolbox.compose)
|
||||
}
|
||||
|
||||
android.namespace = "ru.tech.imageresizershrinker.feature.noise_generation"
|
2
feature/noise-generation/src/main/AndroidManifest.xml
Normal file
2
feature/noise-generation/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest />
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.data
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.lerp
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.NoiseGenerator
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.NoiseParams
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class AndroidNoiseGenerator @Inject constructor(
|
||||
dispatchersHolder: DispatchersHolder
|
||||
) : NoiseGenerator<Bitmap>, DispatchersHolder by dispatchersHolder {
|
||||
|
||||
override suspend fun generateNoise(
|
||||
width: Int,
|
||||
height: Int,
|
||||
noiseParams: NoiseParams,
|
||||
onFailure: (Throwable) -> Unit
|
||||
): Bitmap? = withContext(defaultDispatcher) {
|
||||
val generator = with(noiseParams) {
|
||||
FastNoiseLite().apply {
|
||||
setSeed(seed)
|
||||
setFrequency(frequency)
|
||||
setNoiseType(noiseType)
|
||||
setRotationType3D(rotationType3D)
|
||||
setFractalType(fractalType)
|
||||
setFractalOctaves(fractalOctaves)
|
||||
setFractalLacunarity(fractalLacunarity)
|
||||
setFractalGain(fractalGain)
|
||||
setFractalWeightedStrength(fractalWeightedStrength)
|
||||
setFractalPingPongStrength(fractalPingPongStrength)
|
||||
setCellularDistanceFunction(cellularDistanceFunction)
|
||||
setCellularReturnType(cellularReturnType)
|
||||
setCellularJitter(cellularJitter)
|
||||
setDomainWarpType(domainWarpType)
|
||||
setDomainWarpAmp(domainWarpAmp)
|
||||
}
|
||||
}
|
||||
|
||||
runCatching {
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
|
||||
for (x in 0..<width) {
|
||||
for (y in 0..<height) {
|
||||
val noise = generator.getNoise(x.toFloat(), y.toFloat()) + 1f
|
||||
val color = lerp(Color.Black, Color.White, noise / 2f)
|
||||
bitmap.setPixel(x, y, color.toArgb())
|
||||
}
|
||||
}
|
||||
|
||||
bitmap
|
||||
}.onFailure(onFailure).getOrNull()
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.di
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.tech.imageresizershrinker.noise_generation.data.AndroidNoiseGenerator
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.NoiseGenerator
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal interface NoiseGenerationModule {
|
||||
|
||||
@Binds
|
||||
fun provideGenerator(
|
||||
impl: AndroidNoiseGenerator
|
||||
): NoiseGenerator<Bitmap>
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.domain
|
||||
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.NoiseParams
|
||||
|
||||
interface NoiseGenerator<Image> {
|
||||
|
||||
suspend fun generateNoise(
|
||||
width: Int,
|
||||
height: Int,
|
||||
noiseParams: NoiseParams,
|
||||
onFailure: (Throwable) -> Unit = {}
|
||||
): Image?
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.domain.model
|
||||
|
||||
enum class CellularDistanceFunction {
|
||||
Euclidean,
|
||||
EuclideanSq,
|
||||
Manhattan,
|
||||
Hybrid
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.domain.model
|
||||
|
||||
enum class CellularReturnType {
|
||||
CellValue,
|
||||
Distance,
|
||||
Distance2,
|
||||
Distance2Add,
|
||||
Distance2Sub,
|
||||
Distance2Mul,
|
||||
Distance2Div
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.domain.model
|
||||
|
||||
enum class DomainWarpType {
|
||||
OpenSimplex2,
|
||||
OpenSimplex2Reduced,
|
||||
BasicGrid
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.domain.model
|
||||
|
||||
enum class FractalType {
|
||||
None,
|
||||
FBm,
|
||||
Ridged,
|
||||
PingPong,
|
||||
DomainWarpProgressive,
|
||||
DomainWarpIndependent
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.domain.model
|
||||
|
||||
data class NoiseParams(
|
||||
val seed: Int,
|
||||
val frequency: Float,
|
||||
val noiseType: NoiseType,
|
||||
val rotationType3D: RotationType3D,
|
||||
val fractalType: FractalType,
|
||||
val fractalOctaves: Int,
|
||||
val fractalLacunarity: Float,
|
||||
val fractalGain: Float,
|
||||
val fractalWeightedStrength: Float,
|
||||
val fractalPingPongStrength: Float,
|
||||
val cellularDistanceFunction: CellularDistanceFunction,
|
||||
val cellularReturnType: CellularReturnType,
|
||||
val cellularJitter: Float,
|
||||
val domainWarpType: DomainWarpType,
|
||||
val domainWarpAmp: Float
|
||||
) {
|
||||
companion object {
|
||||
val Default by lazy {
|
||||
NoiseParams(
|
||||
seed = 1337,
|
||||
frequency = 0.01f,
|
||||
noiseType = NoiseType.OpenSimplex2,
|
||||
rotationType3D = RotationType3D.None,
|
||||
fractalType = FractalType.None,
|
||||
fractalOctaves = 3,
|
||||
fractalLacunarity = 2f,
|
||||
fractalGain = 0.5f,
|
||||
fractalWeightedStrength = 0f,
|
||||
fractalPingPongStrength = 2f,
|
||||
cellularDistanceFunction = CellularDistanceFunction.EuclideanSq,
|
||||
cellularReturnType = CellularReturnType.Distance,
|
||||
cellularJitter = 1f,
|
||||
domainWarpType = DomainWarpType.OpenSimplex2,
|
||||
domainWarpAmp = 1f
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.domain.model
|
||||
|
||||
enum class NoiseType {
|
||||
OpenSimplex2,
|
||||
OpenSimplex2S,
|
||||
Cellular,
|
||||
Perlin,
|
||||
ValueCubic,
|
||||
Value
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.domain.model
|
||||
|
||||
enum class RotationType3D {
|
||||
None,
|
||||
ImproveXYPlanes,
|
||||
ImproveXZPlanes
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.presentation
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.olshevski.navigation.reimagined.hilt.hiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.tech.imageresizershrinker.core.domain.image.model.ImageInfo
|
||||
import ru.tech.imageresizershrinker.core.resources.R
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.confetti.LocalConfettiHostState
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.helper.asClip
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.helper.isPortraitOrientationAsState
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.helper.parseSaveResult
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.navigation.Screen
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.state.derivedValueOf
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.AdaptiveLayoutScreen
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.buttons.BottomButtonsBlock
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.buttons.ShareButton
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.controls.ResizeImageField
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.controls.selection.ImageFormatSelector
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.controls.selection.QualitySelector
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.dialogs.OneTimeSaveLocationSelectionDialog
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.image.Picture
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.modifier.container
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.other.Loading
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.other.LoadingDialog
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.other.LocalToastHostState
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.other.TopAppBarEmoji
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.sheets.ProcessImagesPreferenceSheet
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.text.marquee
|
||||
import ru.tech.imageresizershrinker.noise_generation.presentation.components.NoiseParamsSelection
|
||||
import ru.tech.imageresizershrinker.noise_generation.presentation.viewModel.NoiseGenerationViewModel
|
||||
|
||||
@Composable
|
||||
fun NoiseGenerationContent(
|
||||
onGoBack: () -> Unit,
|
||||
onNavigate: (Screen) -> Unit,
|
||||
viewModel: NoiseGenerationViewModel = hiltViewModel()
|
||||
) {
|
||||
val toastHostState = LocalToastHostState.current
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val confettiHostState = LocalConfettiHostState.current
|
||||
|
||||
val showConfetti: () -> Unit = {
|
||||
scope.launch {
|
||||
confettiHostState.showConfetti()
|
||||
}
|
||||
}
|
||||
|
||||
val isPortrait by isPortraitOrientationAsState()
|
||||
|
||||
val saveBitmap: (oneTimeSaveLocationUri: String?) -> Unit = {
|
||||
viewModel.saveNoise(it) { saveResult ->
|
||||
context.parseSaveResult(
|
||||
saveResult = saveResult,
|
||||
onSuccess = showConfetti,
|
||||
toastHostState = toastHostState,
|
||||
scope = scope
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AdaptiveLayoutScreen(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.noise_generation),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.marquee()
|
||||
)
|
||||
},
|
||||
onGoBack = onGoBack,
|
||||
actions = {
|
||||
var editSheetData by remember {
|
||||
mutableStateOf(listOf<Uri>())
|
||||
}
|
||||
ShareButton(
|
||||
onShare = {
|
||||
viewModel.shareNoise(showConfetti)
|
||||
},
|
||||
onCopy = { manager ->
|
||||
viewModel.cacheCurrentNoise { uri ->
|
||||
manager.setClip(uri.asClip(context))
|
||||
showConfetti()
|
||||
}
|
||||
},
|
||||
onEdit = {
|
||||
viewModel.cacheCurrentNoise {
|
||||
editSheetData = listOf(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
ProcessImagesPreferenceSheet(
|
||||
uris = editSheetData,
|
||||
visible = editSheetData.isNotEmpty(),
|
||||
onDismiss = {
|
||||
if (!it) {
|
||||
editSheetData = emptyList()
|
||||
}
|
||||
},
|
||||
onNavigate = { screen ->
|
||||
scope.launch {
|
||||
editSheetData = emptyList()
|
||||
delay(200)
|
||||
onNavigate(screen)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
topAppBarPersistentActions = {
|
||||
TopAppBarEmoji()
|
||||
},
|
||||
imagePreview = {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Picture(
|
||||
model = viewModel.previewBitmap,
|
||||
modifier = Modifier
|
||||
.container(MaterialTheme.shapes.medium)
|
||||
.aspectRatio(viewModel.noiseSize.safeAspectRatio),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
contentScale = ContentScale.FillBounds
|
||||
)
|
||||
if (viewModel.isImageLoading) Loading()
|
||||
}
|
||||
},
|
||||
controls = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
ResizeImageField(
|
||||
imageInfo = derivedValueOf(viewModel.noiseSize) {
|
||||
ImageInfo(viewModel.noiseSize.width, viewModel.noiseSize.height)
|
||||
},
|
||||
originalSize = null,
|
||||
onWidthChange = viewModel::setNoiseWidth,
|
||||
onHeightChange = viewModel::setNoiseHeight
|
||||
)
|
||||
NoiseParamsSelection(
|
||||
value = viewModel.noiseParams,
|
||||
onValueChange = viewModel::updateParams
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
ImageFormatSelector(
|
||||
value = viewModel.imageFormat,
|
||||
onValueChange = viewModel::setImageFormat
|
||||
)
|
||||
QualitySelector(
|
||||
quality = viewModel.quality,
|
||||
imageFormat = viewModel.imageFormat,
|
||||
onQualityChange = viewModel::setQuality
|
||||
)
|
||||
}
|
||||
},
|
||||
buttons = {
|
||||
var showFolderSelectionDialog by rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
BottomButtonsBlock(
|
||||
targetState = false to isPortrait,
|
||||
isSecondaryButtonVisible = false,
|
||||
onSecondaryButtonClick = {},
|
||||
onPrimaryButtonClick = {
|
||||
saveBitmap(null)
|
||||
},
|
||||
onPrimaryButtonLongClick = {
|
||||
showFolderSelectionDialog = true
|
||||
},
|
||||
actions = it
|
||||
)
|
||||
if (showFolderSelectionDialog) {
|
||||
OneTimeSaveLocationSelectionDialog(
|
||||
onDismiss = { showFolderSelectionDialog = false },
|
||||
onSaveRequest = saveBitmap
|
||||
)
|
||||
}
|
||||
},
|
||||
canShowScreenData = true,
|
||||
isPortrait = isPortrait
|
||||
)
|
||||
|
||||
if (viewModel.isSaving) {
|
||||
LoadingDialog(onCancelLoading = viewModel::cancelSaving)
|
||||
}
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.presentation.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.RampLeft
|
||||
import androidx.compose.material.icons.outlined.SettingsEthernet
|
||||
import androidx.compose.material.icons.outlined.Waves
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.smarttoolfactory.extendedcolors.util.roundToTwoDigits
|
||||
import ru.tech.imageresizershrinker.core.domain.utils.roundTo
|
||||
import ru.tech.imageresizershrinker.core.resources.R
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.controls.EnhancedSliderItem
|
||||
import ru.tech.imageresizershrinker.core.ui.widget.controls.selection.DataSelector
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.CellularDistanceFunction
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.CellularReturnType
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.DomainWarpType
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.FractalType
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.NoiseParams
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.NoiseType
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun NoiseParamsSelection(
|
||||
value: NoiseParams,
|
||||
onValueChange: (NoiseParams) -> Unit
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
EnhancedSliderItem(
|
||||
value = value.seed,
|
||||
icon = Icons.Outlined.SettingsEthernet,
|
||||
title = stringResource(R.string.seed),
|
||||
valueRange = -10000f..10000f,
|
||||
internalStateTransformation = {
|
||||
it.roundToInt()
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(seed = it.toInt()))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
EnhancedSliderItem(
|
||||
value = value.frequency,
|
||||
icon = Icons.Outlined.Waves,
|
||||
title = stringResource(R.string.frequency),
|
||||
valueRange = -0.5f..0.5f,
|
||||
internalStateTransformation = {
|
||||
it.roundTo(3)
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(frequency = it))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
DataSelector(
|
||||
value = value.noiseType,
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(noiseType = it))
|
||||
},
|
||||
entries = NoiseType.entries,
|
||||
title = stringResource(R.string.noise_type),
|
||||
titleIcon = null,
|
||||
itemContentText = {
|
||||
it.name
|
||||
},
|
||||
spanCount = 2,
|
||||
color = Color.Unspecified
|
||||
)
|
||||
DataSelector(
|
||||
value = value.fractalType,
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(fractalType = it))
|
||||
},
|
||||
entries = FractalType.entries,
|
||||
title = stringResource(R.string.fractal_type),
|
||||
titleIcon = null,
|
||||
itemContentText = {
|
||||
it.name
|
||||
},
|
||||
spanCount = 2,
|
||||
color = Color.Unspecified
|
||||
)
|
||||
AnimatedVisibility(value.fractalType != FractalType.None) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
EnhancedSliderItem(
|
||||
value = value.fractalOctaves,
|
||||
title = stringResource(R.string.octaves),
|
||||
valueRange = 1f..5f,
|
||||
steps = 3,
|
||||
internalStateTransformation = {
|
||||
it.roundToInt()
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(fractalOctaves = it.toInt()))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
EnhancedSliderItem(
|
||||
value = value.fractalLacunarity,
|
||||
title = stringResource(R.string.lacunarity),
|
||||
valueRange = -50f..50f,
|
||||
internalStateTransformation = {
|
||||
it.roundToTwoDigits()
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(fractalLacunarity = it))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
EnhancedSliderItem(
|
||||
value = value.fractalGain,
|
||||
title = stringResource(R.string.gain),
|
||||
valueRange = -10f..10f,
|
||||
internalStateTransformation = {
|
||||
it.roundToTwoDigits()
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(fractalGain = it))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
EnhancedSliderItem(
|
||||
value = value.fractalWeightedStrength,
|
||||
title = stringResource(R.string.weighted_strength),
|
||||
valueRange = -3f..3f,
|
||||
internalStateTransformation = {
|
||||
it.roundToTwoDigits()
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(fractalWeightedStrength = it))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
AnimatedVisibility(value.fractalType == FractalType.PingPong) {
|
||||
EnhancedSliderItem(
|
||||
value = value.fractalPingPongStrength,
|
||||
title = stringResource(R.string.ping_pong_strength),
|
||||
valueRange = 0f..20f,
|
||||
internalStateTransformation = {
|
||||
it.roundToTwoDigits()
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(fractalPingPongStrength = it))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(value.noiseType == NoiseType.Cellular) {
|
||||
Column {
|
||||
DataSelector(
|
||||
value = value.cellularDistanceFunction,
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(cellularDistanceFunction = it))
|
||||
},
|
||||
entries = CellularDistanceFunction.entries,
|
||||
title = stringResource(R.string.distance_function),
|
||||
titleIcon = null,
|
||||
itemContentText = {
|
||||
it.name
|
||||
},
|
||||
spanCount = 2,
|
||||
color = Color.Unspecified
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
DataSelector(
|
||||
value = value.cellularReturnType,
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(cellularReturnType = it))
|
||||
},
|
||||
entries = CellularReturnType.entries,
|
||||
title = stringResource(R.string.return_type),
|
||||
titleIcon = null,
|
||||
itemContentText = {
|
||||
it.name
|
||||
},
|
||||
spanCount = 2,
|
||||
color = Color.Unspecified
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
EnhancedSliderItem(
|
||||
value = value.cellularJitter,
|
||||
title = stringResource(R.string.jitter),
|
||||
valueRange = -10f..10f,
|
||||
internalStateTransformation = {
|
||||
it.roundToTwoDigits()
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(cellularJitter = it))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DataSelector(
|
||||
value = value.domainWarpType,
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(domainWarpType = it))
|
||||
},
|
||||
entries = DomainWarpType.entries,
|
||||
title = stringResource(R.string.domain_warp),
|
||||
titleIcon = null,
|
||||
itemContentText = {
|
||||
it.name
|
||||
},
|
||||
spanCount = 2,
|
||||
color = Color.Unspecified
|
||||
)
|
||||
EnhancedSliderItem(
|
||||
value = value.domainWarpAmp,
|
||||
icon = Icons.Outlined.RampLeft,
|
||||
title = stringResource(R.string.amplitude),
|
||||
valueRange = -2000f..2000f,
|
||||
internalStateTransformation = {
|
||||
it.roundToTwoDigits()
|
||||
},
|
||||
onValueChange = {
|
||||
onValueChange(value.copy(domainWarpAmp = it))
|
||||
},
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* ImageToolbox is an image editor for android
|
||||
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* You should have received a copy of the Apache License
|
||||
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
|
||||
*/
|
||||
|
||||
package ru.tech.imageresizershrinker.noise_generation.presentation.viewModel
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Job
|
||||
import ru.tech.imageresizershrinker.core.domain.dispatchers.DispatchersHolder
|
||||
import ru.tech.imageresizershrinker.core.domain.image.ImageCompressor
|
||||
import ru.tech.imageresizershrinker.core.domain.image.ImageScaler
|
||||
import ru.tech.imageresizershrinker.core.domain.image.ShareProvider
|
||||
import ru.tech.imageresizershrinker.core.domain.image.model.ImageFormat
|
||||
import ru.tech.imageresizershrinker.core.domain.image.model.ImageInfo
|
||||
import ru.tech.imageresizershrinker.core.domain.image.model.Quality
|
||||
import ru.tech.imageresizershrinker.core.domain.model.IntegerSize
|
||||
import ru.tech.imageresizershrinker.core.domain.saving.FileController
|
||||
import ru.tech.imageresizershrinker.core.domain.saving.model.ImageSaveTarget
|
||||
import ru.tech.imageresizershrinker.core.domain.saving.model.SaveResult
|
||||
import ru.tech.imageresizershrinker.core.domain.utils.smartJob
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.BaseViewModel
|
||||
import ru.tech.imageresizershrinker.core.ui.utils.state.update
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.NoiseGenerator
|
||||
import ru.tech.imageresizershrinker.noise_generation.domain.model.NoiseParams
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class NoiseGenerationViewModel @Inject constructor(
|
||||
dispatchersHolder: DispatchersHolder,
|
||||
private val noiseGenerator: NoiseGenerator<Bitmap>,
|
||||
private val fileController: FileController,
|
||||
private val shareProvider: ShareProvider<Bitmap>,
|
||||
private val imageCompressor: ImageCompressor<Bitmap>,
|
||||
private val imageScaler: ImageScaler<Bitmap>
|
||||
) : BaseViewModel(dispatchersHolder) {
|
||||
|
||||
private val _previewBitmap: MutableState<Bitmap?> = mutableStateOf(null)
|
||||
val previewBitmap: Bitmap? by _previewBitmap
|
||||
|
||||
private val _noiseParams: MutableState<NoiseParams> = mutableStateOf(NoiseParams.Default)
|
||||
val noiseParams: NoiseParams by _noiseParams
|
||||
|
||||
private val _noiseSize: MutableState<IntegerSize> = mutableStateOf(IntegerSize(1000, 1000))
|
||||
val noiseSize: IntegerSize by _noiseSize
|
||||
|
||||
private val _imageFormat: MutableState<ImageFormat> = mutableStateOf(ImageFormat.Default)
|
||||
val imageFormat: ImageFormat by _imageFormat
|
||||
|
||||
private val _quality: MutableState<Quality> = mutableStateOf(Quality.Base(100))
|
||||
val quality: Quality by _quality
|
||||
|
||||
private val _isSaving: MutableState<Boolean> = mutableStateOf(false)
|
||||
val isSaving by _isSaving
|
||||
|
||||
private var savingJob: Job? by smartJob {
|
||||
_isSaving.update { false }
|
||||
}
|
||||
|
||||
fun saveNoise(
|
||||
oneTimeSaveLocationUri: String?,
|
||||
onComplete: (result: SaveResult) -> Unit,
|
||||
) {
|
||||
savingJob = viewModelScope.launch(defaultDispatcher) {
|
||||
_isSaving.update { true }
|
||||
noiseGenerator.generateNoise(
|
||||
width = noiseSize.width,
|
||||
height = noiseSize.height,
|
||||
noiseParams = noiseParams,
|
||||
onFailure = {
|
||||
onComplete(SaveResult.Error.Exception(it))
|
||||
}
|
||||
)?.let { bitmap ->
|
||||
val imageInfo = ImageInfo(
|
||||
width = bitmap.width,
|
||||
height = bitmap.height,
|
||||
quality = quality,
|
||||
imageFormat = imageFormat
|
||||
)
|
||||
onComplete(
|
||||
fileController.save(
|
||||
saveTarget = ImageSaveTarget(
|
||||
imageInfo = imageInfo,
|
||||
metadata = null,
|
||||
originalUri = "Noise",
|
||||
sequenceNumber = null,
|
||||
data = imageCompressor.compress(
|
||||
image = bitmap,
|
||||
imageFormat = imageFormat,
|
||||
quality = quality
|
||||
)
|
||||
),
|
||||
keepOriginalMetadata = true,
|
||||
oneTimeSaveLocationUri = oneTimeSaveLocationUri
|
||||
).onSuccess(::registerSave)
|
||||
)
|
||||
}
|
||||
_isSaving.update { false }
|
||||
}
|
||||
}
|
||||
|
||||
fun cacheCurrentNoise(onComplete: (Uri) -> Unit) {
|
||||
savingJob = viewModelScope.launch {
|
||||
_isSaving.update { true }
|
||||
noiseGenerator.generateNoise(
|
||||
width = noiseSize.width,
|
||||
height = noiseSize.height,
|
||||
noiseParams = noiseParams
|
||||
)?.let { image ->
|
||||
val imageInfo = ImageInfo(
|
||||
width = image.width,
|
||||
height = image.height,
|
||||
quality = quality,
|
||||
imageFormat = imageFormat
|
||||
)
|
||||
shareProvider.cacheImage(
|
||||
image = image,
|
||||
imageInfo = imageInfo
|
||||
)?.let { uri ->
|
||||
onComplete(uri.toUri())
|
||||
}
|
||||
}
|
||||
_isSaving.update { false }
|
||||
}
|
||||
}
|
||||
|
||||
fun shareNoise(onComplete: () -> Unit) {
|
||||
cacheCurrentNoise { uri ->
|
||||
viewModelScope.launch {
|
||||
shareProvider.shareUri(
|
||||
uri = uri.toString(),
|
||||
onComplete = onComplete
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelSaving() {
|
||||
savingJob?.cancel()
|
||||
savingJob = null
|
||||
_isSaving.update { false }
|
||||
}
|
||||
|
||||
fun setImageFormat(imageFormat: ImageFormat) {
|
||||
_imageFormat.update { imageFormat }
|
||||
}
|
||||
|
||||
fun setQuality(quality: Quality) {
|
||||
_quality.update { quality }
|
||||
}
|
||||
|
||||
fun updateParams(params: NoiseParams) {
|
||||
_noiseParams.update { params }
|
||||
updatePreview()
|
||||
}
|
||||
|
||||
fun setNoiseWidth(width: Int) {
|
||||
_noiseSize.update { it.copy(width = width.coerceAtMost(2048)) }
|
||||
updatePreview()
|
||||
}
|
||||
|
||||
fun setNoiseHeight(height: Int) {
|
||||
_noiseSize.update { it.copy(height = height.coerceAtMost(2048)) }
|
||||
updatePreview()
|
||||
}
|
||||
|
||||
private fun updatePreview() {
|
||||
viewModelScope.launch {
|
||||
_isImageLoading.update { true }
|
||||
_previewBitmap.update { null }
|
||||
noiseGenerator.generateNoise(
|
||||
width = noiseSize.width,
|
||||
height = noiseSize.height,
|
||||
noiseParams = noiseParams
|
||||
).also { bitmap ->
|
||||
_previewBitmap.update { bitmap }
|
||||
_isImageLoading.update { false }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
updatePreview()
|
||||
}
|
||||
|
||||
}
|
@ -60,4 +60,5 @@ dependencies {
|
||||
implementation(projects.feature.imageSplitting)
|
||||
implementation(projects.feature.colorTools)
|
||||
implementation(projects.feature.webpTools)
|
||||
implementation(projects.feature.noiseGeneration)
|
||||
}
|
@ -73,6 +73,7 @@ import ru.tech.imageresizershrinker.feature.watermarking.presentation.Watermarki
|
||||
import ru.tech.imageresizershrinker.feature.webp_tools.presentation.WebpToolsContent
|
||||
import ru.tech.imageresizershrinker.feature.zip.presentation.ZipContent
|
||||
import ru.tech.imageresizershrinker.image_splitting.presentation.ImageSplitterContent
|
||||
import ru.tech.imageresizershrinker.noise_generation.presentation.NoiseGenerationContent
|
||||
|
||||
@Composable
|
||||
internal fun ScreenSelector(
|
||||
@ -400,6 +401,13 @@ internal fun ScreenSelector(
|
||||
onNavigate = onNavigate
|
||||
)
|
||||
}
|
||||
|
||||
Screen.NoiseGeneration -> {
|
||||
NoiseGenerationContent(
|
||||
onGoBack = onGoBack,
|
||||
onNavigate = onNavigate
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
ScreenBasedMaxBrightnessEnforcement(navController.currentDestination())
|
||||
|
@ -38,7 +38,7 @@ import java.io.FileWriter
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
internal class SvgManagerImpl @Inject constructor(
|
||||
internal class AndroidSvgManager @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val randomStringGenerator: RandomStringGenerator,
|
||||
private val imageGetter: ImageGetter<Bitmap, ExifInterface>,
|
@ -21,7 +21,7 @@ import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.tech.imageresizershrinker.feature.svg_maker.data.SvgManagerImpl
|
||||
import ru.tech.imageresizershrinker.feature.svg_maker.data.AndroidSvgManager
|
||||
import ru.tech.imageresizershrinker.feature.svg_maker.domain.SvgManager
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -33,7 +33,7 @@ internal interface SvgMakerModule {
|
||||
@Singleton
|
||||
@Binds
|
||||
fun provideSvgManager(
|
||||
manager: SvgManagerImpl
|
||||
manager: AndroidSvgManager
|
||||
): SvgManager
|
||||
|
||||
}
|
@ -32,7 +32,7 @@ import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class ZipManagerImpl @Inject constructor(
|
||||
internal class AndroidZipManager @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
dispatchersHolder: DispatchersHolder
|
||||
) : DispatchersHolder by dispatchersHolder, ZipManager {
|
@ -21,7 +21,7 @@ import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.tech.imageresizershrinker.feature.zip.data.ZipManagerImpl
|
||||
import ru.tech.imageresizershrinker.feature.zip.data.AndroidZipManager
|
||||
import ru.tech.imageresizershrinker.feature.zip.domain.ZipManager
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -33,7 +33,7 @@ internal interface ZipModule {
|
||||
@Singleton
|
||||
@Binds
|
||||
fun provideZipManager(
|
||||
manager: ZipManagerImpl
|
||||
manager: AndroidZipManager
|
||||
): ZipManager
|
||||
|
||||
}
|
@ -17,12 +17,6 @@
|
||||
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
include(":feature:color-tools")
|
||||
|
||||
|
||||
include(":feature:image-splitting")
|
||||
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
includeBuild("build-logic")
|
||||
@ -103,7 +97,10 @@ include(":feature:format-conversion")
|
||||
include(":feature:document-scanner")
|
||||
include(":feature:scan-qr-code")
|
||||
include(":feature:image-stacking")
|
||||
include(":feature:image-splitting")
|
||||
include(":feature:color-tools")
|
||||
include(":feature:webp-tools")
|
||||
include(":feature:noise-generation")
|
||||
|
||||
include(":feature:root")
|
||||
|
||||
|
Reference in New Issue
Block a user