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 |