From 432160302e93049c4070b2b2770911828ac4a656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Thu, 23 Feb 2023 08:46:11 +0100 Subject: [PATCH] simple stamping --- .gitignore | 42 +++++++ pom.xml | 35 ++++++ .../de/ph87/kindermalen/ClickListener.java | 47 +++++++ .../de/ph87/kindermalen/ImageResizer.java | 33 +++++ .../java/de/ph87/kindermalen/KeyListener.java | 40 ++++++ src/main/java/de/ph87/kindermalen/Main.java | 117 ++++++++++++++++++ .../de/ph87/kindermalen/MotionListener.java | 27 ++++ .../java/de/ph87/kindermalen/Publisher.java | 44 +++++++ .../de/ph87/kindermalen/Subscription.java | 24 ++++ .../de/ph87/kindermalen/drawing/Drawing.java | 20 +++ .../kindermalen/drawing/DrawingPanel.java | 64 ++++++++++ .../de/ph87/kindermalen/drawing/Layer.java | 72 +++++++++++ .../de/ph87/kindermalen/drawing/Vector.java | 36 ++++++ .../java/de/ph87/kindermalen/tool/Tool.java | 18 +++ .../de/ph87/kindermalen/tool/ToolPanel.java | 18 +++ .../kindermalen/tool/stamp/ListHelper.java | 18 +++ .../de/ph87/kindermalen/tool/stamp/Stamp.java | 43 +++++++ .../kindermalen/tool/stamp/StampTool.java | 98 +++++++++++++++ .../tool/stamp/StampToolPanel.java | 67 ++++++++++ .../de/ph87/kindermalen/toolbox/Observer.java | 7 ++ .../de/ph87/kindermalen/toolbox/ToolBox.java | 38 ++++++ .../kindermalen/toolbox/ToolBoxPanel.java | 56 +++++++++ src/main/resources/logback.xml | 13 ++ 23 files changed, 977 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/de/ph87/kindermalen/ClickListener.java create mode 100644 src/main/java/de/ph87/kindermalen/ImageResizer.java create mode 100644 src/main/java/de/ph87/kindermalen/KeyListener.java create mode 100644 src/main/java/de/ph87/kindermalen/Main.java create mode 100644 src/main/java/de/ph87/kindermalen/MotionListener.java create mode 100644 src/main/java/de/ph87/kindermalen/Publisher.java create mode 100644 src/main/java/de/ph87/kindermalen/Subscription.java create mode 100644 src/main/java/de/ph87/kindermalen/drawing/Drawing.java create mode 100644 src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java create mode 100644 src/main/java/de/ph87/kindermalen/drawing/Layer.java create mode 100644 src/main/java/de/ph87/kindermalen/drawing/Vector.java create mode 100644 src/main/java/de/ph87/kindermalen/tool/Tool.java create mode 100644 src/main/java/de/ph87/kindermalen/tool/ToolPanel.java create mode 100644 src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java create mode 100644 src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java create mode 100644 src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java create mode 100644 src/main/java/de/ph87/kindermalen/tool/stamp/StampToolPanel.java create mode 100644 src/main/java/de/ph87/kindermalen/toolbox/Observer.java create mode 100644 src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java create mode 100644 src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java create mode 100644 src/main/resources/logback.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb12677 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..fcf38c4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + de.ph87.kindermalen + KinderMalen + 1.0-SNAPSHOT + + + 17 + 17 + UTF-8 + + + + + ch.qos.logback + logback-classic + 1.4.5 + + + org.projectlombok + lombok + 1.18.24 + + + com.mortennobel + java-image-scaling + 0.8.6 + + + + \ No newline at end of file diff --git a/src/main/java/de/ph87/kindermalen/ClickListener.java b/src/main/java/de/ph87/kindermalen/ClickListener.java new file mode 100644 index 0000000..d579c28 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/ClickListener.java @@ -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 onPress; + + private final Consumer onRelease; + + public ClickListener(final Consumer onPress, final Consumer 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) { + + } + +} diff --git a/src/main/java/de/ph87/kindermalen/ImageResizer.java b/src/main/java/de/ph87/kindermalen/ImageResizer.java new file mode 100644 index 0000000..24e6fbe --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/ImageResizer.java @@ -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); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/KeyListener.java b/src/main/java/de/ph87/kindermalen/KeyListener.java new file mode 100644 index 0000000..98f77a8 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/KeyListener.java @@ -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 next; + + public KeyListener(final boolean ctrl, final boolean shift, final int keycode, final Consumer 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) { + + } + +} diff --git a/src/main/java/de/ph87/kindermalen/Main.java b/src/main/java/de/ph87/kindermalen/Main.java new file mode 100644 index 0000000..e71141f --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/Main.java @@ -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"); + } + } + +} diff --git a/src/main/java/de/ph87/kindermalen/MotionListener.java b/src/main/java/de/ph87/kindermalen/MotionListener.java new file mode 100644 index 0000000..edf0604 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/MotionListener.java @@ -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 onDrag; + + public MotionListener(final Consumer onDrag) { + this.onDrag = onDrag; + } + + @Override + public void mouseDragged(final MouseEvent e) { + if (onDrag != null) { + onDrag.accept(e); + } + } + + @Override + public void mouseMoved(final MouseEvent e) { + + } + +} diff --git a/src/main/java/de/ph87/kindermalen/Publisher.java b/src/main/java/de/ph87/kindermalen/Publisher.java new file mode 100644 index 0000000..af23298 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/Publisher.java @@ -0,0 +1,44 @@ +package de.ph87.kindermalen; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class Publisher { + + private final List> 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 subscribe(final Consumer next) { + final Subscription subscription = new Subscription<>(next, this::unsubscribe); + synchronized (subscriptions) { + subscriptions.add(subscription); + if (last != null) { + subscription.next(last); + } + } + return subscription; + } + + private void unsubscribe(final Subscription subscription) { + synchronized (subscriptions) { + subscriptions.remove(subscription); + } + } + +} diff --git a/src/main/java/de/ph87/kindermalen/Subscription.java b/src/main/java/de/ph87/kindermalen/Subscription.java new file mode 100644 index 0000000..9cc6e7a --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/Subscription.java @@ -0,0 +1,24 @@ +package de.ph87.kindermalen; + +import java.util.function.Consumer; + +public class Subscription { + + private final Consumer next; + + private final Consumer> teardown; + + public Subscription(final Consumer next, final Consumer> teardown) { + this.next = next; + this.teardown = teardown; + } + + public void unsubscribe() { + teardown.accept(this); + } + + public void next(final T item) { + this.next.accept(item); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/drawing/Drawing.java b/src/main/java/de/ph87/kindermalen/drawing/Drawing.java new file mode 100644 index 0000000..cd0ac36 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/drawing/Drawing.java @@ -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); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java b/src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java new file mode 100644 index 0000000..97af34e --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/drawing/DrawingPanel.java @@ -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); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/drawing/Layer.java b/src/main/java/de/ph87/kindermalen/drawing/Layer.java new file mode 100644 index 0000000..9cac284 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/drawing/Layer.java @@ -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 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; + } + +} diff --git a/src/main/java/de/ph87/kindermalen/drawing/Vector.java b/src/main/java/de/ph87/kindermalen/drawing/Vector.java new file mode 100644 index 0000000..c0805cb --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/drawing/Vector.java @@ -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); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tool/Tool.java b/src/main/java/de/ph87/kindermalen/tool/Tool.java new file mode 100644 index 0000000..a6d9aee --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tool/Tool.java @@ -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); + +} diff --git a/src/main/java/de/ph87/kindermalen/tool/ToolPanel.java b/src/main/java/de/ph87/kindermalen/tool/ToolPanel.java new file mode 100644 index 0000000..e178360 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tool/ToolPanel.java @@ -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 extends JPanel { + + protected final T tool; + + protected ToolPanel(final T tool) { + this.tool = tool; + setPreferredSize(new Dimension(100, 100)); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java b/src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java new file mode 100644 index 0000000..f89107a --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tool/stamp/ListHelper.java @@ -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 List SYNC(final List aList, final List bOld, final Function key, final Function create, final Function order) { + final List bAdd = aList.stream().filter(a -> bOld.stream().noneMatch(b -> key.apply(b) == a)).map(create).toList(); + final List bNew = new ArrayList<>(bOld.stream().filter(panel -> aList.contains(key.apply(panel))).toList()); + bNew.addAll(bAdd); + bNew.sort(Comparator.comparing(order)); + return bNew; + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java b/src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java new file mode 100644 index 0000000..4887ce0 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tool/stamp/Stamp.java @@ -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 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)); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java b/src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java new file mode 100644 index 0000000..1a268ce --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tool/stamp/StampTool.java @@ -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 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 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 scan(final File dir) { + final List 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); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/tool/stamp/StampToolPanel.java b/src/main/java/de/ph87/kindermalen/tool/stamp/StampToolPanel.java new file mode 100644 index 0000000..669a93f --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/tool/stamp/StampToolPanel.java @@ -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 { + + 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); + } + + } + +} diff --git a/src/main/java/de/ph87/kindermalen/toolbox/Observer.java b/src/main/java/de/ph87/kindermalen/toolbox/Observer.java new file mode 100644 index 0000000..de53eba --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/toolbox/Observer.java @@ -0,0 +1,7 @@ +package de.ph87.kindermalen.toolbox; + +public interface Observer { + + void next(final T item); + +} diff --git a/src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java b/src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java new file mode 100644 index 0000000..457214d --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/toolbox/ToolBox.java @@ -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 tools = List.of(new StampTool()); + + @Getter + private Tool tool; + + private final Publisher 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 onToolChange(final Consumer next) { + return onToolChange.subscribe(next); + } + +} diff --git a/src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java b/src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java new file mode 100644 index 0000000..40457c8 --- /dev/null +++ b/src/main/java/de/ph87/kindermalen/toolbox/ToolBoxPanel.java @@ -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); + } + + } + +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..c3730f4 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) -- %msg %n + + + + + + + + \ No newline at end of file