Import from internal git

This commit is contained in:
Laurent
2026-06-23 20:27:12 +02:00
commit 2ddced7d41
72 changed files with 3308 additions and 0 deletions

43
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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.
![fourier](./menu_fourier.png)
---
## 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
![architecture](./architecture.png)
### 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

24
build.gradle.kts Normal file
View 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

Binary file not shown.

View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

1
settings.gradle.kts Normal file
View File

@@ -0,0 +1 @@
rootProject.name = "TDS"

28
src/main/java/Main.java Normal file
View 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);
});
}
}

View 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);
}
}

View File

@@ -0,0 +1,7 @@
package app;
public interface INavigator {
void showFourier();
}

View 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);
}
}

View 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; }
}

View 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));
}
}

View 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);
}
}

View File

@@ -0,0 +1,9 @@
package domain.common;
public enum Mode {
DISABLED,
DRAW_PIXEL,
DRAW_LINE,
DRAW_RECTANGLE,
DRAW_CIRCLE,
}

View File

@@ -0,0 +1,5 @@
package domain.events;
import domain.image.Image;
public record ImageChangedEvent(Image image) { }

View File

@@ -0,0 +1,6 @@
package domain.events;
import domain.image.GrayScaleMatrix;
public record MatrixChangedEvent(GrayScaleMatrix matrix) {
}

View File

@@ -0,0 +1,5 @@
package domain.events;
import domain.common.Mode;
public record ModeChangedEvent(Mode mode) { }

View File

@@ -0,0 +1,6 @@
package domain.events.fourier;
import domain.image.processing.complex.ComplexMatrix;
public record ComplexMatrixChanged(ComplexMatrix matrix) {
}

View File

@@ -0,0 +1,6 @@
package domain.events.fourier;
import domain.image.GrayScaleMatrix;
public record ImaginaryChangedEvent(GrayScaleMatrix matrix) {
}

View File

@@ -0,0 +1,6 @@
package domain.events.fourier;
import domain.image.GrayScaleMatrix;
public record ModuleChangedEvent(GrayScaleMatrix matrix) {
}

View File

@@ -0,0 +1,6 @@
package domain.events.fourier;
import domain.image.GrayScaleMatrix;
public record PhaseChangedEvent(GrayScaleMatrix matrix) {
}

View File

@@ -0,0 +1,6 @@
package domain.events.fourier;
import domain.image.GrayScaleMatrix;
public record RealChangedEvent(GrayScaleMatrix matrix) {
}

View 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);
}
}

View 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;
}
}

View 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);
}
}

View File

@@ -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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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);
}
}

View 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 {
}
}

View 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);
}
}

View 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();
}
}

View 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);
}
}

View 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);
}
}

View 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();
}
}

View 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(); }
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,9 @@
package ui.implementation.components.intent;
import javax.swing.*;
public class FileChooser extends JFileChooser {
public FileChooser(String initialLocation) {
super(initialLocation);
}
}

View 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);
}
}
}
}
}

View 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);
}
}
}

View 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());
}
}
}

View File

@@ -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);
}
}

View 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
}

View 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();
}

View File

@@ -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);
}
}

View 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);
}
}

View 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
);
}
}

View 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);
}

View 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);
}

View File

@@ -0,0 +1,8 @@
package ui.interfaces;
import presenters.NavPresenter;
public interface INavBar {
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB