simple stamping
This commit is contained in:
commit
432160302e
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/data/
|
||||
|
||||
/.idea/
|
||||
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
35
pom.xml
Normal file
35
pom.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>de.ph87.kindermalen</groupId>
|
||||
<artifactId>KinderMalen</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.4.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mortennobel</groupId>
|
||||
<artifactId>java-image-scaling</artifactId>
|
||||
<version>0.8.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
47
src/main/java/de/ph87/kindermalen/ClickListener.java
Normal file
47
src/main/java/de/ph87/kindermalen/ClickListener.java
Normal file
@ -0,0 +1,47 @@
|
||||
package de.ph87.kindermalen;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ClickListener implements MouseListener {
|
||||
|
||||
private final Consumer<MouseEvent> onPress;
|
||||
|
||||
private final Consumer<MouseEvent> onRelease;
|
||||
|
||||
public ClickListener(final Consumer<MouseEvent> onPress, final Consumer<MouseEvent> onRelease) {
|
||||
this.onPress = onPress;
|
||||
this.onRelease = onRelease;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseClicked(final MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(final MouseEvent e) {
|
||||
if (onPress != null) {
|
||||
onPress.accept(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(final MouseEvent e) {
|
||||
if (onRelease != null) {
|
||||
onRelease.accept(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(final MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(final MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
33
src/main/java/de/ph87/kindermalen/ImageResizer.java
Normal file
33
src/main/java/de/ph87/kindermalen/ImageResizer.java
Normal file
@ -0,0 +1,33 @@
|
||||
package de.ph87.kindermalen;
|
||||
|
||||
import com.mortennobel.imagescaling.ResampleFilters;
|
||||
import com.mortennobel.imagescaling.ResampleOp;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class ImageResizer {
|
||||
|
||||
public static BufferedImage RESIZE(final BufferedImage image, final int maxWidth, final int maxHeight) {
|
||||
final Point size = fitInsideBox(image, maxWidth, maxHeight);
|
||||
final ResampleOp operation = new ResampleOp(size.x, size.y);
|
||||
operation.setFilter(ResampleFilters.getLanczos3Filter());
|
||||
return operation.filter(image, null);
|
||||
}
|
||||
|
||||
private static Point fitInsideBox(final BufferedImage image, final int width, final int height) {
|
||||
int w = image.getWidth();
|
||||
int h = image.getHeight();
|
||||
final double r = (double) w / h;
|
||||
if (w > width) {
|
||||
w = width;
|
||||
h = (int) Math.round(w / r);
|
||||
}
|
||||
if (h > width) {
|
||||
h = height;
|
||||
w = (int) Math.round(h * r);
|
||||
}
|
||||
return new Point(w, h);
|
||||
}
|
||||
|
||||
}
|
||||
40
src/main/java/de/ph87/kindermalen/KeyListener.java
Normal file
40
src/main/java/de/ph87/kindermalen/KeyListener.java
Normal file
@ -0,0 +1,40 @@
|
||||
package de.ph87.kindermalen;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class KeyListener implements java.awt.event.KeyListener {
|
||||
|
||||
private final boolean ctrl;
|
||||
|
||||
private final boolean shift;
|
||||
|
||||
private final int keycode;
|
||||
|
||||
private final Consumer<KeyEvent> next;
|
||||
|
||||
public KeyListener(final boolean ctrl, final boolean shift, final int keycode, final Consumer<KeyEvent> next) {
|
||||
this.ctrl = ctrl;
|
||||
this.shift = shift;
|
||||
this.keycode = keycode;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(final KeyEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(final KeyEvent e) {
|
||||
if (e.isControlDown() == ctrl && e.isShiftDown() == shift && e.getKeyCode() == keycode) {
|
||||
next.accept(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(final KeyEvent e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
117
src/main/java/de/ph87/kindermalen/Main.java
Normal file
117
src/main/java/de/ph87/kindermalen/Main.java
Normal file
@ -0,0 +1,117 @@
|
||||
package de.ph87.kindermalen;
|
||||
|
||||
import de.ph87.kindermalen.drawing.Drawing;
|
||||
import de.ph87.kindermalen.drawing.DrawingPanel;
|
||||
import de.ph87.kindermalen.tool.Tool;
|
||||
import de.ph87.kindermalen.tool.ToolPanel;
|
||||
import de.ph87.kindermalen.tool.stamp.StampTool;
|
||||
import de.ph87.kindermalen.tool.stamp.StampToolPanel;
|
||||
import de.ph87.kindermalen.toolbox.ToolBox;
|
||||
import de.ph87.kindermalen.toolbox.ToolBoxPanel;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
public class Main extends JFrame {
|
||||
|
||||
public static final int SCREEN = 1;
|
||||
|
||||
private static final double SIDEBAR_RATIO = 0.15;
|
||||
|
||||
public static final double TOOLBOX_RATIO = 0;
|
||||
|
||||
public static final double PROPERTIES_RATIO = 2;
|
||||
|
||||
private final ToolBox toolBox = new ToolBox();
|
||||
|
||||
private final Drawing drawing = new Drawing(800, 600);
|
||||
|
||||
private final DrawingPanel drawingPanel = new DrawingPanel(toolBox, drawing);
|
||||
|
||||
private final ToolBoxPanel toolBoxPanel = new ToolBoxPanel(toolBox);
|
||||
|
||||
private ToolPanel toolPanel = null;
|
||||
|
||||
public static void main(String[] args) {
|
||||
final Main main = new Main();
|
||||
main.setVisible(true);
|
||||
}
|
||||
|
||||
public Main() {
|
||||
setLayout(new GridBagLayout());
|
||||
final GridBagConstraints c = new GridBagConstraints();
|
||||
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.weightx = SIDEBAR_RATIO;
|
||||
c.weighty = TOOLBOX_RATIO;
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.anchor = GridBagConstraints.FIRST_LINE_START;
|
||||
add(toolBoxPanel, c);
|
||||
|
||||
c.gridx = 1;
|
||||
c.gridy = 0;
|
||||
c.weightx = 1 - SIDEBAR_RATIO;
|
||||
c.weighty = 1;
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.gridheight = 3;
|
||||
add(drawingPanel, c);
|
||||
|
||||
pack();
|
||||
|
||||
setFullscreen();
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
|
||||
addKeyListener(new KeyListener(true, false, KeyEvent.VK_Z, this::undo));
|
||||
addKeyListener(new KeyListener(true, true, KeyEvent.VK_Z, this::redo));
|
||||
|
||||
toolBox.onToolChange(this::onToolChanged);
|
||||
}
|
||||
|
||||
private void undo(final KeyEvent keyEvent) {
|
||||
drawing.getCurrent().undo();
|
||||
drawingPanel.repaint();
|
||||
}
|
||||
|
||||
private void redo(final KeyEvent keyEvent) {
|
||||
drawing.getCurrent().redo();
|
||||
drawingPanel.repaint();
|
||||
}
|
||||
|
||||
private void onToolChanged(final Tool tool) {
|
||||
if (toolPanel != null) {
|
||||
remove(toolPanel);
|
||||
toolPanel = null;
|
||||
}
|
||||
|
||||
if (tool instanceof StampTool) {
|
||||
toolPanel = new StampToolPanel((StampTool) tool);
|
||||
}
|
||||
|
||||
if (toolPanel != null) {
|
||||
final GridBagConstraints c = new GridBagConstraints();
|
||||
c.gridx = 0;
|
||||
c.gridy = 1;
|
||||
c.weightx = SIDEBAR_RATIO;
|
||||
c.weighty = PROPERTIES_RATIO;
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
c.anchor = GridBagConstraints.FIRST_LINE_START;
|
||||
add(toolPanel, c);
|
||||
pack();
|
||||
}
|
||||
}
|
||||
|
||||
private void setFullscreen() {
|
||||
final GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
final GraphicsDevice[] graphicsDevices = graphicsEnvironment.getScreenDevices();
|
||||
if (SCREEN < graphicsDevices.length) {
|
||||
graphicsDevices[SCREEN].setFullScreenWindow(this);
|
||||
} else if (graphicsDevices.length > 0) {
|
||||
graphicsDevices[0].setFullScreenWindow(this);
|
||||
} else {
|
||||
throw new RuntimeException("No Screens Found");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
27
src/main/java/de/ph87/kindermalen/MotionListener.java
Normal file
27
src/main/java/de/ph87/kindermalen/MotionListener.java
Normal file
@ -0,0 +1,27 @@
|
||||
package de.ph87.kindermalen;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MotionListener implements MouseMotionListener {
|
||||
|
||||
private final Consumer<MouseEvent> onDrag;
|
||||
|
||||
public MotionListener(final Consumer<MouseEvent> onDrag) {
|
||||
this.onDrag = onDrag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(final MouseEvent e) {
|
||||
if (onDrag != null) {
|
||||
onDrag.accept(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(final MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
44
src/main/java/de/ph87/kindermalen/Publisher.java
Normal file
44
src/main/java/de/ph87/kindermalen/Publisher.java
Normal file
@ -0,0 +1,44 @@
|
||||
package de.ph87.kindermalen;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Publisher<T> {
|
||||
|
||||
private final List<Subscription<T>> subscriptions = new ArrayList<>();
|
||||
|
||||
private T last = null;
|
||||
|
||||
private boolean signalling = false;
|
||||
|
||||
public void publish(final T item) {
|
||||
synchronized (subscriptions) {
|
||||
if (signalling) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
signalling = true;
|
||||
last = item;
|
||||
new ArrayList<>(subscriptions).forEach(o -> o.next(item));
|
||||
signalling = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Subscription<T> subscribe(final Consumer<T> next) {
|
||||
final Subscription<T> subscription = new Subscription<>(next, this::unsubscribe);
|
||||
synchronized (subscriptions) {
|
||||
subscriptions.add(subscription);
|
||||
if (last != null) {
|
||||
subscription.next(last);
|
||||
}
|
||||
}
|
||||
return subscription;
|
||||
}
|
||||
|
||||
private void unsubscribe(final Subscription<T> subscription) {
|
||||
synchronized (subscriptions) {
|
||||
subscriptions.remove(subscription);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
24
src/main/java/de/ph87/kindermalen/Subscription.java
Normal file
24
src/main/java/de/ph87/kindermalen/Subscription.java
Normal file
@ -0,0 +1,24 @@
|
||||
package de.ph87.kindermalen;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Subscription<T> {
|
||||
|
||||
private final Consumer<T> next;
|
||||
|
||||
private final Consumer<Subscription<T>> teardown;
|
||||
|
||||
public Subscription(final Consumer<T> next, final Consumer<Subscription<T>> teardown) {
|
||||
this.next = next;
|
||||
this.teardown = teardown;
|
||||
}
|
||||
|
||||
public void unsubscribe() {
|
||||
teardown.accept(this);
|
||||
}
|
||||
|
||||
public void next(final T item) {
|
||||
this.next.accept(item);
|
||||
}
|
||||
|
||||
}
|
||||
20
src/main/java/de/ph87/kindermalen/drawing/Drawing.java
Normal file
20
src/main/java/de/ph87/kindermalen/drawing/Drawing.java
Normal file
@ -0,0 +1,20 @@
|
||||
package de.ph87.kindermalen.drawing;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class Drawing {
|
||||
|
||||
private final int width;
|
||||
|
||||
private final int height;
|
||||
|
||||
private final Layer current;
|
||||
|
||||
public Drawing(final int width, final int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
current = new Layer(width, height);
|
||||
}
|
||||
|
||||
}
|
||||
64
src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java
Normal file
64
src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java
Normal file
@ -0,0 +1,64 @@
|
||||
package de.ph87.kindermalen.drawing;
|
||||
|
||||
import de.ph87.kindermalen.ClickListener;
|
||||
import de.ph87.kindermalen.MotionListener;
|
||||
import de.ph87.kindermalen.toolbox.ToolBox;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
@Slf4j
|
||||
public class DrawingPanel extends JPanel {
|
||||
|
||||
private final ToolBox toolBox;
|
||||
|
||||
private final Drawing drawing;
|
||||
|
||||
private Vector lastPoint = null;
|
||||
|
||||
public DrawingPanel(final ToolBox toolBox, final Drawing drawing) {
|
||||
this.toolBox = toolBox;
|
||||
this.drawing = drawing;
|
||||
this.addMouseListener(new ClickListener(this::onPress, null));
|
||||
this.addMouseMotionListener(new MotionListener(this::onDrag));
|
||||
}
|
||||
|
||||
private void onPress(final MouseEvent e) {
|
||||
lastPoint = new Vector(e.getPoint());
|
||||
drawing.getCurrent().newRevision();
|
||||
toolBox.getTool().apply(drawing.getCurrent().getCurrent(), e.getX(), e.getY());
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void onDrag(final MouseEvent e) {
|
||||
final Vector vector = new Vector(lastPoint, e.getPoint());
|
||||
int i = 0;
|
||||
for (int rest = (int) Math.floor(vector.length); rest >= 20; rest -= 20) {
|
||||
System.out.println(rest);
|
||||
toolBox.getTool().apply(drawing.getCurrent().getCurrent(), e.getX(), e.getY());
|
||||
lastPoint = lastPoint.plus(vector.withLength(20));
|
||||
i++;
|
||||
if (i > 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(final Graphics g) {
|
||||
g.setColor(Color.gray);
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
if (drawing == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
g.setColor(Color.white);
|
||||
g.fillRect(0, 0, drawing.getWidth(), drawing.getHeight());
|
||||
g.drawImage(drawing.getCurrent().getCurrent(), 0, 0, null);
|
||||
}
|
||||
|
||||
}
|
||||
72
src/main/java/de/ph87/kindermalen/drawing/Layer.java
Normal file
72
src/main/java/de/ph87/kindermalen/drawing/Layer.java
Normal file
@ -0,0 +1,72 @@
|
||||
package de.ph87.kindermalen.drawing;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class Layer {
|
||||
|
||||
private final int width;
|
||||
|
||||
private final int height;
|
||||
|
||||
@Getter
|
||||
private BufferedImage current;
|
||||
|
||||
private List<BufferedImage> history = new ArrayList<>();
|
||||
|
||||
private int index;
|
||||
|
||||
public Layer(final int width, final int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.current = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
this.history.add(current);
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
public void newRevision() {
|
||||
if (canRedo()) {
|
||||
history = history.subList(0, index + 1);
|
||||
}
|
||||
current = copy();
|
||||
index = history.size();
|
||||
history.add(current);
|
||||
log.info("Revision {} created", index);
|
||||
}
|
||||
|
||||
private BufferedImage copy() {
|
||||
final BufferedImage created = new BufferedImage(current.getWidth(), current.getHeight(), current.getType());
|
||||
current.copyData(created.getRaster());
|
||||
return created;
|
||||
}
|
||||
|
||||
public void undo() {
|
||||
if (index > 0) {
|
||||
index--;
|
||||
current = history.get(index);
|
||||
log.info("UNDO: Revision {} loaded", index);
|
||||
} else {
|
||||
log.warn("No UNDO steps left.");
|
||||
}
|
||||
}
|
||||
|
||||
public void redo() {
|
||||
if (canRedo()) {
|
||||
index++;
|
||||
current = history.get(index);
|
||||
log.info("REDO: Revision {} loaded", index);
|
||||
} else {
|
||||
log.warn("No REDO steps left.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canRedo() {
|
||||
return index < history.size() - 1;
|
||||
}
|
||||
|
||||
}
|
||||
36
src/main/java/de/ph87/kindermalen/drawing/Vector.java
Normal file
36
src/main/java/de/ph87/kindermalen/drawing/Vector.java
Normal file
@ -0,0 +1,36 @@
|
||||
package de.ph87.kindermalen.drawing;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public class Vector {
|
||||
|
||||
public final double x;
|
||||
|
||||
public final double y;
|
||||
|
||||
public final double length;
|
||||
|
||||
public Vector(final Point point) {
|
||||
this(point.x, point.y);
|
||||
}
|
||||
|
||||
public Vector(final Vector start, final Point end) {
|
||||
this(end.x - start.x, end.y - start.y);
|
||||
}
|
||||
|
||||
public Vector(final double x, final double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.length = Math.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
public Vector withLength(final double newLength) {
|
||||
final double factor = newLength / length;
|
||||
return new Vector(x / factor, y / factor);
|
||||
}
|
||||
|
||||
public Vector plus(final Vector other) {
|
||||
return new Vector(x + other.x, y + other.y);
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/java/de/ph87/kindermalen/tool/Tool.java
Normal file
18
src/main/java/de/ph87/kindermalen/tool/Tool.java
Normal file
@ -0,0 +1,18 @@
|
||||
package de.ph87.kindermalen.tool;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public abstract class Tool {
|
||||
|
||||
@Getter
|
||||
private final String name;
|
||||
|
||||
protected Tool(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public abstract void apply(final BufferedImage image, final int x, final int y);
|
||||
|
||||
}
|
||||
18
src/main/java/de/ph87/kindermalen/tool/ToolPanel.java
Normal file
18
src/main/java/de/ph87/kindermalen/tool/ToolPanel.java
Normal file
@ -0,0 +1,18 @@
|
||||
package de.ph87.kindermalen.tool;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
@Slf4j
|
||||
public abstract class ToolPanel<T extends Tool> extends JPanel {
|
||||
|
||||
protected final T tool;
|
||||
|
||||
protected ToolPanel(final T tool) {
|
||||
this.tool = tool;
|
||||
setPreferredSize(new Dimension(100, 100));
|
||||
}
|
||||
|
||||
}
|
||||
18
src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java
Normal file
18
src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java
Normal file
@ -0,0 +1,18 @@
|
||||
package de.ph87.kindermalen.tool.stamp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ListHelper {
|
||||
|
||||
public static <A, B> List<B> SYNC(final List<A> aList, final List<B> bOld, final Function<B, A> key, final Function<A, B> create, final Function<B, String> order) {
|
||||
final List<B> bAdd = aList.stream().filter(a -> bOld.stream().noneMatch(b -> key.apply(b) == a)).map(create).toList();
|
||||
final List<B> bNew = new ArrayList<>(bOld.stream().filter(panel -> aList.contains(key.apply(panel))).toList());
|
||||
bNew.addAll(bAdd);
|
||||
bNew.sort(Comparator.comparing(order));
|
||||
return bNew;
|
||||
}
|
||||
|
||||
}
|
||||
43
src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java
Normal file
43
src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java
Normal file
@ -0,0 +1,43 @@
|
||||
package de.ph87.kindermalen.tool.stamp;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static de.ph87.kindermalen.ImageResizer.RESIZE;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@ToString
|
||||
public class Stamp {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final File file;
|
||||
|
||||
private BufferedImage original = null;
|
||||
|
||||
private final Map<Integer, BufferedImage> sizes = new HashMap<>();
|
||||
|
||||
public Stamp(final File file) {
|
||||
this.name = file.getName().substring(0, file.getName().lastIndexOf("."));
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public void load() throws IOException {
|
||||
original = ImageIO.read(file);
|
||||
log.info("Stamp loaded: {}", this);
|
||||
}
|
||||
|
||||
public BufferedImage getSize(final int size) {
|
||||
return sizes.computeIfAbsent(size, s -> RESIZE(original, s, s));
|
||||
}
|
||||
|
||||
}
|
||||
98
src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java
Normal file
98
src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java
Normal file
@ -0,0 +1,98 @@
|
||||
package de.ph87.kindermalen.tool.stamp;
|
||||
|
||||
import de.ph87.kindermalen.tool.Tool;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static de.ph87.kindermalen.ImageResizer.RESIZE;
|
||||
import static de.ph87.kindermalen.tool.stamp.ListHelper.SYNC;
|
||||
|
||||
@Slf4j
|
||||
@ToString
|
||||
public class StampTool extends Tool {
|
||||
|
||||
private static final File STAMPS_DIR = new File("./data/stamps");
|
||||
|
||||
@Getter
|
||||
private List<Stamp> stamps = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private Stamp stamp;
|
||||
|
||||
@Getter
|
||||
private int size = 100;
|
||||
|
||||
@Getter
|
||||
private double alpha = 0.75;
|
||||
|
||||
private BufferedImage prepared;
|
||||
|
||||
public StampTool() {
|
||||
super("Stempel");
|
||||
final List<File> files = scan(STAMPS_DIR);
|
||||
stamps = SYNC(files, stamps, Stamp::getFile, Stamp::new, Stamp::getName);
|
||||
stamps.stream().filter(stamp -> stamp.getOriginal() == null).forEach(this::load);
|
||||
}
|
||||
|
||||
private List<File> scan(final File dir) {
|
||||
final List<File> files = new ArrayList<>();
|
||||
for (final File child : Objects.requireNonNull(dir.listFiles())) {
|
||||
if (child.isFile()) {
|
||||
files.add(child.getAbsoluteFile());
|
||||
} else if (child.isDirectory()) {
|
||||
files.addAll(scan(child));
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
private void load(final Stamp stamp) {
|
||||
try {
|
||||
stamp.load();
|
||||
if (this.stamp == stamp) {
|
||||
prepare();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void setStamp(final Stamp stamp) {
|
||||
this.stamp = stamp;
|
||||
log.info("Stamp chosen: {}", stamp.getName());
|
||||
prepare();
|
||||
}
|
||||
|
||||
public void setSize(final int size) {
|
||||
this.size = size;
|
||||
prepare();
|
||||
}
|
||||
|
||||
public void setAlpha(final double alpha) {
|
||||
this.alpha = alpha;
|
||||
prepare();
|
||||
}
|
||||
|
||||
private void prepare() {
|
||||
if (this.stamp == null || stamp.getOriginal() == null) {
|
||||
return;
|
||||
}
|
||||
this.prepared = RESIZE(stamp.getOriginal(), size, size);
|
||||
log.info("Stamp prepared: {}", stamp.getName());
|
||||
// TODO apply alpha
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(final BufferedImage destination, final int x, final int y) {
|
||||
destination.getGraphics().drawImage(prepared, x - prepared.getWidth() / 2, y - prepared.getHeight() / 2, null);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package de.ph87.kindermalen.tool.stamp;
|
||||
|
||||
import de.ph87.kindermalen.ClickListener;
|
||||
import de.ph87.kindermalen.tool.ToolPanel;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
@Slf4j
|
||||
public class StampToolPanel extends ToolPanel<StampTool> {
|
||||
|
||||
private static final BasicStroke STROKE_BORDER = new BasicStroke(1f);
|
||||
|
||||
private static final int STROKE_HIGHLIGHT_WIDTH = 4;
|
||||
|
||||
private static final BasicStroke STROKE_HIGHLIGHT = new BasicStroke(STROKE_HIGHLIGHT_WIDTH);
|
||||
|
||||
private static final int SIZE = 50;
|
||||
|
||||
public StampToolPanel(final StampTool tool) {
|
||||
super(tool);
|
||||
setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
|
||||
tool.getStamps().forEach(stamp -> add(new StampPanel(stamp)));
|
||||
}
|
||||
|
||||
private class StampPanel extends JPanel {
|
||||
|
||||
@Getter
|
||||
private final Stamp stamp;
|
||||
|
||||
private final BufferedImage icon;
|
||||
|
||||
private final Point position;
|
||||
|
||||
public StampPanel(final Stamp stamp) {
|
||||
this.stamp = stamp;
|
||||
setPreferredSize(new Dimension(SIZE, SIZE));
|
||||
addMouseListener(new ClickListener(null, e -> tool.setStamp(stamp)));
|
||||
icon = stamp.getSize(SIZE - STROKE_HIGHLIGHT_WIDTH * 2);
|
||||
position = new Point((SIZE - icon.getWidth()) / 2, (SIZE - icon.getHeight()) / 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(final Graphics g) {
|
||||
super.paint(g);
|
||||
|
||||
final Graphics2D g2 = (Graphics2D) g;
|
||||
|
||||
if (tool.getStamp() == stamp) {
|
||||
g2.setColor(Color.orange);
|
||||
g2.setStroke(STROKE_HIGHLIGHT);
|
||||
g2.fillRect(0, 0, getWidth(), getHeight());
|
||||
} else {
|
||||
g2.setColor(Color.white);
|
||||
g2.setStroke(STROKE_BORDER);
|
||||
}
|
||||
|
||||
g2.drawRect(0, 0, SIZE - 1, SIZE - 1);
|
||||
g2.drawImage(icon, position.x, position.y, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
7
src/main/java/de/ph87/kindermalen/toolbox/Observer.java
Normal file
7
src/main/java/de/ph87/kindermalen/toolbox/Observer.java
Normal file
@ -0,0 +1,7 @@
|
||||
package de.ph87.kindermalen.toolbox;
|
||||
|
||||
public interface Observer<T> {
|
||||
|
||||
void next(final T item);
|
||||
|
||||
}
|
||||
38
src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java
Normal file
38
src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java
Normal file
@ -0,0 +1,38 @@
|
||||
package de.ph87.kindermalen.toolbox;
|
||||
|
||||
import de.ph87.kindermalen.Publisher;
|
||||
import de.ph87.kindermalen.Subscription;
|
||||
import de.ph87.kindermalen.tool.Tool;
|
||||
import de.ph87.kindermalen.tool.stamp.StampTool;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Slf4j
|
||||
public class ToolBox {
|
||||
|
||||
@Getter
|
||||
private final List<Tool> tools = List.of(new StampTool());
|
||||
|
||||
@Getter
|
||||
private Tool tool;
|
||||
|
||||
private final Publisher<Tool> onToolChange = new Publisher<>();
|
||||
|
||||
public ToolBox() {
|
||||
setTool(tools.get(0));
|
||||
}
|
||||
|
||||
public void setTool(final Tool tool) {
|
||||
this.tool = tool;
|
||||
log.info("Tool selected: {}", tool.getName());
|
||||
onToolChange.publish(tool);
|
||||
}
|
||||
|
||||
public Subscription<Tool> onToolChange(final Consumer<Tool> next) {
|
||||
return onToolChange.subscribe(next);
|
||||
}
|
||||
|
||||
}
|
||||
56
src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java
Normal file
56
src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java
Normal file
@ -0,0 +1,56 @@
|
||||
package de.ph87.kindermalen.toolbox;
|
||||
|
||||
import de.ph87.kindermalen.ClickListener;
|
||||
import de.ph87.kindermalen.tool.Tool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
@Slf4j
|
||||
public class ToolBoxPanel extends JPanel {
|
||||
|
||||
private final ToolBox toolbox;
|
||||
|
||||
public ToolBoxPanel(final ToolBox toolbox) {
|
||||
this.toolbox = toolbox;
|
||||
setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
|
||||
for (final Tool tool : toolbox.getTools()) {
|
||||
add(new ToolButton(tool));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(final Graphics g) {
|
||||
super.paint(g);
|
||||
// final Graphics2D g2 = (Graphics2D) g;
|
||||
// g2.setColor(Color.red);
|
||||
// g2.setStroke(new BasicStroke(1f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, new float[]{2f}, 0));
|
||||
// g2.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
|
||||
}
|
||||
|
||||
private class ToolButton extends Component {
|
||||
|
||||
private final Tool tool;
|
||||
|
||||
public ToolButton(final Tool tool) {
|
||||
this.tool = tool;
|
||||
setPreferredSize(new Dimension(50, 50));
|
||||
addMouseListener(new ClickListener(null, e -> toolbox.setTool(tool)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(final Graphics g) {
|
||||
g.setColor(Color.magenta);
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
g.setColor(Color.black);
|
||||
g.setFont(g.getFont().deriveFont(12.0f));
|
||||
g.drawString(tool.getName(), 0, g.getFontMetrics().getHeight());
|
||||
|
||||
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
13
src/main/resources/logback.xml
Normal file
13
src/main/resources/logback.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<configuration>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) -- %msg %n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="debug">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
Loading…
Reference in New Issue
Block a user