Import from internal git
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
.kotlin
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
12
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
/discord.xml
|
||||
/material_theme_project_new.xml
|
||||
/PMDPlugin.xml
|
||||
/.name
|
||||
17
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
10
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
86
README.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Projet TDS MASI 4
|
||||
---
|
||||
# Ce qui a changé
|
||||
|
||||
## Fonctionnalité
|
||||
Certaines fonctionnalités ont été déplacées ou légérement adaptée afin de les rendre
|
||||
plus facile à prendre en main
|
||||
|
||||
---
|
||||
|
||||
### Réorganisation des menus
|
||||
Certains items du menu tels que le chargement et la création des image,
|
||||
les fonctionnalités de dessin et l'affichage des transformées de Fourier ont été rémaniés.
|
||||
---
|
||||
|
||||
### Chargement et création des images
|
||||
Désormais les images sont automatiquement chargée en format RGB.
|
||||
Leur réprésentation en niveaux de gris est gardée en mémoire à chaque
|
||||
modification de l'image et peut-être utilisée dès que nécessaire.
|
||||
|
||||
#### Exemple
|
||||
```java
|
||||
//Crée une image blanche de 512 pixels par 512 pixels
|
||||
Image image = new Image(255,255,255, 512,512);
|
||||
|
||||
//Crée une copie de l'image en récupérant son équivalent en niveaus de gris
|
||||
GrayScaleMatrix = image.toGrayScale();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Affichage des transformées de fourier
|
||||
|
||||
Le module, la phase, la partie réelle et la partie imaginaire sont maintenant
|
||||
régroupés dans une seule et même fenêtre. L'affichage des transformées de plusieurs
|
||||
images différentes à travers différentes fenêtres reste possible.
|
||||
|
||||

|
||||
---
|
||||
|
||||
## Architecture
|
||||
C'est ici que la majorité des changements ont eus lieu.
|
||||
Une architecture et des patterns modernes ont remplacé la majorité
|
||||
de la base de code précédente.
|
||||
|
||||
### MVP
|
||||
Le programme utilise maintenant un design pattern architectural
|
||||
Model-View-Presenter afin de séparer au mieux les responsabilités
|
||||
des différentes couches et d'imposer un flux d'éxecution strict
|
||||
tel que décrit comme suit :
|
||||
|
||||
- View : Réceptionne les événements utilisateur et gère l'état visuel de la fenêtre
|
||||
- Presenter : Couche d'orchestration entre la vue et la logique métier. Possède un accès aux données locales à la vue courante.
|
||||
- Services : Couche tampons faisant le lien entre les presenters, l'infrastructure et la couche domaine. Possède un accès à l'état global de l'application
|
||||
- Domaine : Contient la logique métier de l'application. Doit rester totalement agnostique de toute dépendance technologique
|
||||
- Infrastructure : Couche contenant un accès à des ressources externes telles que le système de fichier de la machine hôte
|
||||

|
||||
|
||||
### Gestion d'état
|
||||
La gestion d'état a été remaniée. Plusieurs composants d'état sont maintenant disponabible dans le package *app.state* :
|
||||
- Classe abstraite *State* : Classe parent donnant une structure à chaque classe d'état
|
||||
- Etat global du programme : Représenté par la classe *AppState*. Cet classe est déclarée comme étant un singleton dans le conteneur Guava
|
||||
- Etat local d'une vue : Representé par toutes les autres classes disponibles dans le package. Ne devrait pas être un singleton
|
||||
|
||||
### Injection de dépendances
|
||||
Le programme utilise maintenant le conteneur d'injection de dépendance de Google, Guava.
|
||||
Cet ajout permet d'isoler la logique d'instanciation des différents composants lors de l'éxecution.
|
||||
La logique de navigation entre les différentes fenêtres s'en retrouve facilitée.
|
||||
Toutes les dépendances sont enregistrées dans la classe *AppModule* au sein du package *app*
|
||||
|
||||
### Logique de navigation
|
||||
L'injection de dépendance précédement expliquée a permis d'isoler la logique de navigation.
|
||||
Désormais, pour naviguer entre différentes vue, il suffit de créer une méthode au sein de
|
||||
la classe SwingNavigator, d'injecter l'interface INavigator dans une Presenter et d'appeler la méthode nouvellement créée.
|
||||
|
||||
Un exemple de fonctionnement est déjà fourni dans la classe SwingNavigator et peut-être facilement retracé en trouvant
|
||||
les usages de la méthode *showFourier()* avec un IDE moderne.
|
||||
|
||||
#### Note importante
|
||||
Toutes les vues doivent être enregistrée en tant que dépendance au sein du conteneur Guava.
|
||||
Pour ce faire, différents exemples sont déjà implémenté dans la classe *AppModule*.
|
||||
Cet ajout aura pour conséquence de donner accès à un objet ``Provider<T>`` (où T est le type de la dépendance enregistrée)
|
||||
qui pourra être injecté dans la classe SwingNavigator.
|
||||
|
||||
Appeler la méthode ``Provider<T>.get()`` aura pour conséquence d'instancier la dépendance désirée, dans notre cas,
|
||||
une potentielle vue qui pourra ensuite être affichée.
|
||||
BIN
architecture.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
24
build.gradle.kts
Normal file
@@ -0,0 +1,24 @@
|
||||
plugins {
|
||||
id("java")
|
||||
}
|
||||
|
||||
group = "be.naaturel"
|
||||
version = "1.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jfree:jfreechart:1.5.4")
|
||||
implementation("com.google.inject:guice:7.0.0")
|
||||
implementation("com.google.guava:guava:33.4.0-jre")
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Wed Apr 08 11:32:29 CEST 2026
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
234
gradlew
vendored
Normal file
@@ -0,0 +1,234 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
BIN
menu_fourier.png
Normal file
|
After Width: | Height: | Size: 314 KiB |
1
settings.gradle.kts
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = "TDS"
|
||||
28
src/main/java/Main.java
Normal file
@@ -0,0 +1,28 @@
|
||||
import app.AppModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import presenters.MainPresenter;
|
||||
import presenters.NavPresenter;
|
||||
import ui.implementation.components.nav.NavBar;
|
||||
import ui.implementation.views.MainView;
|
||||
import ui.interfaces.IMainView;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* @author Laurent Crema
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Injector injector = Guice.createInjector(new AppModule());
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
|
||||
NavBar navBar = injector.getInstance(NavBar.class);
|
||||
MainView view = (MainView) injector.getInstance(IMainView.class);
|
||||
view.setNavBar(navBar);
|
||||
view.setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
49
src/main/java/app/AppModule.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package app;
|
||||
|
||||
import app.state.AppState;
|
||||
import app.state.DoubleMatrixState;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Scopes;
|
||||
import presenters.DoubleMatrixPresenter;
|
||||
import presenters.MainPresenter;
|
||||
import presenters.NavPresenter;
|
||||
import services.ImageService;
|
||||
import ui.implementation.components.nav.NavBar;
|
||||
import ui.implementation.views.DoubleMatrix;
|
||||
import ui.implementation.views.MainView;
|
||||
import ui.interfaces.IDoubleMatrix;
|
||||
import ui.interfaces.IMainView;
|
||||
import ui.interfaces.INavBar;
|
||||
|
||||
/**
|
||||
* @author Laurent Crema
|
||||
*/
|
||||
public class AppModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
|
||||
//--- States ---
|
||||
bind(AppState.class).in(Scopes.SINGLETON);
|
||||
bind(DoubleMatrixState.class);
|
||||
|
||||
//--- Application layer ---
|
||||
bind(EventBus.class).in(Scopes.SINGLETON);
|
||||
bind(INavigator.class).to(SwingNavigator.class).in(Scopes.SINGLETON);
|
||||
|
||||
//--- UI layer ---
|
||||
bind(INavBar.class).to(NavBar.class);
|
||||
bind(IMainView.class).to(MainView.class);
|
||||
bind(IDoubleMatrix.class).to(DoubleMatrix.class);
|
||||
|
||||
//--- Presentation layer ---
|
||||
bind(MainPresenter.class);
|
||||
bind(NavPresenter.class);
|
||||
bind(DoubleMatrixPresenter.class);
|
||||
|
||||
//--- Service layer layer ---
|
||||
bind(ImageService.class);
|
||||
|
||||
}
|
||||
}
|
||||
7
src/main/java/app/INavigator.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package app;
|
||||
|
||||
public interface INavigator {
|
||||
|
||||
void showFourier();
|
||||
|
||||
}
|
||||
23
src/main/java/app/SwingNavigator.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package app;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import ui.implementation.views.DoubleMatrix;
|
||||
|
||||
public class SwingNavigator implements INavigator {
|
||||
|
||||
private final Provider<DoubleMatrix> doubleMatrixProvider;
|
||||
|
||||
@Inject
|
||||
public SwingNavigator(
|
||||
Provider<DoubleMatrix> doubleMatrixProvider
|
||||
) {
|
||||
this.doubleMatrixProvider = doubleMatrixProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFourier() {
|
||||
DoubleMatrix doubleMatrix = doubleMatrixProvider.get();
|
||||
doubleMatrix.setVisible(true);
|
||||
}
|
||||
}
|
||||
37
src/main/java/app/state/AppState.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package app.state;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import domain.common.Mode;
|
||||
import domain.events.ImageChangedEvent;
|
||||
import domain.events.ModeChangedEvent;
|
||||
import domain.image.Image;
|
||||
|
||||
@Singleton
|
||||
public class AppState extends State {
|
||||
|
||||
private Mode mode;
|
||||
private Image rgbImage;
|
||||
|
||||
@Inject
|
||||
public AppState(EventBus eventBus) {
|
||||
super(eventBus);
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
this.mode = mode;
|
||||
this.eventBus.post(new ModeChangedEvent(mode));
|
||||
}
|
||||
|
||||
public void setImage(Image image) {
|
||||
this.rgbImage = image;
|
||||
this.eventBus.post(new ImageChangedEvent(image));
|
||||
}
|
||||
|
||||
public Image getImage(){
|
||||
return this.rgbImage;
|
||||
}
|
||||
|
||||
public Mode getMode() { return this.mode; }
|
||||
}
|
||||
49
src/main/java/app/state/DoubleMatrixState.java
Normal file
@@ -0,0 +1,49 @@
|
||||
package app.state;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
import domain.events.fourier.*;
|
||||
import domain.image.GrayScaleMatrix;
|
||||
import domain.image.processing.complex.ComplexMatrix;
|
||||
|
||||
public class DoubleMatrixState extends State {
|
||||
|
||||
private ComplexMatrix fourier;
|
||||
private GrayScaleMatrix module;
|
||||
private GrayScaleMatrix phase;
|
||||
private GrayScaleMatrix real;
|
||||
private GrayScaleMatrix imaginary;
|
||||
|
||||
@Inject
|
||||
public DoubleMatrixState() { super(new EventBus()); }
|
||||
|
||||
public ComplexMatrix getFourier() {
|
||||
return fourier;
|
||||
}
|
||||
|
||||
public void setFourier(ComplexMatrix fourier) {
|
||||
this.fourier = fourier;
|
||||
this.eventBus.post(new ComplexMatrixChanged(fourier));
|
||||
}
|
||||
|
||||
public void setModule(GrayScaleMatrix matrix) {
|
||||
this.module = matrix;
|
||||
this.eventBus.post(new ModuleChangedEvent(matrix));
|
||||
}
|
||||
|
||||
public void setPhase(GrayScaleMatrix matrix) {
|
||||
this.phase = matrix;
|
||||
this.eventBus.post(new PhaseChangedEvent(matrix));
|
||||
}
|
||||
|
||||
public void setReal(GrayScaleMatrix matrix) {
|
||||
this.real = matrix;
|
||||
this.eventBus.post(new RealChangedEvent(matrix));
|
||||
}
|
||||
|
||||
public void setImaginary(GrayScaleMatrix matrix) {
|
||||
this.imaginary = matrix;
|
||||
this.eventBus.post(new ImaginaryChangedEvent(matrix));
|
||||
}
|
||||
}
|
||||
16
src/main/java/app/state/State.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package app.state;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
public abstract class State {
|
||||
|
||||
protected EventBus eventBus;
|
||||
|
||||
public State(EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
public void register(Object obj) {
|
||||
eventBus.register(obj);
|
||||
}
|
||||
}
|
||||
9
src/main/java/domain/common/Mode.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package domain.common;
|
||||
|
||||
public enum Mode {
|
||||
DISABLED,
|
||||
DRAW_PIXEL,
|
||||
DRAW_LINE,
|
||||
DRAW_RECTANGLE,
|
||||
DRAW_CIRCLE,
|
||||
}
|
||||
5
src/main/java/domain/events/ImageChangedEvent.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package domain.events;
|
||||
|
||||
import domain.image.Image;
|
||||
|
||||
public record ImageChangedEvent(Image image) { }
|
||||
6
src/main/java/domain/events/MatrixChangedEvent.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package domain.events;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
|
||||
public record MatrixChangedEvent(GrayScaleMatrix matrix) {
|
||||
}
|
||||
5
src/main/java/domain/events/ModeChangedEvent.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package domain.events;
|
||||
|
||||
import domain.common.Mode;
|
||||
|
||||
public record ModeChangedEvent(Mode mode) { }
|
||||
@@ -0,0 +1,6 @@
|
||||
package domain.events.fourier;
|
||||
|
||||
import domain.image.processing.complex.ComplexMatrix;
|
||||
|
||||
public record ComplexMatrixChanged(ComplexMatrix matrix) {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package domain.events.fourier;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
|
||||
public record ImaginaryChangedEvent(GrayScaleMatrix matrix) {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package domain.events.fourier;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
|
||||
public record ModuleChangedEvent(GrayScaleMatrix matrix) {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package domain.events.fourier;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
|
||||
public record PhaseChangedEvent(GrayScaleMatrix matrix) {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package domain.events.fourier;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
|
||||
public record RealChangedEvent(GrayScaleMatrix matrix) {
|
||||
}
|
||||
130
src/main/java/domain/image/GrayScaleMatrix.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package domain.image;
|
||||
|
||||
import infrastructure.image.io.ImageSaver;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Jean-Marc Wagner, Laurent Crema
|
||||
*/
|
||||
public class GrayScaleMatrix {
|
||||
|
||||
private final double[][] matrix;
|
||||
|
||||
private final double maxValue;
|
||||
private final double minValue;
|
||||
|
||||
public GrayScaleMatrix(double[][] matrix) {
|
||||
this.matrix = matrix;
|
||||
this.maxValue = computeMax();
|
||||
this.minValue = computeMin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the actual content of this matrix.
|
||||
* Since a copy is returned, no change that will be applied on it is going to affect the original matrix
|
||||
* @return a copy of the actual content
|
||||
*/
|
||||
public double[][] getRawData() {
|
||||
return Arrays.copyOf(matrix, matrix.length);
|
||||
}
|
||||
|
||||
public int getWidth(){
|
||||
return matrix[0].length;
|
||||
}
|
||||
|
||||
public int getHeight(){
|
||||
return matrix.length;
|
||||
}
|
||||
|
||||
public double getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public double getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
public double getValueAt(double normalized) {
|
||||
if(normalized <= 0) { return minValue; }
|
||||
if(normalized >= 1) { return maxValue; }
|
||||
return minValue + (maxValue - minValue) * normalized;
|
||||
}
|
||||
|
||||
private double computeMax(){
|
||||
double res = matrix[0][0];
|
||||
for(int y = 0; y < getHeight(); y++) {
|
||||
for (int x = 0; x < getWidth(); x++) {
|
||||
double value = matrix[y][x];
|
||||
if (value > res) res = matrix[y][x];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private double computeMin(){
|
||||
double res = matrix[0][0];
|
||||
for(int y = 0; y < getHeight(); y++) {
|
||||
for (int x = 0; x < getWidth(); x++) {
|
||||
double value = matrix[y][x];
|
||||
if (value < res) res = matrix[y][x];
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public GrayScaleMatrix clip() {
|
||||
return clip(minValue, maxValue);
|
||||
}
|
||||
|
||||
public GrayScaleMatrix clip(double lowerBound, double upperBound) {
|
||||
|
||||
int height = getHeight();
|
||||
int width = getWidth();
|
||||
|
||||
double[][] res = new double[height][width];
|
||||
|
||||
double range = upperBound - lowerBound;
|
||||
if (range == 0) range = 1;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
|
||||
double v = matrix[y][x];
|
||||
double out;
|
||||
|
||||
if (v >= upperBound) {
|
||||
out = 255;
|
||||
} else if (v <= lowerBound) {
|
||||
out = 0;
|
||||
} else {
|
||||
out = (v - lowerBound) / range * 255.0;
|
||||
}
|
||||
|
||||
res[y][x] = Math.clamp(out, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
return new GrayScaleMatrix(res);
|
||||
}
|
||||
|
||||
public Image toImage() {
|
||||
|
||||
Pixel[][] res = new Pixel[getHeight()][getWidth()];
|
||||
|
||||
double range = maxValue - minValue;
|
||||
if (range == 0) range = 1;
|
||||
|
||||
for (int y = 0; y < getHeight(); y++) {
|
||||
for (int x = 0; x < getWidth(); x++) {
|
||||
|
||||
double normalized = (matrix[y][x] - minValue) / range;
|
||||
int gray = (int) (normalized * 255);
|
||||
|
||||
res[y][x] = new Pixel(gray, gray, gray);
|
||||
}
|
||||
}
|
||||
|
||||
return new Image(res);
|
||||
}
|
||||
}
|
||||
126
src/main/java/domain/image/Image.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package domain.image;
|
||||
|
||||
/**
|
||||
* @author Laurent Crema
|
||||
*/
|
||||
public class Image {
|
||||
|
||||
private final Pixel[][] pixels;
|
||||
private GrayScaleMatrix grayScaleEquivalent;
|
||||
|
||||
public Image(Pixel[][] pixels) {
|
||||
if(pixels == null || pixels.length == 0 || pixels[0].length == 0)
|
||||
throw new IllegalArgumentException("Invalid dimensions");
|
||||
this.pixels = pixels;
|
||||
}
|
||||
|
||||
public Image(int red, int green, int blue, int height, int width) {
|
||||
pixels = new Pixel[height][width];
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
pixels[y][x] = new Pixel(red, green, blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getWidth(){
|
||||
return pixels[0].length;
|
||||
}
|
||||
|
||||
public int getHeight(){
|
||||
return pixels.length;
|
||||
}
|
||||
|
||||
public Pixel getPixel(int x, int y){
|
||||
if(x < 0 || y < 0 || x >= getWidth() || y >= getHeight())
|
||||
throw new IllegalArgumentException("Invalid coordinates");
|
||||
return pixels[y][x];
|
||||
}
|
||||
|
||||
public void setPixelColor(int x, int y, int red, int green, int blue){
|
||||
if(x < 0 || y < 0 || x >= getWidth() || y >= getHeight())
|
||||
throw new IllegalArgumentException("Invalid coordinates");
|
||||
Pixel p = new Pixel(red, green, blue);
|
||||
pixels[y][x] = p;
|
||||
grayScaleEquivalent = toGrayScale();
|
||||
}
|
||||
|
||||
public void setRectangleColor(int x1, int x2, int y1, int y2,
|
||||
int red, int green, int blue) {
|
||||
|
||||
int minX = Math.max(0, Math.min(x1, x2));
|
||||
int maxX = Math.min(pixels[0].length - 1, Math.max(x1, x2));
|
||||
|
||||
int minY = Math.max(0, Math.min(y1, y2));
|
||||
int maxY = Math.min(pixels.length - 1, Math.max(y1, y2));
|
||||
|
||||
Pixel p = new Pixel(red, green, blue);
|
||||
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
pixels[y][x] = p;
|
||||
}
|
||||
}
|
||||
|
||||
grayScaleEquivalent = toGrayScale();
|
||||
}
|
||||
|
||||
public void setLineColor(int x1, int x2, int y1, int y2,
|
||||
int red, int green, int blue) {
|
||||
|
||||
int dx = Math.abs(x2 - x1);
|
||||
int dy = Math.abs(y2 - y1);
|
||||
|
||||
int sx = x1 < x2 ? 1 : -1;
|
||||
int sy = y1 < y2 ? 1 : -1;
|
||||
|
||||
int err = dx - dy;
|
||||
|
||||
Pixel p = new Pixel(red, green, blue);
|
||||
|
||||
while (true) {
|
||||
if (x1 >= 0 && y1 >= 0 && x1 < getWidth() && y1 < getHeight()) {
|
||||
pixels[y1][x1] = p;
|
||||
}
|
||||
|
||||
if (x1 == x2 && y1 == y2) break;
|
||||
|
||||
int e2 = 2 * err;
|
||||
|
||||
if (e2 > -dy) {
|
||||
err -= dy;
|
||||
x1 += sx;
|
||||
}
|
||||
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
y1 += sy;
|
||||
}
|
||||
}
|
||||
|
||||
grayScaleEquivalent = toGrayScale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the corresponding gray scale matrix. Since this operation is destructive, the original image is not modified.
|
||||
* Instead, a new one is returned.
|
||||
* @return A newly converted image
|
||||
*/
|
||||
public GrayScaleMatrix toGrayScale() {
|
||||
|
||||
if(grayScaleEquivalent != null) return this.grayScaleEquivalent;
|
||||
|
||||
double[][] res = new double[getHeight()][getWidth()];
|
||||
|
||||
for (int y = 0; y < getHeight(); y++) {
|
||||
for (int x = 0; x < getWidth(); x++) {
|
||||
|
||||
Pixel p = pixels[y][x];
|
||||
res[y][x] = p.grayValue();
|
||||
}
|
||||
}
|
||||
|
||||
this.grayScaleEquivalent = new GrayScaleMatrix(res);
|
||||
return this.grayScaleEquivalent;
|
||||
}
|
||||
}
|
||||
62
src/main/java/domain/image/Pixel.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package domain.image;
|
||||
|
||||
/**
|
||||
* @author Laurent Crema
|
||||
*/
|
||||
public class Pixel {
|
||||
|
||||
private int red, green, blue;
|
||||
|
||||
public Pixel(int red, int green, int blue) {
|
||||
|
||||
if(checkColorValidity(red))
|
||||
throw new IllegalArgumentException("Invalid red value"); // <-- here
|
||||
if(checkColorValidity(green)) throw new IllegalArgumentException("Invalid green value");
|
||||
if(checkColorValidity(blue)) throw new IllegalArgumentException("Invalid blue value");
|
||||
|
||||
this.red = red;
|
||||
this.green = green;
|
||||
this.blue = blue;
|
||||
}
|
||||
|
||||
public int getRed() {
|
||||
return red;
|
||||
}
|
||||
|
||||
public void setRed(int value) {
|
||||
if(checkColorValidity(value)) throw new IllegalArgumentException("Invalid value");
|
||||
this.red = value;
|
||||
}
|
||||
|
||||
public int getGreen() {
|
||||
return green;
|
||||
}
|
||||
|
||||
public void setGreen(int value) {
|
||||
if(checkColorValidity(value)) throw new IllegalArgumentException("Invalid value");
|
||||
this.green = value;
|
||||
}
|
||||
|
||||
public int getBlue() {
|
||||
return blue;
|
||||
}
|
||||
|
||||
public void setBlue(int value) {
|
||||
if(checkColorValidity(value)) throw new IllegalArgumentException("Invalid value");
|
||||
this.blue = value;
|
||||
}
|
||||
|
||||
public boolean checkColorValidity(int color) {
|
||||
return !(color >= 0 && color <= 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the current RGB combination into a gray value according to UIT-R BT 709 standard.
|
||||
* The current RGB values are not modified.
|
||||
* @return the corresponding gray scale value
|
||||
*/
|
||||
public int grayValue(){
|
||||
return (int) (0.2126 * red + 0.7152 * green + 0.0722 * blue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package domain.image.processing.complex;
|
||||
|
||||
public class ComplexMatrix
|
||||
{
|
||||
private Complexe m[][];
|
||||
private int lignes;
|
||||
private int colonnes;
|
||||
|
||||
/** Creates a new instance of MatriceComplexe */
|
||||
public ComplexMatrix(int l, int c)
|
||||
{
|
||||
lignes = l;
|
||||
colonnes = c;
|
||||
m = new Complexe[l][c];
|
||||
for(int i=0 ; i<lignes ; i++)
|
||||
for(int j=0 ; j<colonnes ; j++)
|
||||
m[i][j] = new Complexe(0.0,0.0);
|
||||
}
|
||||
|
||||
public void set(int ligne,int colonne,Complexe complexe)
|
||||
{
|
||||
m[ligne][colonne] = complexe;
|
||||
}
|
||||
|
||||
public void set(int ligne,int colonne,double partieReelle,double partieImaginaire)
|
||||
{
|
||||
Complexe c = new Complexe(partieReelle,partieImaginaire);
|
||||
set(ligne,colonne,c);
|
||||
}
|
||||
|
||||
public Complexe get(int ligne,int colonne)
|
||||
{
|
||||
return m[ligne][colonne];
|
||||
}
|
||||
|
||||
public double[][] getPartieReelle()
|
||||
{
|
||||
double d[][] = new double[lignes][colonnes];
|
||||
for(int i=0 ; i<lignes ; i++)
|
||||
for(int j=0 ; j<colonnes ; j++)
|
||||
d[i][j] = m[i][j].getPartieReelle();
|
||||
return d;
|
||||
}
|
||||
|
||||
public double[][] getPartieImaginaire()
|
||||
{
|
||||
double d[][] = new double[lignes][colonnes];
|
||||
for(int i=0 ; i<lignes ; i++)
|
||||
for(int j=0 ; j<colonnes ; j++)
|
||||
d[i][j] = m[i][j].getPartieImaginaire();
|
||||
return d;
|
||||
}
|
||||
|
||||
public double[][] getModule()
|
||||
{
|
||||
double d[][] = new double[lignes][colonnes];
|
||||
for(int i=0 ; i<lignes ; i++)
|
||||
for(int j=0 ; j<colonnes ; j++)
|
||||
d[i][j] = m[i][j].getModule();
|
||||
return d;
|
||||
}
|
||||
|
||||
public double[][] getPhase()
|
||||
{
|
||||
double d[][] = new double[lignes][colonnes];
|
||||
for(int i=0 ; i<lignes ; i++)
|
||||
for(int j=0 ; j<colonnes ; j++)
|
||||
d[i][j] = m[i][j].getPhase();
|
||||
return d;
|
||||
}
|
||||
|
||||
public int getLignes() {
|
||||
return lignes;
|
||||
}
|
||||
|
||||
public int getColonnes() {
|
||||
return colonnes;
|
||||
}
|
||||
|
||||
}
|
||||
61
src/main/java/domain/image/processing/complex/Complexe.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package domain.image.processing.complex;
|
||||
|
||||
public class Complexe
|
||||
{
|
||||
private double partieReelle;
|
||||
private double partieImaginaire;
|
||||
|
||||
/** Creates a new instance of Complexe */
|
||||
public Complexe(double pr,double pi)
|
||||
{
|
||||
partieReelle = pr;
|
||||
partieImaginaire = pi;
|
||||
}
|
||||
|
||||
public double getModule()
|
||||
{
|
||||
return Math.sqrt(partieReelle*partieReelle + partieImaginaire*partieImaginaire);
|
||||
}
|
||||
|
||||
public double getPhase()
|
||||
{
|
||||
return Math.atan2(partieImaginaire,partieReelle);
|
||||
}
|
||||
|
||||
public void additionne(Complexe c)
|
||||
{
|
||||
partieReelle += c.getPartieReelle();
|
||||
partieImaginaire += c.getPartieImaginaire();
|
||||
}
|
||||
|
||||
public void multiplie(Complexe c)
|
||||
{
|
||||
double r,i;
|
||||
r = partieReelle*c.getPartieReelle() - partieImaginaire*c.getPartieImaginaire();
|
||||
i = partieReelle*c.getPartieImaginaire() + partieImaginaire*c.getPartieReelle();
|
||||
partieReelle = r;
|
||||
partieImaginaire = i;
|
||||
}
|
||||
|
||||
public Complexe conjugue()
|
||||
{
|
||||
return new Complexe(partieReelle,-partieImaginaire);
|
||||
}
|
||||
|
||||
public double getPartieReelle() {
|
||||
return partieReelle;
|
||||
}
|
||||
|
||||
public void setPartieReelle(double partieReelle) {
|
||||
this.partieReelle = partieReelle;
|
||||
}
|
||||
|
||||
public double getPartieImaginaire() {
|
||||
return partieImaginaire;
|
||||
}
|
||||
|
||||
public void setPartieImaginaire(double partieImaginaire) {
|
||||
this.partieImaginaire = partieImaginaire;
|
||||
}
|
||||
|
||||
}
|
||||
173
src/main/java/domain/image/processing/fourier/Fourier.java
Normal file
@@ -0,0 +1,173 @@
|
||||
package domain.image.processing.fourier;
|
||||
|
||||
import domain.image.processing.complex.ComplexMatrix;
|
||||
|
||||
public class Fourier
|
||||
{
|
||||
public static ComplexMatrix Fourier2D(double f[][])
|
||||
{
|
||||
int M = f.length;
|
||||
int N = f[0].length;
|
||||
ComplexMatrix F = new ComplexMatrix(M,N);
|
||||
|
||||
//********** n --> v **********
|
||||
double pr1[][] = new double[M][N];
|
||||
double pi1[][] = new double[M][N];
|
||||
|
||||
for(int m=0 ; m<M ; m++)
|
||||
{
|
||||
for(int v=0 ; v<=N/2 ; v++)
|
||||
{
|
||||
pr1[m][v] = 0.0;
|
||||
pi1[m][v] = 0.0;
|
||||
double cosTheta = 1.0, newCosTheta;
|
||||
double sinTheta = 0.0, newSinTheta;
|
||||
double cosDTheta = Math.cos(-2.0*Math.PI*(double)v/(double)N);
|
||||
double sinDTheta = Math.sin(-2.0*Math.PI*(double)v/(double)N);
|
||||
for (int n=0 ; n<N ; n++)
|
||||
{
|
||||
pr1[m][v] += f[m][n] * cosTheta;
|
||||
pi1[m][v] += f[m][n] * sinTheta;
|
||||
newCosTheta = cosTheta*cosDTheta - sinTheta*sinDTheta;
|
||||
newSinTheta = sinTheta*cosDTheta + cosTheta*sinDTheta;
|
||||
cosTheta = newCosTheta;
|
||||
sinTheta = newSinTheta;
|
||||
}
|
||||
pr1[m][v] /= (double)N;
|
||||
pi1[m][v] /= (double)N;
|
||||
if (v > 0)
|
||||
{
|
||||
pr1[m][N-v] = pr1[m][v];
|
||||
pi1[m][N-v] = -pi1[m][v];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//********** m --> u **********
|
||||
double pr2[][] = new double[M][N];
|
||||
double pi2[][] = new double[M][N];
|
||||
|
||||
for(int v=0 ; v<N ; v++)
|
||||
{
|
||||
for(int u=0 ; u<M ; u++)
|
||||
{
|
||||
pr2[u][v] = 0.0;
|
||||
pi2[u][v] = 0.0;
|
||||
double cosTheta = 1.0, newCosTheta;
|
||||
double sinTheta = 0.0, newSinTheta;
|
||||
double cosDTheta = Math.cos(-2.0*Math.PI*(double)u/(double)M);
|
||||
double sinDTheta = Math.sin(-2.0*Math.PI*(double)u/(double)M);
|
||||
for (int m=0 ; m<M ; m++)
|
||||
{
|
||||
pr2[u][v] += pr1[m][v] * cosTheta - pi1[m][v] * sinTheta;
|
||||
pi2[u][v] += pr1[m][v] * sinTheta + pi1[m][v] * cosTheta;
|
||||
newCosTheta = cosTheta*cosDTheta - sinTheta*sinDTheta;
|
||||
newSinTheta = sinTheta*cosDTheta + cosTheta*sinDTheta;
|
||||
cosTheta = newCosTheta;
|
||||
sinTheta = newSinTheta;
|
||||
}
|
||||
pr2[u][v] /= (double)M;
|
||||
pi2[u][v] /= (double)M;
|
||||
}
|
||||
}
|
||||
|
||||
//********** Remplissage de F **********
|
||||
for(int u=0 ; u<M ; u++)
|
||||
for(int v=0 ; v<N ; v++)
|
||||
F.set(u,v,pr2[u][v],pi2[u][v]);
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
public static ComplexMatrix InverseFourier2D(ComplexMatrix F)
|
||||
{
|
||||
int M = F.getLignes();
|
||||
int N = F.getColonnes();
|
||||
ComplexMatrix f = new ComplexMatrix(M,N);
|
||||
|
||||
double pr0[][] = F.getPartieReelle();
|
||||
double pi0[][] = F.getPartieImaginaire();
|
||||
|
||||
//********** v --> n **********
|
||||
double pr1[][] = new double[M][N];
|
||||
double pi1[][] = new double[M][N];
|
||||
|
||||
for(int u=0 ; u<M ; u++)
|
||||
{
|
||||
for(int n=0 ; n<N ; n++)
|
||||
{
|
||||
pr1[u][n] = 0.0;
|
||||
pi1[u][n] = 0.0;
|
||||
double cosTheta = 1.0, newCosTheta;
|
||||
double sinTheta = 0.0, newSinTheta;
|
||||
double cosDTheta = Math.cos(2.0*Math.PI*(double)n/(double)N);
|
||||
double sinDTheta = Math.sin(2.0*Math.PI*(double)n/(double)N);
|
||||
for (int v=0 ; v<N ; v++)
|
||||
{
|
||||
pr1[u][n] += pr0[u][v] * cosTheta - pi0[u][v] * sinTheta;
|
||||
pi1[u][n] += pr0[u][v] * sinTheta + pi0[u][v] * cosTheta;
|
||||
newCosTheta = cosTheta*cosDTheta - sinTheta*sinDTheta;
|
||||
newSinTheta = sinTheta*cosDTheta + cosTheta*sinDTheta;
|
||||
cosTheta = newCosTheta;
|
||||
sinTheta = newSinTheta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//********** u --> m **********
|
||||
double pr2[][] = new double[M][N];
|
||||
double pi2[][] = new double[M][N];
|
||||
|
||||
for(int n=0 ; n<N ; n++)
|
||||
{
|
||||
for(int m=0 ; m<M ; m++)
|
||||
{
|
||||
pr2[m][n] = 0.0;
|
||||
pi2[m][n] = 0.0;
|
||||
double cosTheta = 1.0, newCosTheta;
|
||||
double sinTheta = 0.0, newSinTheta;
|
||||
double cosDTheta = Math.cos(2.0*Math.PI*(double)m/(double)M);
|
||||
double sinDTheta = Math.sin(2.0*Math.PI*(double)m/(double)M);
|
||||
for (int u=0 ; u<M ; u++)
|
||||
{
|
||||
pr2[m][n] += pr1[u][n] * cosTheta - pi1[u][n] * sinTheta;
|
||||
pi2[m][n] += pr1[u][n] * sinTheta + pi1[u][n] * cosTheta;
|
||||
newCosTheta = cosTheta*cosDTheta - sinTheta*sinDTheta;
|
||||
newSinTheta = sinTheta*cosDTheta + cosTheta*sinDTheta;
|
||||
cosTheta = newCosTheta;
|
||||
sinTheta = newSinTheta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//********** Remplissage de f **********
|
||||
for(int m=0 ; m<M ; m++)
|
||||
for(int n=0 ; n<N ; n++)
|
||||
f.set(m,n,pr2[m][n],pi2[m][n]);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public static ComplexMatrix decroise(ComplexMatrix F)
|
||||
{
|
||||
int M = F.getLignes();
|
||||
int N = F.getColonnes();
|
||||
ComplexMatrix mat = new ComplexMatrix(M,N);
|
||||
|
||||
int m,n;
|
||||
|
||||
for(m=0 ; m<M/2 ; m++)
|
||||
for(n=0 ; n<N/2 ; n++) mat.set(m,n,F.get(M/2+m,N/2+n));
|
||||
|
||||
for(m=M/2 ; m<M ; m++)
|
||||
for(n=0 ; n<N/2 ; n++) mat.set(m,n,F.get(m-M/2,n+N/2));
|
||||
|
||||
for(m=0 ; m<M/2 ; m++)
|
||||
for(n=N/2 ; n<N ; n++) mat.set(m,n,F.get(m+M/2,n-N/2));
|
||||
|
||||
for(m=M/2 ; m<M ; m++)
|
||||
for(n=N/2 ; n<N ; n++) mat.set(m,n,F.get(m-M/2,n-N/2));
|
||||
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package domain.image.processing.histogram;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
import domain.image.Image;
|
||||
|
||||
public class Histogramme {
|
||||
public static int[] Histogramme256(GrayScaleMatrix mat) {
|
||||
int M = mat.getHeight();
|
||||
int N = mat.getWidth();
|
||||
int histo[] = new int[256];
|
||||
|
||||
for (int i = 0; i < 256; i++) histo[i] = 0;
|
||||
|
||||
Image grayScaleImage = mat.toImage();
|
||||
|
||||
int pixelGrayValue = 0;
|
||||
for (int i = 0; i < M; i++) {
|
||||
for (int j = 0; j < N; j++) {
|
||||
pixelGrayValue = grayScaleImage.getPixel(i, j).grayValue();
|
||||
if ((pixelGrayValue >= 0) && (pixelGrayValue <= 255)) {
|
||||
histo[pixelGrayValue]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return histo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package domain.image.processing.lineaire;
|
||||
|
||||
import jdk.jshell.spi.ExecutionControl;
|
||||
|
||||
public class FiltrageLinaireGlobal {
|
||||
|
||||
public static int[][] filtrePasseBasIdeal(int[][] image,int frequenceCoupure){
|
||||
return null;
|
||||
}
|
||||
public static int[][] filtrePasseHautIdeal(int[][] image,int frequenceCoupure){
|
||||
return null;
|
||||
}
|
||||
public static int[][] filtrePasseBasButterworth(int[][] image,int frequenceCoupure,int ordre){
|
||||
return null;
|
||||
}
|
||||
public static int[][] filtrePasseHautButterworth(int[][] image,int frequenceCoupure, int ordre) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
39
src/main/java/infrastructure/image/io/ImageLoader.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package infrastructure.image.io;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import domain.image.Image;
|
||||
import domain.image.Pixel;
|
||||
|
||||
public class ImageLoader {
|
||||
|
||||
public static Image loadImage(File f) throws IOException {
|
||||
|
||||
BufferedImage buffered = ImageIO.read(f);
|
||||
|
||||
if (buffered == null) throw new IOException("Unsupported or invalid image file: " + f);
|
||||
|
||||
int width = buffered.getWidth();
|
||||
int height = buffered.getHeight();
|
||||
|
||||
Pixel[][] pixels = new Pixel[height][width];
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
|
||||
int rgb = buffered.getRGB(x, y);
|
||||
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
pixels[y][x] = new Pixel(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
return new Image(pixels);
|
||||
}
|
||||
}
|
||||
12
src/main/java/infrastructure/image/io/ImageSaver.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package infrastructure.image.io;
|
||||
|
||||
import domain.image.Image;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ImageSaver {
|
||||
|
||||
public static void saveImage(Image image) throws IOException {
|
||||
}
|
||||
|
||||
}
|
||||
55
src/main/java/infrastructure/ui/ImageMapper.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package infrastructure.ui;
|
||||
|
||||
import domain.image.Image;
|
||||
import domain.image.Pixel;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public final class ImageMapper {
|
||||
|
||||
public static BufferedImage toBufferedImage(Image image) {
|
||||
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
BufferedImage out = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
|
||||
Pixel p = image.getPixel(x, y);
|
||||
|
||||
int rgb = (p.getRed() << 16)
|
||||
| (p.getGreen() << 8)
|
||||
| p.getBlue();
|
||||
|
||||
out.setRGB(x, y, rgb);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public static Image fromBufferedImage(BufferedImage img) {
|
||||
|
||||
int width = img.getWidth();
|
||||
int height = img.getHeight();
|
||||
|
||||
Pixel[][] pixels = new Pixel[height][width];
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
|
||||
int rgb = img.getRGB(x, y);
|
||||
|
||||
int r = (rgb >> 16) & 0xFF;
|
||||
int g = (rgb >> 8) & 0xFF;
|
||||
int b = rgb & 0xFF;
|
||||
|
||||
pixels[y][x] = new Pixel(r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
return new Image(pixels);
|
||||
}
|
||||
}
|
||||
126
src/main/java/presenters/DoubleMatrixPresenter.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package presenters;
|
||||
|
||||
import app.state.DoubleMatrixState;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Inject;
|
||||
import domain.events.fourier.ImaginaryChangedEvent;
|
||||
import domain.events.fourier.ModuleChangedEvent;
|
||||
import domain.events.fourier.PhaseChangedEvent;
|
||||
import domain.events.fourier.RealChangedEvent;
|
||||
import domain.image.GrayScaleMatrix;
|
||||
import domain.image.Image;
|
||||
import domain.image.processing.complex.ComplexMatrix;
|
||||
import services.ImageService;
|
||||
import ui.interfaces.IDoubleMatrix;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Laurent Crema
|
||||
*/
|
||||
public class DoubleMatrixPresenter {
|
||||
|
||||
private IDoubleMatrix view;
|
||||
private final DoubleMatrixState state;
|
||||
private final ImageService imageService;
|
||||
|
||||
@Inject
|
||||
public DoubleMatrixPresenter(ImageService imageService, DoubleMatrixState state) {
|
||||
this.imageService = imageService;
|
||||
this.state = state;
|
||||
this.state.register(this);
|
||||
}
|
||||
|
||||
public void setView(IDoubleMatrix view){
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public void saveImage(Image image) throws IOException {
|
||||
this.imageService.saveImage(image);
|
||||
}
|
||||
|
||||
public void loadModule(){
|
||||
double[][] module = getFourier().getModule();
|
||||
state.setModule(new GrayScaleMatrix(module));
|
||||
}
|
||||
|
||||
public void loadPhase(){
|
||||
double[][] phase = getFourier().getPhase();
|
||||
state.setPhase(new GrayScaleMatrix(phase));
|
||||
}
|
||||
|
||||
public void loadReal(){
|
||||
double[][] real = getFourier().getPartieReelle();
|
||||
state.setReal(new GrayScaleMatrix(real));
|
||||
}
|
||||
|
||||
public void loadImaginary(){
|
||||
double[][] imaginary = getFourier().getPartieImaginaire();
|
||||
state.setImaginary(new GrayScaleMatrix(imaginary));
|
||||
}
|
||||
|
||||
public double getValueAt(GrayScaleMatrix matrix, double normalized){
|
||||
return matrix.getValueAt(normalized);
|
||||
}
|
||||
|
||||
public void clipModule(double black, double white){
|
||||
GrayScaleMatrix module = new GrayScaleMatrix(getFourier().getModule());
|
||||
GrayScaleMatrix matrix = clip(module, black, white);
|
||||
state.setModule(matrix);
|
||||
}
|
||||
|
||||
public void clipPhase(double black, double white){
|
||||
GrayScaleMatrix phase = new GrayScaleMatrix(getFourier().getPhase());
|
||||
GrayScaleMatrix matrix = clip(phase, black, white);
|
||||
state.setPhase(matrix);
|
||||
}
|
||||
|
||||
public void clipReal(double black, double white){
|
||||
GrayScaleMatrix real = new GrayScaleMatrix(getFourier().getPartieReelle());
|
||||
GrayScaleMatrix matrix = clip(real, black, white);
|
||||
state.setReal(matrix);
|
||||
}
|
||||
|
||||
public void clipImaginary(double black, double white){
|
||||
GrayScaleMatrix imaginary = new GrayScaleMatrix(getFourier().getPartieImaginaire());
|
||||
GrayScaleMatrix matrix = clip(imaginary, black, white);
|
||||
state.setImaginary(matrix);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onModuleUpdate(ModuleChangedEvent event) {
|
||||
GrayScaleMatrix module = new GrayScaleMatrix(getFourier().getModule());
|
||||
view.updateModule(module, event.matrix());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPhaseUpdate(PhaseChangedEvent event) {
|
||||
GrayScaleMatrix phase = new GrayScaleMatrix(getFourier().getPhase());
|
||||
view.updatePhase(phase, event.matrix());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onRealUpdate(RealChangedEvent event) {
|
||||
GrayScaleMatrix real = new GrayScaleMatrix(getFourier().getPartieReelle());
|
||||
view.updateReal(real, event.matrix());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onImaginaryUpdate(ImaginaryChangedEvent event) {
|
||||
GrayScaleMatrix imaginary = new GrayScaleMatrix(getFourier().getPartieImaginaire());
|
||||
view.updateImaginary(imaginary, event.matrix());
|
||||
}
|
||||
|
||||
private GrayScaleMatrix clip(GrayScaleMatrix matrix, double black, double white){
|
||||
double lowerBound = matrix.getValueAt(black);
|
||||
double upperBound = matrix.getValueAt(white);
|
||||
return matrix.clip(lowerBound, upperBound);
|
||||
}
|
||||
|
||||
private ComplexMatrix getFourier() {
|
||||
if (state.getFourier() == null) {
|
||||
state.setFourier(imageService.computeFourier());
|
||||
}
|
||||
return state.getFourier();
|
||||
}
|
||||
}
|
||||
67
src/main/java/presenters/MainPresenter.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package presenters;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Inject;
|
||||
import domain.common.Mode;
|
||||
import domain.events.ImageChangedEvent;
|
||||
import domain.events.ModeChangedEvent;
|
||||
import domain.image.Image;
|
||||
import services.ImageService;
|
||||
import services.ModeService;
|
||||
import ui.interfaces.IMainView;
|
||||
|
||||
/**
|
||||
* @author Laurent Crema
|
||||
*/
|
||||
public class MainPresenter {
|
||||
|
||||
private IMainView view;
|
||||
private final ModeService modeService;
|
||||
private final ImageService imageService;
|
||||
|
||||
@Inject
|
||||
public MainPresenter(ModeService modeService, ImageService imageService, EventBus eventBus) {
|
||||
this.modeService = modeService;
|
||||
this.imageService = imageService;
|
||||
eventBus.register(this);
|
||||
}
|
||||
|
||||
public void setView(IMainView view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onImageChanged(ImageChangedEvent e){
|
||||
view.displayImage(e.image());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onModeChanged(ModeChangedEvent e){
|
||||
view.changeMode(e.mode());
|
||||
}
|
||||
|
||||
public void drawPixel(int x, int y, int red, int green, int blue){
|
||||
if(modeService.getMode() != Mode.DRAW_PIXEL) return;
|
||||
Image image = imageService.getImage();
|
||||
image.setPixelColor(x, y, red, green, blue);
|
||||
imageService.setImage(image);
|
||||
}
|
||||
|
||||
public void drawShape(int x1, int x2, int y1, int y2, int red, int green, int blue){
|
||||
Image image = imageService.getImage();
|
||||
switch (modeService.getMode()) {
|
||||
case DRAW_RECTANGLE:
|
||||
image.setRectangleColor(x1, x2, y1, y2, red, green, blue);
|
||||
break;
|
||||
case DRAW_LINE:
|
||||
image.setLineColor(x1, x2, y1, y2, red, green, blue);
|
||||
break;
|
||||
case DRAW_CIRCLE:
|
||||
break;
|
||||
}
|
||||
imageService.setImage(image);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
55
src/main/java/presenters/NavPresenter.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package presenters;
|
||||
|
||||
import app.INavigator;
|
||||
import com.google.inject.Inject;
|
||||
import domain.common.Mode;
|
||||
import domain.image.GrayScaleMatrix;
|
||||
import services.ImageService;
|
||||
import services.ModeService;
|
||||
import ui.interfaces.INavBar;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Laurent Crema
|
||||
*/
|
||||
public class NavPresenter {
|
||||
|
||||
private INavBar navBar;
|
||||
private INavigator navigator;
|
||||
private final ImageService imageService;
|
||||
private final ModeService modeService;
|
||||
|
||||
@Inject
|
||||
public NavPresenter(INavigator navigator, ImageService imageService, ModeService modeService) {
|
||||
this.navigator = navigator;
|
||||
this.imageService = imageService;
|
||||
this.modeService = modeService;
|
||||
}
|
||||
|
||||
public void setView(INavBar navBar) {
|
||||
this.navBar = navBar;
|
||||
}
|
||||
|
||||
public void createImage(int red, int green, int blue, int height, int width) {
|
||||
imageService.createImage(red, green, blue, height, width);
|
||||
}
|
||||
|
||||
public void loadImage(File f) throws IOException {
|
||||
imageService.loadImage(f);
|
||||
}
|
||||
|
||||
public void goToFourier(){
|
||||
navigator.showFourier();
|
||||
}
|
||||
|
||||
public GrayScaleMatrix getImageGrayMatrix() {
|
||||
//return ((CImageNG)appState.getImage()).getMatrice();
|
||||
return imageService.getImage().toGrayScale();
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
modeService.setMode(mode);
|
||||
}
|
||||
}
|
||||
55
src/main/java/services/ImageService.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package services;
|
||||
|
||||
import app.state.AppState;
|
||||
import com.google.inject.Inject;
|
||||
import domain.image.Image;
|
||||
import domain.image.GrayScaleMatrix;
|
||||
import domain.image.processing.complex.ComplexMatrix;
|
||||
import domain.image.processing.fourier.Fourier;
|
||||
import infrastructure.image.io.ImageLoader;
|
||||
import infrastructure.image.io.ImageSaver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ImageService {
|
||||
|
||||
private final AppState appState;
|
||||
|
||||
@Inject
|
||||
public ImageService(AppState appState) {
|
||||
this.appState = appState;
|
||||
}
|
||||
|
||||
public void createImage(int red, int green, int blue, int height, int width) {
|
||||
Image image = new Image(red, green, blue, height, width);
|
||||
appState.setImage(image);
|
||||
}
|
||||
|
||||
public void loadImage(File f) throws IOException {
|
||||
Image image = ImageLoader.loadImage(f);
|
||||
appState.setImage(image);
|
||||
}
|
||||
|
||||
public void saveImage(Image image) throws IOException {
|
||||
ImageSaver.saveImage(image);
|
||||
}
|
||||
|
||||
public ComplexMatrix computeFourier(){
|
||||
GrayScaleMatrix matrix = appState.getImage().toGrayScale();
|
||||
ComplexMatrix fourier = Fourier.Fourier2D(matrix.getRawData());
|
||||
return Fourier.decroise(fourier);
|
||||
}
|
||||
|
||||
public Image getImage(){
|
||||
return appState.getImage();
|
||||
}
|
||||
|
||||
public void setImage(Image img){
|
||||
appState.setImage(img);
|
||||
}
|
||||
|
||||
public GrayScaleMatrix getGrayScale(){
|
||||
return appState.getImage().toGrayScale();
|
||||
}
|
||||
}
|
||||
22
src/main/java/services/ModeService.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package services;
|
||||
|
||||
import app.state.AppState;
|
||||
import com.google.inject.Inject;
|
||||
import domain.common.Mode;
|
||||
|
||||
public class ModeService {
|
||||
|
||||
private final AppState appState;
|
||||
|
||||
@Inject
|
||||
public ModeService(AppState appState) {
|
||||
this.appState = appState;
|
||||
}
|
||||
|
||||
public void setMode(Mode m){
|
||||
appState.setMode(m);
|
||||
}
|
||||
|
||||
public Mode getMode() { return this.appState.getMode(); }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package ui.implementation.components.image;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
import domain.image.Image;
|
||||
import infrastructure.ui.ImageMapper;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
|
||||
public class ImagePanel extends JPanel {
|
||||
private BufferedImage image;
|
||||
private double scale;
|
||||
private int drawW;
|
||||
private int drawH;
|
||||
private int offsetX;
|
||||
private int offsetY;
|
||||
|
||||
public ImagePanel() { }
|
||||
|
||||
public ImagePanel(BufferedImage image) {
|
||||
loadImage(image);
|
||||
}
|
||||
|
||||
public void loadImage(BufferedImage image){
|
||||
this.image = image;
|
||||
setPreferredSize(new Dimension(image.getWidth(null), image.getHeight(null)));
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void loadImage(GrayScaleMatrix matrix){
|
||||
this.image = ImageMapper.toBufferedImage(matrix.toImage());
|
||||
setPreferredSize(new Dimension(this.image.getWidth(null), this.image.getHeight(null)));
|
||||
repaint();
|
||||
}
|
||||
|
||||
public Point toImageCoordinates(int x, int y) {
|
||||
if (image == null) return null;
|
||||
|
||||
if (x < offsetX || x >= offsetX + drawW ||
|
||||
y < offsetY || y >= offsetY + drawH) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int imgX = (int) ((x - offsetX) / scale);
|
||||
int imgY = (int) ((y - offsetY) / scale);
|
||||
|
||||
return new Point(imgX, imgY);
|
||||
}
|
||||
|
||||
private void computeViewport() {
|
||||
if (image == null) return;
|
||||
|
||||
int imgW = image.getWidth();
|
||||
int imgH = image.getHeight();
|
||||
|
||||
scale = Math.min(
|
||||
(double) getWidth() / imgW,
|
||||
(double) getHeight() / imgH
|
||||
);
|
||||
|
||||
drawW = (int) (imgW * scale);
|
||||
drawH = (int) (imgH * scale);
|
||||
|
||||
offsetX = (getWidth() - drawW) / 2;
|
||||
offsetY = (getHeight() - drawH) / 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
if (image == null) return;
|
||||
computeViewport();
|
||||
g.drawImage(image, offsetX, offsetY, drawW, drawH, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ui.implementation.components.intent;
|
||||
|
||||
import javax.swing.*;
|
||||
public class FileChooser extends JFileChooser {
|
||||
|
||||
public FileChooser(String initialLocation) {
|
||||
super(initialLocation);
|
||||
}
|
||||
}
|
||||
32
src/main/java/ui/implementation/components/nav/Menu.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package ui.implementation.components.nav;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.net.URL;
|
||||
|
||||
public class Menu extends JMenu {
|
||||
|
||||
public Menu(String title, JMenuItem... items)
|
||||
{
|
||||
this(title,null, items);
|
||||
}
|
||||
|
||||
public Menu(String title, String iconPath, JMenuItem... items)
|
||||
{
|
||||
super(title);
|
||||
|
||||
if (iconPath != null) {
|
||||
URL url = getClass().getResource(iconPath);
|
||||
if (url != null) {
|
||||
setIcon(new ImageIcon(url));
|
||||
}
|
||||
}
|
||||
|
||||
if (items != null) {
|
||||
for (JMenuItem item : items) {
|
||||
if (item != null) {
|
||||
this.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/main/java/ui/implementation/components/nav/MenuItem.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package ui.implementation.components.nav;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.net.URL;
|
||||
|
||||
public class MenuItem extends JMenuItem {
|
||||
|
||||
public MenuItem(String title, ActionListener listener) {
|
||||
this(title, null, listener);
|
||||
}
|
||||
|
||||
public MenuItem(String title, String iconPath, ActionListener listener) {
|
||||
super(title);
|
||||
if(iconPath != null) {
|
||||
URL url = getClass().getResource(iconPath);
|
||||
if (url != null) {
|
||||
super.setIcon(new ImageIcon(url));
|
||||
}
|
||||
}
|
||||
if (listener != null) {
|
||||
addActionListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
301
src/main/java/ui/implementation/components/nav/NavBar.java
Normal file
@@ -0,0 +1,301 @@
|
||||
package ui.implementation.components.nav;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
import domain.image.processing.histogram.Histogramme;
|
||||
import domain.common.Mode;
|
||||
import jakarta.inject.Inject;
|
||||
import org.jfree.chart.ChartFactory;
|
||||
import org.jfree.chart.ChartFrame;
|
||||
import org.jfree.chart.JFreeChart;
|
||||
import org.jfree.chart.axis.ValueAxis;
|
||||
import org.jfree.chart.plot.PlotOrientation;
|
||||
import org.jfree.chart.plot.XYPlot;
|
||||
import org.jfree.data.xy.XYSeries;
|
||||
import org.jfree.data.xy.XYSeriesCollection;
|
||||
import presenters.NavPresenter;
|
||||
import ui.implementation.components.intent.FileChooser;
|
||||
import ui.implementation.dialogs.ImageCreatorDialog;
|
||||
import ui.implementation.dialogs.GreyScaleImageCreatorDialog;
|
||||
import ui.implementation.dialogs.RGBImageCreatorDialog;
|
||||
import ui.interfaces.INavBar;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Jean-Marc Wagner, Laurent Crema
|
||||
*/
|
||||
public class NavBar extends JMenuBar implements INavBar {
|
||||
|
||||
private JMenu imageMenu;
|
||||
private JMenu drawingMenu;
|
||||
private JMenu fourierMenu;
|
||||
private JMenu linearMenu;
|
||||
private JMenu histogramMenu;
|
||||
|
||||
private NavPresenter presenter;
|
||||
|
||||
@Inject
|
||||
public NavBar(NavPresenter presenter) {
|
||||
this.presenter = presenter;
|
||||
this.presenter.setView(this);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
private void initComponents(){
|
||||
|
||||
imageMenu = new Menu("Image","/net_13_p1.jpg",
|
||||
new Menu("Nouvelle", "/file_65_p3.jpg",
|
||||
new MenuItem("RGB", e -> this.createImage(e, new RGBImageCreatorDialog(new JFrame(),true))),
|
||||
new MenuItem("NG", e -> this.createImage(e, new GreyScaleImageCreatorDialog(new JFrame(),true)))
|
||||
),
|
||||
new MenuItem("Ouvrir...", "/folder_036_p3.jpg", this::loadImage),
|
||||
new MenuItem("Enregistrer sous...","/dd_27_p3.jpg", e -> {}),
|
||||
new MenuItem("Quitter","/cp_59_p3.jpg", this::quit)
|
||||
);
|
||||
|
||||
drawingMenu = new Menu("Editer","/dd_28_p1.jpg",
|
||||
new Menu("Dessiner", "/display_14_p3.jpg",
|
||||
new MenuItem("Couleur", this::chooseColor),
|
||||
new Menu("Formes",
|
||||
new MenuItem("Pixel", e -> this.setMode(e, Mode.DRAW_PIXEL)),
|
||||
new MenuItem("Ligne", e -> this.setMode(e, Mode.DRAW_LINE)),
|
||||
new MenuItem("Rectangle",e -> this.setMode(e, Mode.DRAW_RECTANGLE)),
|
||||
new MenuItem("Cercle", e -> this.setMode(e, Mode.DRAW_CIRCLE))
|
||||
)
|
||||
),
|
||||
new Menu("Convertir",
|
||||
new MenuItem("RGB", e -> {}),
|
||||
new MenuItem("NG", e -> {})
|
||||
)
|
||||
);
|
||||
|
||||
fourierMenu = new Menu("Fourier","/cp_51_p1.jpg",
|
||||
new MenuItem("Afficher", "/cp_51_p3.jpg", this::displayFourier)
|
||||
);
|
||||
|
||||
linearMenu = new Menu("Linéaire","/cp_51_p1.jpg",
|
||||
new Menu("Global",
|
||||
new Menu("Formes",
|
||||
new MenuItem("PasseBasIdeal", e -> this.setMode(e, Mode.DRAW_PIXEL)),
|
||||
new MenuItem("PasseHautIdeal", e -> this.setMode(e, Mode.DRAW_LINE)),
|
||||
new MenuItem("PasseBasButterworth",e -> this.setMode(e, Mode.DRAW_RECTANGLE)),
|
||||
new MenuItem("PasseHautButterworth", e -> this.setMode(e, Mode.DRAW_CIRCLE))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
histogramMenu = new Menu("Histogramme","/report_48_hot.jpg",
|
||||
new MenuItem("Afficher", "/report_32_hot.jpg",this::displayHistogram)
|
||||
);
|
||||
|
||||
this.add(imageMenu);
|
||||
this.add(drawingMenu);
|
||||
this.add(fourierMenu);
|
||||
this.add(linearMenu);
|
||||
this.add(histogramMenu);
|
||||
|
||||
drawingMenu.setEnabled(false);
|
||||
fourierMenu.setEnabled(false);
|
||||
linearMenu.setEnabled(false);
|
||||
histogramMenu.setEnabled(false);
|
||||
}
|
||||
|
||||
//####################################################
|
||||
|
||||
private File chooseFile(){
|
||||
FileChooser chooser = new FileChooser("./");
|
||||
|
||||
int dialogResult = chooser.showOpenDialog(this);
|
||||
if (dialogResult != JFileChooser.APPROVE_OPTION) return null;
|
||||
|
||||
return chooser.getSelectedFile();
|
||||
}
|
||||
|
||||
private void loadImage(ActionEvent e) {
|
||||
try
|
||||
{
|
||||
File file = chooseFile();
|
||||
if(file == null) return;
|
||||
presenter.loadImage(file);
|
||||
activeMenusRGB();
|
||||
activeMenusNG();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
System.err.println("Erreur I/O : " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void quit(ActionEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private void createImage(ActionEvent e, ImageCreatorDialog dialog) {
|
||||
dialog.setVisible(true);
|
||||
presenter.createImage(
|
||||
dialog.getRed(), dialog.getGreen(), dialog.getBlue(),
|
||||
dialog.getImageHeight(), dialog.getImageWidth());
|
||||
activeMenusRGB();
|
||||
activeMenusNG();
|
||||
}
|
||||
|
||||
private void setMode(ActionEvent e, Mode mode) {
|
||||
presenter.setMode(mode);
|
||||
}
|
||||
|
||||
private void chooseColor(ActionEvent e) {
|
||||
/*if (imageRGB != null)
|
||||
{
|
||||
Color newC = JColorChooser.showDialog(this,"Couleur du pinceau", couleurPinceauRGB);
|
||||
if (newC != null) couleurPinceauRGB = newC;
|
||||
observer.setCouleurPinceau(couleurPinceauRGB);
|
||||
}
|
||||
|
||||
if (imageNG != null)
|
||||
{
|
||||
GreyScalePicker dialog = new GreyScalePicker(new JFrame(),true, couleurPinceauNG);
|
||||
dialog.setVisible(true);
|
||||
couleurPinceauNG = dialog.getCouleur();
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
private void activeMenusNG()
|
||||
{
|
||||
drawingMenu.setEnabled(true);
|
||||
fourierMenu.setEnabled(true);
|
||||
linearMenu.setEnabled(true);
|
||||
histogramMenu.setEnabled(true);
|
||||
}
|
||||
|
||||
private void activeMenusRGB()
|
||||
{
|
||||
drawingMenu.setEnabled(true);
|
||||
fourierMenu.setEnabled(false);
|
||||
linearMenu.setEnabled(false);
|
||||
histogramMenu.setEnabled(false);
|
||||
}
|
||||
|
||||
//####################################################
|
||||
|
||||
private void displayHistogram(ActionEvent e) {
|
||||
GrayScaleMatrix i_graymatrix = presenter.getImageGrayMatrix();
|
||||
int[] histo = Histogramme.Histogramme256(i_graymatrix);
|
||||
|
||||
//Création du dataset
|
||||
XYSeries serie = new XYSeries("Histo");
|
||||
for(int i=0 ; i<256 ; i++) serie.add(i,histo[i]);
|
||||
XYSeriesCollection dataset = new XYSeriesCollection();
|
||||
dataset.addSeries(serie);
|
||||
|
||||
// Creation du chart
|
||||
JFreeChart chart = ChartFactory.createHistogram("Histogramme","Niveaux de gris",
|
||||
"Nombre de pixels", dataset, PlotOrientation.VERTICAL,false,false,false);
|
||||
|
||||
XYPlot plot = chart.getXYPlot();
|
||||
ValueAxis axeX = plot.getDomainAxis();
|
||||
axeX.setRange(0,255);
|
||||
plot.setDomainAxis(axeX);
|
||||
|
||||
// creation d'une frame
|
||||
ChartFrame frame = new ChartFrame("Histogramme de l'image",chart);
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private void jMenuItemFourierAfficherPartieImaginaireActionPerformed(ActionEvent e) {
|
||||
// try
|
||||
// {
|
||||
// int f_int[][] = imageNG.getMatrice();
|
||||
// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()];
|
||||
// for(int i=0 ; i<imageNG.getLargeur() ; i++)
|
||||
// for(int j=0 ; j<imageNG.getHauteur() ; j++) f[i][j] = (double)(f_int[i][j]);
|
||||
//
|
||||
// System.out.println("Debut Fourier");
|
||||
// MatriceComplexe fourier = Fourier.Fourier2D(f);
|
||||
// System.out.println("Fin Fourier");
|
||||
// fourier = Fourier.decroise(fourier);
|
||||
// double partieImaginaire[][] = fourier.getPartieImaginaire();
|
||||
//
|
||||
// DoubleMatrix dialog = new DoubleMatrix(this,true,partieImaginaire,"Fourier : Affichage de la partie imaginaire");
|
||||
// dialog.setVisible(true);
|
||||
// }
|
||||
// catch (CImageNGException ex)
|
||||
// {
|
||||
// System.out.println("Erreur CImageNG : " + ex.getMessage());
|
||||
// }
|
||||
}
|
||||
|
||||
private void jMenuItemFourierAfficherPartieReelleActionPerformed(ActionEvent e) {
|
||||
// try
|
||||
// {
|
||||
// int f_int[][] = imageNG.getMatrice();
|
||||
// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()];
|
||||
// for(int i=0 ; i<imageNG.getLargeur() ; i++)
|
||||
// for(int j=0 ; j<imageNG.getHauteur() ; j++) f[i][j] = (double)(f_int[i][j]);
|
||||
//
|
||||
// System.out.println("Debut Fourier");
|
||||
// MatriceComplexe fourier = Fourier.Fourier2D(f);
|
||||
// System.out.println("Fin Fourier");
|
||||
// fourier = Fourier.decroise(fourier);
|
||||
// double partieReelle[][] = fourier.getPartieReelle();
|
||||
//
|
||||
// DoubleMatrix dialog = new DoubleMatrix(this,true,partieReelle,"Fourier : Affichage de la partie reelle");
|
||||
// dialog.setVisible(true);
|
||||
// }
|
||||
// catch (CImageNGException ex)
|
||||
// {
|
||||
// System.out.println("Erreur CImageNG : " + ex.getMessage());
|
||||
// }
|
||||
}
|
||||
|
||||
private void jMenuItemFourierAfficherPhaseActionPerformed(ActionEvent e) {
|
||||
// try
|
||||
// {
|
||||
// int f_int[][] = imageNG.getMatrice();
|
||||
// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()];
|
||||
// for(int i=0 ; i<imageNG.getLargeur() ; i++)
|
||||
// for(int j=0 ; j<imageNG.getHauteur() ; j++) f[i][j] = (double)(f_int[i][j]);
|
||||
//
|
||||
// System.out.println("Debut Fourier");
|
||||
// MatriceComplexe fourier = Fourier.Fourier2D(f);
|
||||
// System.out.println("Fin Fourier");
|
||||
// fourier = Fourier.decroise(fourier);
|
||||
// double phase[][] = fourier.getPhase();
|
||||
//
|
||||
// DoubleMatrix dialog = new DoubleMatrix(this,true,phase,"Fourier : Affichage de la phase");
|
||||
// dialog.setVisible(true);
|
||||
// }
|
||||
// catch (CImageNGException ex)
|
||||
// {
|
||||
// System.out.println("Erreur CImageNG : " + ex.getMessage());
|
||||
// }
|
||||
}
|
||||
|
||||
private void displayFourier(ActionEvent e) {
|
||||
try
|
||||
{
|
||||
presenter.goToFourier();
|
||||
// int f_int[][] = imageNG.getMatrice();
|
||||
// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()];
|
||||
// for(int i=0 ; i<imageNG.getLargeur() ; i++)
|
||||
// for(int j=0 ; j<imageNG.getHauteur() ; j++) f[i][j] = (double)(f_int[i][j]);
|
||||
//
|
||||
// System.out.println("Debut Fourier");
|
||||
// MatriceComplexe fourier = Fourier.Fourier2D(f);
|
||||
// System.out.println("Fin Fourier");
|
||||
// fourier = Fourier.decroise(fourier);
|
||||
// double module[][] = fourier.getModule();
|
||||
//
|
||||
// DoubleMatrix dialog = new DoubleMatrix(this,true,module,"Fourier : Affichage du module");
|
||||
// dialog.setVisible(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.out.println("Erreur CImageNG : " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package ui.implementation.dialogs;
|
||||
|
||||
import java.awt.Color;
|
||||
import javax.swing.*;
|
||||
|
||||
public class GreyScaleImageCreatorDialog extends ImageCreatorDialog
|
||||
{
|
||||
private JSlider slider;
|
||||
|
||||
public GreyScaleImageCreatorDialog(java.awt.Frame parent, boolean modal)
|
||||
{
|
||||
super(parent, modal);
|
||||
createColorPicker();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createColorPicker() {
|
||||
slider = new JSlider();
|
||||
slider.setValue(getValue());
|
||||
slider.setMajorTickSpacing(255);
|
||||
slider.setMaximum(255);
|
||||
slider.setMinorTickSpacing(15);
|
||||
slider.setPaintLabels(true);
|
||||
slider.setPaintTicks(true);
|
||||
slider.addChangeListener(this::jSliderStateChanged);
|
||||
colorPickerContainer.add(slider);
|
||||
}
|
||||
|
||||
private int getValue(){
|
||||
return color.getRed();
|
||||
}
|
||||
|
||||
private void jSliderStateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
int value = slider.getValue();
|
||||
color = new Color(value, value, value);
|
||||
colorVisualizer.setBackground(color);
|
||||
}
|
||||
}
|
||||
122
src/main/java/ui/implementation/dialogs/GreyScalePicker.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package ui.implementation.dialogs;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public class GreyScalePicker extends javax.swing.JDialog
|
||||
{
|
||||
private int couleur;
|
||||
|
||||
/** Creates new form JDialogChoixCouleurNG */
|
||||
public GreyScalePicker(java.awt.Frame parent, boolean modal, int c)
|
||||
{
|
||||
super(parent, modal);
|
||||
couleur = c;
|
||||
|
||||
initComponents();
|
||||
couleur = c;
|
||||
jSlider.setValue(couleur);
|
||||
jPanel.setBackground(new Color(couleur,couleur,couleur));
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
|
||||
private void initComponents() {
|
||||
jSlider = new javax.swing.JSlider();
|
||||
jPanel = new javax.swing.JPanel();
|
||||
jButtonOk = new javax.swing.JButton();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setTitle("Choix du niveau de gris");
|
||||
setResizable(false);
|
||||
jSlider.setMajorTickSpacing(255);
|
||||
jSlider.setMaximum(255);
|
||||
jSlider.setMinorTickSpacing(15);
|
||||
jSlider.setPaintLabels(true);
|
||||
jSlider.setPaintTicks(true);
|
||||
jSlider.addChangeListener(new javax.swing.event.ChangeListener() {
|
||||
public void stateChanged(javax.swing.event.ChangeEvent evt) {
|
||||
jSliderStateChanged(evt);
|
||||
}
|
||||
});
|
||||
|
||||
jPanel.setBorder(javax.swing.BorderFactory.createCompoundBorder(null, javax.swing.BorderFactory.createLineBorder(new Color(255, 0, 0))));
|
||||
javax.swing.GroupLayout jPanelLayout = new javax.swing.GroupLayout(jPanel);
|
||||
jPanel.setLayout(jPanelLayout);
|
||||
jPanelLayout.setHorizontalGroup(
|
||||
jPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 62, Short.MAX_VALUE)
|
||||
);
|
||||
jPanelLayout.setVerticalGroup(
|
||||
jPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 45, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
jButtonOk.setText("Ok");
|
||||
jButtonOk.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
jButtonOkActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(19, 19, 19)
|
||||
.addComponent(jPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE)
|
||||
.addComponent(jButtonOk, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||
.addComponent(jButtonOk, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 47, Short.MAX_VALUE)
|
||||
.addComponent(jPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(jSlider, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
pack();
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void jButtonOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButtonOkActionPerformed
|
||||
couleur = jSlider.getValue();
|
||||
setVisible(false);
|
||||
dispose();
|
||||
}//GEN-LAST:event_jButtonOkActionPerformed
|
||||
|
||||
private void jSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSliderStateChanged
|
||||
int valeur = jSlider.getValue();
|
||||
Color c = new Color(valeur,valeur,valeur);
|
||||
jPanel.setBackground(c);
|
||||
}//GEN-LAST:event_jSliderStateChanged
|
||||
|
||||
public int getCouleur() { return couleur; }
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
new GreyScalePicker(new javax.swing.JFrame(), true, 128).setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton jButtonOk;
|
||||
private javax.swing.JPanel jPanel;
|
||||
private javax.swing.JSlider jSlider;
|
||||
// End of variables declaration//GEN-END:variables
|
||||
|
||||
}
|
||||
167
src/main/java/ui/implementation/dialogs/ImageCreatorDialog.java
Normal file
@@ -0,0 +1,167 @@
|
||||
package ui.implementation.dialogs;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
public abstract class ImageCreatorDialog extends JDialog
|
||||
{
|
||||
protected Color color;
|
||||
protected int height;
|
||||
protected int width;
|
||||
|
||||
protected JButton confirmButton;
|
||||
protected JPanel colorVisualizer;
|
||||
protected JComponent colorPickerContainer;
|
||||
|
||||
protected JTextField heightInput;
|
||||
protected JTextField widthInput;
|
||||
|
||||
public ImageCreatorDialog(Frame parent, boolean modal)
|
||||
{
|
||||
super(parent, modal);
|
||||
initComponents();
|
||||
|
||||
color = new Color(255,255,255);
|
||||
width = 256;
|
||||
height = 256;
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
colorVisualizer = new JPanel();
|
||||
confirmButton = new JButton();
|
||||
widthInput = new JTextField();
|
||||
heightInput = new JTextField();
|
||||
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
setTitle("Choix de la couleur");
|
||||
setResizable(false);
|
||||
|
||||
GroupLayout jPanelLayout = new GroupLayout(colorVisualizer);
|
||||
colorVisualizer.setBorder(
|
||||
BorderFactory.createCompoundBorder(null,
|
||||
BorderFactory.createLineBorder(new Color(255, 0, 0)))
|
||||
);
|
||||
colorVisualizer.setLayout(jPanelLayout);
|
||||
|
||||
jPanelLayout.setHorizontalGroup(
|
||||
jPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 62, Short.MAX_VALUE)
|
||||
);
|
||||
jPanelLayout.setVerticalGroup(
|
||||
jPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addGap(0, 55, Short.MAX_VALUE)
|
||||
);
|
||||
|
||||
confirmButton.setText("Ok");
|
||||
confirmButton.addActionListener(this::onConfirmClicked);
|
||||
|
||||
widthInput.setHorizontalAlignment(JTextField.CENTER);
|
||||
|
||||
heightInput.setHorizontalAlignment(JTextField.CENTER);
|
||||
|
||||
JLabel widthLabel = new JLabel("Largeur");
|
||||
JLabel heightLabel = new JLabel("Hauteur");
|
||||
|
||||
colorPickerContainer = new JPanel(new BorderLayout());
|
||||
|
||||
GroupLayout layout = new GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addGap(27, 27, 27)
|
||||
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(widthLabel)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(widthInput, GroupLayout.PREFERRED_SIZE, 97, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(37, 37, 37)
|
||||
.addComponent(heightLabel)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(heightInput, GroupLayout.PREFERRED_SIZE, 96, GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(51, Short.MAX_VALUE))
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(colorVisualizer, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(25, 25, 25)
|
||||
.addComponent(colorPickerContainer)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 135, Short.MAX_VALUE)
|
||||
.addComponent(confirmButton, GroupLayout.PREFERRED_SIZE, 65, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(26, 26, 26))))
|
||||
);
|
||||
|
||||
layout.linkSize(SwingConstants.HORIZONTAL, heightInput, widthInput);
|
||||
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addGap(24, 24, 24)
|
||||
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(heightInput, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(heightLabel)
|
||||
.addComponent(widthInput, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(widthLabel)
|
||||
)
|
||||
.addGap(37, 37, 37)
|
||||
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(confirmButton, GroupLayout.DEFAULT_SIZE, 57, Short.MAX_VALUE)
|
||||
.addComponent(colorPickerContainer, GroupLayout.DEFAULT_SIZE, 57, Short.MAX_VALUE))
|
||||
.addComponent(colorVisualizer, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
.addContainerGap())
|
||||
);
|
||||
pack();
|
||||
colorVisualizer.setBackground(color);
|
||||
widthInput.setText("256");
|
||||
heightInput.setText("256");
|
||||
}
|
||||
|
||||
protected void onColorChosen(ActionEvent e) {
|
||||
Color color = JColorChooser.showDialog(this,"Couleur de fond", this.color);
|
||||
if (color == null) return;
|
||||
this.color = color;
|
||||
colorVisualizer.setBackground(this.color);
|
||||
}
|
||||
|
||||
protected void onConfirmClicked(ActionEvent e) {
|
||||
try
|
||||
{
|
||||
height = Integer.parseInt(widthInput.getText());
|
||||
width = Integer.parseInt(heightInput.getText());
|
||||
|
||||
setVisible(false);
|
||||
dispose();
|
||||
} catch(NumberFormatException nfe)
|
||||
{
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Hauteur et Largeur doivent etre entiers !","Erreur",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
public Color getImageColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public int getRed() {
|
||||
return color.getRed();
|
||||
}
|
||||
|
||||
public int getGreen() {
|
||||
return color.getGreen();
|
||||
}
|
||||
|
||||
public int getBlue() {
|
||||
return color.getBlue();
|
||||
}
|
||||
|
||||
public int getImageWidth(){
|
||||
return width;
|
||||
}
|
||||
|
||||
public int getImageHeight(){
|
||||
return height;
|
||||
}
|
||||
|
||||
protected abstract void createColorPicker();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ui.implementation.dialogs;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class RGBImageCreatorDialog extends ImageCreatorDialog
|
||||
{
|
||||
|
||||
private JButton colorPickerButton;
|
||||
|
||||
public RGBImageCreatorDialog(Frame parent, boolean modal)
|
||||
{
|
||||
super(parent, modal);
|
||||
createColorPicker();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createColorPicker() {
|
||||
colorPickerButton = new JButton();
|
||||
colorPickerButton.setText("Choisir");
|
||||
colorPickerButton.addActionListener(this::onColorChosen);
|
||||
colorPickerContainer.add(colorPickerButton);
|
||||
}
|
||||
}
|
||||
374
src/main/java/ui/implementation/views/DoubleMatrix.java
Normal file
@@ -0,0 +1,374 @@
|
||||
package ui.implementation.views;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
import domain.image.Image;
|
||||
import jakarta.inject.Inject;
|
||||
import presenters.DoubleMatrixPresenter;
|
||||
import ui.implementation.components.image.ImagePanel;
|
||||
import ui.implementation.components.intent.FileChooser;
|
||||
import ui.interfaces.IDoubleMatrix;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.*;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
|
||||
/**
|
||||
* @author Jean-Marc Wagner, Laurent Crema
|
||||
*/
|
||||
public class DoubleMatrix extends JFrame implements IDoubleMatrix
|
||||
{
|
||||
private double normalizedWhite;
|
||||
private double normalizedBlack;
|
||||
|
||||
private boolean isUpdating = false;
|
||||
|
||||
private JTabbedPane modeTab;
|
||||
private ImagePanel modulePreviewContainer;
|
||||
private ImagePanel phasePreviewContainer;
|
||||
private ImagePanel realPreviewContainer;
|
||||
private ImagePanel imaginaryPreviewContainer;
|
||||
|
||||
private JSlider whiteSlider;
|
||||
private JTextField whiteTextField;
|
||||
private JLabel maxLabel;
|
||||
|
||||
private JSlider blackSlider;
|
||||
private JTextField blackTextField;
|
||||
private JLabel minLabel;
|
||||
|
||||
private final DoubleMatrixPresenter presenter;
|
||||
|
||||
@Inject
|
||||
public DoubleMatrix(DoubleMatrixPresenter presenter)
|
||||
{
|
||||
this.presenter = presenter;
|
||||
initComponents();
|
||||
presenter.setView(this);
|
||||
initValues();
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
|
||||
modulePreviewContainer = new ImagePanel();
|
||||
phasePreviewContainer = new ImagePanel();
|
||||
realPreviewContainer = new ImagePanel();
|
||||
imaginaryPreviewContainer = new ImagePanel();
|
||||
|
||||
blackSlider = buildSlider();
|
||||
whiteSlider = buildSlider();
|
||||
|
||||
blackTextField = buildTextField(Color.BLACK);
|
||||
whiteTextField = buildTextField(null);
|
||||
|
||||
minLabel = buildValueLabel();
|
||||
maxLabel = buildValueLabel();
|
||||
JLabel minValueText = new JLabel("Valeur MIN :");
|
||||
JLabel maxValueText = new JLabel("Valeur MAX :");
|
||||
|
||||
JButton saveButton = new JButton(new ImageIcon(Objects.requireNonNull(getClass().getResource("/dd_27_p3.jpg"))));
|
||||
saveButton.addActionListener(this::saveImage);
|
||||
|
||||
blackSlider.addChangeListener(this::onValueUpdate);
|
||||
whiteSlider.addChangeListener(this::onValueUpdate);
|
||||
|
||||
blackTextField.addActionListener(this::jTextFieldNoirActionPerformed);
|
||||
whiteTextField.addActionListener(this::jTextFieldBlancActionPerformed);
|
||||
|
||||
modeTab = buildModeTab();
|
||||
buildLayout(modeTab, maxValueText, minValueText, minLabel, maxLabel, saveButton);
|
||||
|
||||
setTitle("Fourier");
|
||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||
pack();
|
||||
}
|
||||
|
||||
private void initValues(){
|
||||
modeTab.setSelectedIndex(1);
|
||||
|
||||
blackSlider.setValue(0);
|
||||
whiteSlider.setValue(1000);
|
||||
}
|
||||
|
||||
private JSlider buildSlider() {
|
||||
return new JSlider(JSlider.VERTICAL);
|
||||
}
|
||||
|
||||
private JTextField buildTextField(Color background) {
|
||||
JTextField field = new JTextField();
|
||||
field.setFont(new Font("Tahoma", Font.BOLD, 11));
|
||||
field.setForeground(Color.RED);
|
||||
field.setHorizontalAlignment(JTextField.CENTER);
|
||||
if (background != null) field.setBackground(background);
|
||||
return field;
|
||||
}
|
||||
|
||||
private JLabel buildValueLabel() {
|
||||
JLabel label = new JLabel("0.0");
|
||||
label.setFont(new Font("Tahoma", Font.BOLD, 11));
|
||||
label.setForeground(Color.BLUE);
|
||||
return label;
|
||||
}
|
||||
|
||||
private JTabbedPane buildModeTab(){
|
||||
|
||||
JTabbedPane modeTabs = new JTabbedPane();
|
||||
modeTabs.addTab("Module", modulePreviewContainer);
|
||||
modeTabs.addTab("Phase", phasePreviewContainer);
|
||||
modeTabs.addTab("Partie réelle", realPreviewContainer);
|
||||
modeTabs.addTab("Partie imaginaire", imaginaryPreviewContainer);
|
||||
modeTabs.addChangeListener(e -> {
|
||||
switch (modeTabs.getSelectedIndex()) {
|
||||
case 0 -> presenter.loadModule();
|
||||
case 1 -> presenter.loadPhase();
|
||||
case 2 -> presenter.loadReal();
|
||||
case 3 -> presenter.loadImaginary();
|
||||
}
|
||||
});
|
||||
return modeTabs;
|
||||
}
|
||||
|
||||
private void buildLayout(JTabbedPane modeTab,
|
||||
JLabel labelMax, JLabel labelMin,
|
||||
JLabel valeurMin, JLabel valeurMax,
|
||||
JButton saveButton) {
|
||||
|
||||
JPanel rightPanel = new JPanel();
|
||||
GroupLayout rightLayout = new GroupLayout(rightPanel);
|
||||
rightPanel.setLayout(rightLayout);
|
||||
|
||||
rightLayout.setHorizontalGroup(
|
||||
rightLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addComponent(whiteTextField, GroupLayout.PREFERRED_SIZE, 90, GroupLayout.PREFERRED_SIZE)
|
||||
.addGroup(rightLayout.createSequentialGroup()
|
||||
.addComponent(blackSlider)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(whiteSlider)
|
||||
)
|
||||
.addComponent(blackTextField, GroupLayout.PREFERRED_SIZE, 90, GroupLayout.PREFERRED_SIZE)
|
||||
);
|
||||
|
||||
rightLayout.setVerticalGroup(
|
||||
rightLayout.createSequentialGroup()
|
||||
.addComponent(whiteTextField, GroupLayout.PREFERRED_SIZE,
|
||||
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER)
|
||||
.addComponent(blackSlider)
|
||||
.addComponent(whiteSlider)
|
||||
)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(blackTextField, GroupLayout.PREFERRED_SIZE,
|
||||
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
);
|
||||
|
||||
// -------------------------
|
||||
// MAIN LAYOUT
|
||||
// -------------------------
|
||||
GroupLayout layout = new GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
|
||||
// TOP BAR
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(labelMin)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(valeurMin)
|
||||
.addGap(50)
|
||||
.addComponent(labelMax)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(valeurMax)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED,
|
||||
GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(saveButton)
|
||||
.addContainerGap()
|
||||
)
|
||||
|
||||
// MAIN ROW
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(modeTab, 400, 400, Short.MAX_VALUE)
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addComponent(rightPanel, GroupLayout.PREFERRED_SIZE,
|
||||
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap()
|
||||
)
|
||||
);
|
||||
|
||||
layout.setVerticalGroup(
|
||||
layout.createSequentialGroup()
|
||||
|
||||
// TOP BAR
|
||||
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(labelMin)
|
||||
.addComponent(valeurMin)
|
||||
.addComponent(labelMax)
|
||||
.addComponent(valeurMax)
|
||||
.addComponent(saveButton)
|
||||
)
|
||||
|
||||
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
|
||||
|
||||
// MAIN CONTENT
|
||||
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addComponent(modeTab)
|
||||
.addComponent(rightPanel)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private File chooseFile(){
|
||||
FileChooser chooser = new FileChooser("./");
|
||||
|
||||
int dialogResult = chooser.showOpenDialog(this);
|
||||
if (dialogResult != JFileChooser.APPROVE_OPTION) return null;
|
||||
|
||||
return chooser.getSelectedFile();
|
||||
}
|
||||
|
||||
private void onValueUpdate(ChangeEvent e) {
|
||||
if (isUpdating) return;
|
||||
isUpdating = true;
|
||||
|
||||
try {
|
||||
int black = blackSlider.getValue();
|
||||
int white = whiteSlider.getValue();
|
||||
|
||||
int minGap = 1;
|
||||
if (white - black < minGap) {
|
||||
if (blackSlider.getValueIsAdjusting()) {
|
||||
black = white - minGap;
|
||||
blackSlider.setValue(black);
|
||||
} else {
|
||||
white = black + minGap;
|
||||
whiteSlider.setValue(white);
|
||||
}
|
||||
}
|
||||
|
||||
normalizedBlack = (double) black / blackSlider.getMaximum();
|
||||
normalizedWhite = (double) white / whiteSlider.getMaximum();
|
||||
|
||||
switch (modeTab.getSelectedIndex()) {
|
||||
case 0 -> presenter.clipModule(normalizedBlack, normalizedWhite);
|
||||
case 1 -> presenter.clipPhase(normalizedBlack, normalizedWhite);
|
||||
case 2 -> presenter.clipReal(normalizedBlack, normalizedWhite);
|
||||
case 3 -> presenter.clipImaginary(normalizedBlack, normalizedWhite);
|
||||
}
|
||||
|
||||
} finally {
|
||||
isUpdating = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateModule(GrayScaleMatrix source, GrayScaleMatrix clipped) {
|
||||
updatePreview(modulePreviewContainer, source, clipped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePhase(GrayScaleMatrix source, GrayScaleMatrix clipped) {
|
||||
updatePreview(phasePreviewContainer, source, clipped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateReal(GrayScaleMatrix source, GrayScaleMatrix clipped) {
|
||||
updatePreview(realPreviewContainer, source, clipped);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateImaginary(GrayScaleMatrix source, GrayScaleMatrix clipped) {
|
||||
updatePreview(imaginaryPreviewContainer, source, clipped);
|
||||
}
|
||||
|
||||
private void updatePreview(ImagePanel imagePanel, GrayScaleMatrix source, GrayScaleMatrix matrix) {
|
||||
try {
|
||||
imagePanel.loadImage(matrix);
|
||||
updateLabels(source.getMinValue(), source.getMaxValue());
|
||||
updateTextFields(
|
||||
presenter.getValueAt(source, normalizedBlack),
|
||||
presenter.getValueAt(source, normalizedWhite)
|
||||
);
|
||||
} catch (Exception e){
|
||||
displayErrorMessage("Oups !", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTextFields(double black, double white) {
|
||||
blackTextField.setText(String.format("%.5f", black));
|
||||
whiteTextField.setText(String.format("%.5f", white));
|
||||
}
|
||||
|
||||
private void updateLabels(double black, double white) {
|
||||
minLabel.setText(String.format("%.5f", black));
|
||||
maxLabel.setText(String.format("%.5f", white));
|
||||
}
|
||||
|
||||
private void saveImage(ActionEvent evt) {
|
||||
|
||||
File file = chooseFile();
|
||||
if (file != null) {
|
||||
try {
|
||||
presenter.saveImage(null);
|
||||
//image.enregistreFormatPNG(fichier);
|
||||
} catch (IOException ex) {
|
||||
System.err.println("Erreur I/O : " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void displayErrorMessage(String title, String message) {
|
||||
JOptionPane.showMessageDialog(
|
||||
this,
|
||||
message,
|
||||
title,
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
}
|
||||
|
||||
private void jTextFieldNoirActionPerformed(ActionEvent evt) {
|
||||
// double val = Double.parseDouble(jTextFieldNoir.getText());
|
||||
//
|
||||
// double black = presenter.getBlackLevel();
|
||||
// double white = presenter.getWhiteLevel();
|
||||
//
|
||||
// if (val < black)
|
||||
// {
|
||||
// jSliderNoir.setValue(0);
|
||||
// return;
|
||||
// }
|
||||
// if (val >= normalizedWhite)
|
||||
// {
|
||||
// jSliderNoir.setValue(jSliderBlanc.getValue()-1);
|
||||
// return;
|
||||
// }
|
||||
// int s = (int)((double)D*(val-black)/(white-black)+0.5);
|
||||
// jSliderNoir.setValue(s);
|
||||
|
||||
}
|
||||
|
||||
private void jTextFieldBlancActionPerformed(ActionEvent evt) {
|
||||
// double val = Double.parseDouble(jTextFieldBlanc.getText());
|
||||
// double black = presenter.getBlackLevel();
|
||||
// double white = presenter.getWhiteLevel();
|
||||
//
|
||||
// if (val > white)
|
||||
// {
|
||||
// jSliderBlanc.setValue(D);
|
||||
// return;
|
||||
// }
|
||||
// if (val <= normalizedBlack)
|
||||
// {
|
||||
// jSliderBlanc.setValue(jSliderNoir.getValue()+1);
|
||||
// return;
|
||||
// }
|
||||
// int s = (int)((double)D*(val-black)/(white-black)+0.5);
|
||||
// jSliderBlanc.setValue(s);
|
||||
}
|
||||
}
|
||||
201
src/main/java/ui/implementation/views/MainView.java
Normal file
@@ -0,0 +1,201 @@
|
||||
package ui.implementation.views;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import domain.common.Mode;
|
||||
import domain.image.Image;
|
||||
import infrastructure.ui.ImageMapper;
|
||||
import presenters.MainPresenter;
|
||||
import ui.implementation.components.image.ImagePanel;
|
||||
import ui.implementation.components.nav.NavBar;
|
||||
import ui.interfaces.IMainView;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* @author Jean-Marc Wagner, Laurent Crema
|
||||
*/
|
||||
public class MainView extends JFrame implements IMainView
|
||||
{
|
||||
private Mode currentMode;
|
||||
private ImagePanel imagePreviewContainer;
|
||||
private final MainPresenter presenter;
|
||||
|
||||
@Inject
|
||||
public MainView(MainPresenter presenter){
|
||||
this.presenter = presenter;
|
||||
this.presenter.setView(this);
|
||||
initComponents();
|
||||
}
|
||||
|
||||
public void setNavBar(NavBar navBar) {
|
||||
setJMenuBar(navBar);
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
imagePreviewContainer = new ImagePanel();
|
||||
|
||||
imagePreviewContainer.addMouseMotionListener(new MouseMotionAdapter() {
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
drawPixel(e.getX(), e.getY(), 255, 0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
imagePreviewContainer.addMouseListener(new MouseAdapter() {
|
||||
|
||||
private Point startPoint;
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
startPoint = new Point(e.getX(), e.getY());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
Point endPoint = new Point(e.getX(), e.getY());
|
||||
if (startPoint == null) return;
|
||||
|
||||
drawShape(
|
||||
startPoint.x,
|
||||
endPoint.x,
|
||||
startPoint.y,
|
||||
endPoint.y,
|
||||
255, 0, 0
|
||||
);
|
||||
|
||||
startPoint = null;
|
||||
}
|
||||
});
|
||||
|
||||
GroupLayout layout = new GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(imagePreviewContainer)
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(imagePreviewContainer)
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
setSize(new Dimension(500, 400));
|
||||
setLocationRelativeTo(null);
|
||||
}
|
||||
|
||||
public void drawPixel(int x, int y, int red, int green, int blue){
|
||||
try {
|
||||
Point p = imagePreviewContainer.toImageCoordinates(x, y);
|
||||
if(p == null) return;
|
||||
presenter.drawPixel((int)p.getX(), (int)p.getY(), red, green, blue);
|
||||
} catch (Exception e) {
|
||||
displayErrorMessage("Oups !",
|
||||
"Impossible de dessiner sur l'image pour le moment : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void drawShape(int x1, int x2, int y1, int y2, int red, int green, int blue){
|
||||
try {
|
||||
Point p1 = imagePreviewContainer.toImageCoordinates(x1, y1);
|
||||
Point p2 = imagePreviewContainer.toImageCoordinates(x2, y2);
|
||||
if(p1 == null || p2 == null) return;
|
||||
presenter.drawShape(
|
||||
(int)p1.getX(), (int)p2.getX(), (int) p1.getY(), (int) p2.getY(),
|
||||
red, green, blue);
|
||||
|
||||
} catch (Exception e) {
|
||||
displayErrorMessage("Oups !",
|
||||
"Impossible de dessiner sur l'image pour le moment : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// public void SelectCercleDetected(DeuxClicsEvent e)
|
||||
// {
|
||||
// if (presenter.isCircleModeActive())
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// if (imageRGB != null)
|
||||
// imageRGB.DessineCercle(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauRGB);
|
||||
// if (imageNG != null)
|
||||
// imageNG.DessineCercle(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauNG);
|
||||
// }
|
||||
// catch (CImageRGBException ex)
|
||||
// { System.out.println("Erreur RGB : " + ex.getMessage()); }
|
||||
// catch (CImageNGException ex)
|
||||
// { System.out.println("Erreur NG : " + ex.getMessage()); }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void SelectCercleFillDetected(DeuxClicsEvent e)
|
||||
// {
|
||||
// if (presenter.isPlainCircleModeActive())
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// if (imageRGB != null)
|
||||
// imageRGB.RemplitCercle(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauRGB);
|
||||
// if (imageNG != null)
|
||||
// imageNG.RemplitCercle(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauNG);
|
||||
// }
|
||||
// catch (CImageRGBException ex)
|
||||
// { System.out.println("Erreur RGB : " + ex.getMessage()); }
|
||||
// catch (CImageNGException ex)
|
||||
// { System.out.println("Erreur NG : " + ex.getMessage()); }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void SelectRectFillDetected(DeuxClicsEvent e)
|
||||
// {
|
||||
// if (presenter.isPlainRectangleActive())
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// if (imageRGB != null)
|
||||
// imageRGB.RemplitRect(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauRGB);
|
||||
// if (imageNG != null)
|
||||
// imageNG.RemplitRect(e.getX1(),e.getY1(),e.getX2(),e.getY2(),couleurPinceauNG);
|
||||
// }
|
||||
// catch (CImageRGBException ex)
|
||||
// { System.out.println("Erreur RGB : " + ex.getMessage()); }
|
||||
// catch (CImageNGException ex)
|
||||
// { System.out.println("Erreur NG : " + ex.getMessage()); }
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void displayImage(Image image) {
|
||||
try {
|
||||
BufferedImage bufferedImage = ImageMapper.toBufferedImage(image);
|
||||
imagePreviewContainer.loadImage(bufferedImage);
|
||||
} catch (Exception ex) {
|
||||
displayErrorMessage("Oups !", ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeMode(Mode m) {
|
||||
|
||||
}
|
||||
|
||||
private void displayErrorMessage(String title, String message) {
|
||||
JOptionPane.showMessageDialog(
|
||||
this,
|
||||
message,
|
||||
title,
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
11
src/main/java/ui/interfaces/IDoubleMatrix.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package ui.interfaces;
|
||||
|
||||
import domain.image.GrayScaleMatrix;
|
||||
|
||||
public interface IDoubleMatrix {
|
||||
|
||||
void updateModule(GrayScaleMatrix source, GrayScaleMatrix clipped);
|
||||
void updatePhase(GrayScaleMatrix source, GrayScaleMatrix clipped);
|
||||
void updateReal(GrayScaleMatrix source, GrayScaleMatrix clipped);
|
||||
void updateImaginary(GrayScaleMatrix source, GrayScaleMatrix clipped);
|
||||
}
|
||||
15
src/main/java/ui/interfaces/IMainView.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package ui.interfaces;
|
||||
|
||||
import domain.common.Mode;
|
||||
import domain.image.Image;
|
||||
import presenters.DoubleMatrixPresenter;
|
||||
import presenters.MainPresenter;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public interface IMainView {
|
||||
|
||||
void changeMode(Mode m);
|
||||
void displayImage(Image image);
|
||||
|
||||
}
|
||||
8
src/main/java/ui/interfaces/INavBar.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package ui.interfaces;
|
||||
|
||||
import presenters.NavPresenter;
|
||||
|
||||
public interface INavBar {
|
||||
|
||||
|
||||
}
|
||||
BIN
src/main/resources/Thumbs.db
Normal file
BIN
src/main/resources/cp_51_p1.jpg
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/main/resources/cp_51_p3.jpg
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/main/resources/cp_59_p3.jpg
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/main/resources/dd_27_p3.jpg
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/main/resources/dd_28_p1.jpg
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
src/main/resources/display_14_p3.jpg
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/main/resources/file_65_p3.jpg
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/main/resources/folder_036_p3.jpg
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/main/resources/net_13_p1.jpg
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src/main/resources/report_32_hot.jpg
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/main/resources/report_48_hot.jpg
Normal file
|
After Width: | Height: | Size: 4.7 KiB |