From eab6df10ab8292a59a05b25d18c413dd107bb94a Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Thu, 26 Apr 2018 05:48:09 -0700 Subject: Initial commit --- src/bjc/imgchain/ImgChain.java | 337 ++++++++++------- src/bjc/imgchain/ImgPipeline.java | 206 ++++++++++ src/bjc/imgchain/ImgViewer.java | 416 +++++++++++---------- src/bjc/imgchain/pipeline/MutablePipeline.java | 102 +++++ src/bjc/imgchain/pipeline/Pipeline.java | 37 ++ src/bjc/imgchain/pipeline/PipelinePicker.java | 63 ++++ src/bjc/imgchain/pipeline/PipelineStage.java | 52 +++ src/bjc/imgchain/pipeline/StageType.java | 21 ++ .../pipeline/stages/AbstractPipelineStage.java | 33 ++ .../pipeline/stages/AbstractPixelStage.java | 30 ++ .../imgchain/pipeline/stages/BrightnessStage.java | 141 +++++++ .../imgchain/pipeline/stages/ColorSkewStage.java | 141 +++++++ src/bjc/imgchain/pipeline/stages/GaussStage.java | 110 ++++++ .../imgchain/pipeline/stages/GreyscaleStage.java | 45 +++ src/bjc/imgchain/pipeline/stages/IDStage.java | 44 +++ .../imgchain/pipeline/stages/NegativeStage.java | 33 ++ src/bjc/imgchain/pipeline/stages/StagePicker.java | 68 ++++ src/bjc/imgchain/utils/ImmutableTableModel.java | 17 + src/bjc/imgchain/utils/LabeledInputPanel.java | 42 +++ src/bjc/imgchain/utils/Utils.java | 54 +++ 20 files changed, 1666 insertions(+), 326 deletions(-) create mode 100644 src/bjc/imgchain/ImgPipeline.java create mode 100644 src/bjc/imgchain/pipeline/MutablePipeline.java create mode 100644 src/bjc/imgchain/pipeline/Pipeline.java create mode 100644 src/bjc/imgchain/pipeline/PipelinePicker.java create mode 100644 src/bjc/imgchain/pipeline/PipelineStage.java create mode 100644 src/bjc/imgchain/pipeline/StageType.java create mode 100644 src/bjc/imgchain/pipeline/stages/AbstractPipelineStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/AbstractPixelStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/BrightnessStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/ColorSkewStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/GaussStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/GreyscaleStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/IDStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/NegativeStage.java create mode 100644 src/bjc/imgchain/pipeline/stages/StagePicker.java create mode 100644 src/bjc/imgchain/utils/ImmutableTableModel.java create mode 100644 src/bjc/imgchain/utils/LabeledInputPanel.java create mode 100644 src/bjc/imgchain/utils/Utils.java (limited to 'src/bjc/imgchain') diff --git a/src/bjc/imgchain/ImgChain.java b/src/bjc/imgchain/ImgChain.java index f90ae78..e4e32cc 100644 --- a/src/bjc/imgchain/ImgChain.java +++ b/src/bjc/imgchain/ImgChain.java @@ -1,126 +1,211 @@ -package bjc.imgchain; - -import java.awt.GridLayout; -import java.awt.Image; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -import javax.swing.JDesktopPane; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; - -public class ImgChain { - private final class LoadImageListener implements ActionListener { - @Override - public void actionPerformed(ActionEvent ev) { - JFileChooser jfc = new JFileChooser(); - jfc.setMultiSelectionEnabled(true); - - int res = jfc.showOpenDialog(desktop); - - if (res != JFileChooser.APPROVE_OPTION) { - return; - } - - for (File fle : jfc.getSelectedFiles()) { - ImgViewer view = new ImgViewer(ImgChain.this, fle); - - if (view.isInitialized()) { - desktop.add(view); - view.setVisible(true); - } - } - } - } - - private JDesktopPane desktop; - - private Map imageRepo; - - public static void main(String[] args) { - System.out.println("ImgChain Loading..."); - - ImgChain chn = new ImgChain(); - - chn.setupGUI(); - } - - public ImgChain() { - imageRepo = new HashMap<>(); - } - - private void setupGUI() { - JFrame frame = new JFrame("ImgChain v1"); - frame.setLayout(new GridLayout(1, 1)); - - desktop = new JDesktopPane(); - - JMenuBar menu = setupMenubar(frame); - - frame.setJMenuBar(menu); - - frame.add(desktop); - - frame.setSize(640, 480); - - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - - frame.setVisible(true); - } - - private JMenuBar setupMenubar(JFrame frame) { - JMenuBar menu = new JMenuBar(); - - JMenu fileMenu = new JMenu("File"); - fileMenu.setMnemonic('F'); - - JMenuItem aboutItem = new JMenuItem("About"); - aboutItem.setMnemonic('A'); - aboutItem.addActionListener((ev) -> { - JOptionPane.showMessageDialog(frame, "ImgChain v1\nDeveloped by Benjamin Culkin", - "About ImgChain", JOptionPane.INFORMATION_MESSAGE); - }); - - JMenuItem closeItem = new JMenuItem("Close"); - closeItem.setMnemonic('C'); - closeItem.addActionListener((ev) -> { - frame.dispose(); - }); - - fileMenu.add(aboutItem); - fileMenu.addSeparator(); - fileMenu.add(closeItem); - - JMenu imageMenu = new JMenu("Images"); - imageMenu.setMnemonic('I'); - - JMenuItem loadImage = new JMenuItem("Load Images..."); - loadImage.setMnemonic('L'); - loadImage.addActionListener(new LoadImageListener()); - - imageMenu.add(loadImage); - - menu.add(fileMenu); - menu.add(imageMenu); - return menu; - } - - public void addImage(String name, Image img) { - if (imageRepo.containsKey(name)) { - String msg = String.format("Are you sure you want to overwrite stored image '%s'?", name); - - if (JOptionPane.showInternalConfirmDialog(desktop, msg) != JOptionPane.OK_OPTION) { - return; - } - } - - imageRepo.put(name, img); - } -} +package bjc.imgchain; + +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JDesktopPane; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; + +import bjc.imgchain.pipeline.Pipeline; + +/** + * Main class for ImgChain + * + * @author acm + * + */ +public class ImgChain { + /* + * Action to perform when loading an image. + */ + private final class LoadImageListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent ev) { + JFileChooser jfc = new JFileChooser(); + jfc.setMultiSelectionEnabled(true); + + int res = jfc.showOpenDialog(desktop); + + if (res != JFileChooser.APPROVE_OPTION) { + return; + } + + for (File fle : jfc.getSelectedFiles()) { + ImgViewer view = new ImgViewer(ImgChain.this, fle); + + if (view.isInitialized()) { + desktop.add(view); + view.setVisible(true); + } + } + } + } + + /** + * The desktop everything is attached to. + */ + public JDesktopPane desktop; + + /* + * The storage for images. + */ + public final Map imageRepo; + + /* + * The storage for images. + */ + public final Map pipelineRepo; + + /** + * The image chain instance. + */ + public static ImgChain chan; + + /** + * Main method + * + * @param args + * Unused CLI args + */ + public static void main(String[] args) { + System.out.println("ImgChain Loading..."); + + chan = new ImgChain(); + + chan.setupGUI(); + } + + /** + * Initialize image repo + */ + public ImgChain() { + imageRepo = new HashMap<>(); + pipelineRepo = new HashMap<>(); + } + + /* + * Setup the GUI + */ + private void setupGUI() { + JFrame frame = new JFrame("ImgChain v1"); + frame.setLayout(new GridLayout(1, 1)); + + desktop = new JDesktopPane(); + + JMenuBar menu = setupMenubar(frame); + + frame.setJMenuBar(menu); + + frame.add(desktop); + + frame.setSize(640, 480); + + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + frame.setVisible(true); + } + + /* + * Setup the menubar. + */ + private JMenuBar setupMenubar(JFrame frame) { + JMenuBar menu = new JMenuBar(); + + JMenu fileMenu = new JMenu("File"); + fileMenu.setMnemonic('F'); + + JMenuItem aboutItem = new JMenuItem("About"); + aboutItem.setMnemonic('A'); + aboutItem.addActionListener((ev) -> { + JOptionPane.showMessageDialog(frame, "ImgChain v2\nDeveloped by Benjamin Culkin", + "About ImgChain", JOptionPane.INFORMATION_MESSAGE); + }); + + JMenuItem closeItem = new JMenuItem("Close"); + closeItem.setMnemonic('C'); + closeItem.addActionListener((ev) -> { + frame.dispose(); + }); + + fileMenu.add(aboutItem); + fileMenu.addSeparator(); + fileMenu.add(closeItem); + + JMenu imageMenu = new JMenu("Images"); + imageMenu.setMnemonic('I'); + + JMenuItem loadImage = new JMenuItem("Load Images..."); + loadImage.setMnemonic('L'); + loadImage.addActionListener(new LoadImageListener()); + + imageMenu.add(loadImage); + + JMenu pipelineMenu = new JMenu("Pipelines"); + JMenuItem createPipe = new JMenuItem("Create Pipeline"); + createPipe.setMnemonic('C'); + createPipe.addActionListener((ev) -> { + ImgPipeline pip = new ImgPipeline(); + + desktop.add(pip); + pip.setVisible(true); + }); + + pipelineMenu.add(createPipe); + + menu.add(fileMenu); + menu.add(imageMenu); + menu.add(pipelineMenu); + + return menu; + } + + /** + * Add an image to the image repository. + * + * @param name + * The name of the image + * @param img + * The image to add. + */ + public void addImage(String name, Image img) { + if (imageRepo.containsKey(name)) { + String msg = String.format("Are you sure you want to overwrite stored image '%s'?", name); + + if (JOptionPane.showInternalConfirmDialog(desktop, msg) != JOptionPane.OK_OPTION) { + return; + } + } + + imageRepo.put(name, img); + } + + /** + * Add an pipeline to the pipeline repository. + * + * @param pipe + * The pipeline to add. + */ + public void addPipe(Pipeline pipe) { + String name = pipe.name(); + + if (pipelineRepo.containsKey(name)) { + String msg = String.format("Are you sure you want to overwrite stored pipeline '%s'?", name); + + if (JOptionPane.showInternalConfirmDialog(desktop, msg) != JOptionPane.OK_OPTION) { + return; + } + } + + pipelineRepo.put(name, pipe); + } +} diff --git a/src/bjc/imgchain/ImgPipeline.java b/src/bjc/imgchain/ImgPipeline.java new file mode 100644 index 0000000..8dbbebf --- /dev/null +++ b/src/bjc/imgchain/ImgPipeline.java @@ -0,0 +1,206 @@ +package bjc.imgchain; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.MessageFormat; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JInternalFrame; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.border.BevelBorder; +import javax.swing.border.CompoundBorder; +import javax.swing.border.TitledBorder; + +import bjc.imgchain.pipeline.MutablePipeline; +import bjc.imgchain.pipeline.PipelineStage; +import bjc.imgchain.pipeline.stages.ColorSkewStage; +import bjc.imgchain.pipeline.stages.GaussStage; +import bjc.imgchain.pipeline.stages.GreyscaleStage; +import bjc.imgchain.pipeline.stages.IDStage; +import bjc.imgchain.pipeline.stages.NegativeStage; +import bjc.imgchain.pipeline.stages.StagePicker; + +/** + * Edit an image pipeline. + * + * @author acm + * + */ +public class ImgPipeline extends JInternalFrame { + private static final long serialVersionUID = 2252473157369730085L; + + private MutablePipeline editing; + + /** + * Create a editor for a new image pipeline. + */ + public ImgPipeline() { + super("Pipeline Editor - New pipeline", true, true, true, true); + + editing = new MutablePipeline(); + + setupGUI(); + } + + /** + * Create an editor for an existing pipeline. + * + * @param pipe + * The pipeline to edit. + */ + public ImgPipeline(MutablePipeline pipe) { + super("Pipeline Editor - New pipeline", true, true, true, true); + + editing = pipe; + + setupGUI(); + } + + private void setupGUI() { + setSize(320, 320); + setLayout(new GridLayout(1, 1)); + + JPanel stageEditor = new JPanel(); + stageEditor.setLayout(new BorderLayout()); + TitledBorder border = new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "No Stage Selected"); + stageEditor.setBorder(border); + + DefaultListModel mod = new DefaultListModel<>(); + for (PipelineStage stage : editing.stages()) { + mod.addElement(stage); + } + + JList stageList = new JList<>(mod); + stageList.addListSelectionListener(e -> { + if (e.getValueIsAdjusting()) + return; + + PipelineStage stag = stageList.getSelectedValue(); + + stageEditor.removeAll(); + + stageEditor.add(stag.getEditor(), BorderLayout.CENTER); + border.setTitle(stag.name()); + + // stageEditor.repaint(); + }); + JScrollPane stageScroll = new JScrollPane(stageList); + + JPanel listPanel = new JPanel(); + listPanel.setLayout(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(2, 1)); + + JPanel stageButtons = new JPanel(); + stageButtons.setLayout(new GridLayout(1, 2)); + + JButton addStage = new JButton("Add Stage"); + addStage.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + StagePicker pick = new StagePicker(); + pick.pack(); + pick.setVisible(true); + + if (pick.stageName == null) { + System.out.println("WARN: picked null stage"); + return; + } + + PipelineStage stag; + /* + * :AddStage + */ + switch (pick.stageName) { + case "Identity": { + stag = new IDStage(); + } + break; + case "Greyscale": { + stag = new GreyscaleStage(); + } + break; + case "Color Skew": { + stag = new ColorSkewStage(); + } + break; + case "Sepia": { + /* + * NOTE: these values were pulled from somewhere on the internet, and tweaked + * slightly to work better. + */ + stag = new ColorSkewStage(.393, .769, .189, .349, .686, .168, .272, .534, .131); + } + case "Negative": { + stag = new NegativeStage(); + } + break; + case "Gaussian Blur": { + stag = new GaussStage(); + } + break; + default: + JOptionPane.showMessageDialog(ImgChain.chan.desktop, String + .format("Attempted to add unknown stage '%s'", pick.stageName)); + return; + } + + editing.addStage(stag); + mod.addElement(stag); + + stageList.setSelectedValue(stag, true); + } + }); + + JButton delStage = new JButton("Delete Stage"); + delStage.addActionListener(e -> { + int res = JOptionPane.showInternalConfirmDialog(this, + "Are you sure you want to remove the stage?"); + + if (res != JOptionPane.YES_OPTION) { + return; + } + + int idx = stageList.getSelectedIndex(); + + mod.remove(idx); + editing.removeStage(idx); + + stageEditor.removeAll(); + border.setTitle("No Stage Selected"); + }); + + stageButtons.add(addStage); + stageButtons.add(delStage); + + JButton savePipeline = new JButton("Save Pipeline"); + savePipeline.addActionListener((ev) -> { + String newName = JOptionPane.showInternalInputDialog(this, "Enter Pipeline Name"); + + editing.name(newName); + + ImgChain.chan.addPipe(editing); + + setTitle("Pipeline Editor - " + newName); + }); + + buttonPanel.add(stageButtons); + buttonPanel.add(savePipeline); + + listPanel.add(stageScroll, BorderLayout.CENTER); + listPanel.add(buttonPanel, BorderLayout.PAGE_END); + + JSplitPane main = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listPanel, stageEditor); + + add(main); + } +} diff --git a/src/bjc/imgchain/ImgViewer.java b/src/bjc/imgchain/ImgViewer.java index 39586a8..06c2a1e 100644 --- a/src/bjc/imgchain/ImgViewer.java +++ b/src/bjc/imgchain/ImgViewer.java @@ -1,200 +1,216 @@ -package bjc.imgchain; - -import java.awt.GridLayout; -import java.awt.Image; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; - -import javax.imageio.ImageIO; -import javax.swing.*; - -import bjc.imgchain.utils.SimpleInputPanel; - -public class ImgViewer extends JInternalFrame { - private final class ScaleImageListener implements ActionListener { - @Override - public void actionPerformed(ActionEvent ev) { - int height = Integer.parseInt(JOptionPane.showInputDialog("Enter new image height")); - int width = Integer.parseInt(JOptionPane.showInputDialog("Enter new image width")); - - icon.setImage(icon.getImage().getScaledInstance(width, height, Image.SCALE_DEFAULT)); - - repaint(); - } - } - - private final class ReloadImageListener implements ActionListener { - private final File img; - - private ReloadImageListener(File img) { - this.img = img; - } - - @Override - public void actionPerformed(ActionEvent ev) { - try { - icon.setImage(ImageIO.read(img)); - } catch (IOException e) { - String msg = String.format("Error: Could not load image %s", img.getPath()); - - System.out.printf("%s\n", msg); - - e.printStackTrace(); - - JOptionPane.showInternalMessageDialog(ImgViewer.this, msg, "Error loading image", - JOptionPane.ERROR_MESSAGE); - } - } - } - - private final class ChangeImageListener implements ActionListener { - @Override - public void actionPerformed(ActionEvent ev) { - JFileChooser jfc = new JFileChooser(); - jfc.setMultiSelectionEnabled(false); - - int res = jfc.showOpenDialog(ImgViewer.this); - - if (res != JFileChooser.APPROVE_OPTION) { - return; - } - - try { - File tmp = jfc.getSelectedFile(); - - icon.setImage(ImageIO.read(tmp)); - - img = tmp; - } catch (IOException e) { - String msg = String.format("Error: Could not load image %s", img.getPath()); - - System.out.printf("%s\n", msg); - - e.printStackTrace(); - - JOptionPane.showInternalMessageDialog(null, msg, "Error loading image", - JOptionPane.ERROR_MESSAGE); - } - } - } - - private static final long serialVersionUID = -4727584150861526435L; - - private File img; - - private boolean initted; - - private ImageIcon icon; - - private ImgChain desktop; - - public ImgViewer(ImgChain desk, File img) { - super("Image Viewer - " + img.getName(), true, true, true, true); - initted = false; - - this.img = img; - this.desktop = desk; - - initted = setupGUI(img); - } - - private boolean setupGUI(File img) { - setSize(320, 240); - setLayout(new GridLayout(1, 1)); - - JMenuBar bar = setupMenubar(img); - - setJMenuBar(bar); - - JLabel lab = loadLabel(img); - if (lab == null) { - return false; - } - - add(lab); - - return true; - } - - private JMenuBar setupMenubar(File img) { - JMenuBar bar = new JMenuBar(); - - JMenu fileMenu = new JMenu("File"); - fileMenu.setMnemonic('F'); - - JMenuItem changeImage = new JMenuItem("Change Image"); - changeImage.setMnemonic('C'); - changeImage.addActionListener(new ChangeImageListener()); - - JMenuItem reloadImage = new JMenuItem("Reload Image"); - reloadImage.setMnemonic('R'); - reloadImage.addActionListener(new ReloadImageListener(img)); - - JMenuItem storeImage = new JMenuItem("Store Image"); - storeImage.setMnemonic('S'); - storeImage.addActionListener((ev) -> { - String inp = JOptionPane.showInternalInputDialog(this, "Enter name to store image under"); - - if(inp == null) return; - - inp = inp.trim(); - - if(inp.equals("")) { - return; - } - - desktop.addImage(inp, icon.getImage()); - }); - - fileMenu.add(changeImage); - fileMenu.add(reloadImage); - fileMenu.addSeparator(); - fileMenu.add(storeImage); - - JMenu editMenu = new JMenu("Edit Image"); - editMenu.setMnemonic('E'); - - JMenuItem scaleImage = new JMenuItem("Scale Image"); - scaleImage.setMnemonic('S'); - scaleImage.addActionListener(new ScaleImageListener()); - - editMenu.add(scaleImage); - - bar.add(fileMenu); - bar.add(editMenu); - return bar; - } - - private JLabel loadLabel(File img) { - JLabel lab = null; - - try { - URL imgURL = img.toURI().toURL(); - - icon = new ImageIcon(imgURL); - - setSize(icon.getIconWidth(), icon.getIconHeight()); - - lab = new JLabel(icon); - } catch (MalformedURLException e) { - String msg = String.format("Error: Could not load image %s", img.getPath()); - - System.out.printf("%s\n", msg); - - e.printStackTrace(); - - JOptionPane.showInternalMessageDialog(this, msg, "Error loading image", - JOptionPane.ERROR_MESSAGE); - } - - return lab; - } - - public boolean isInitialized() { - return initted; - } -} +package bjc.imgchain; + +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.imageio.ImageIO; +import javax.swing.*; + +import bjc.imgchain.pipeline.Pipeline; +import bjc.imgchain.pipeline.PipelinePicker; +import bjc.imgchain.utils.SimpleInputPanel; +import bjc.imgchain.utils.Utils; + +public class ImgViewer extends JInternalFrame { + private final class ReloadImageListener implements ActionListener { + private final File img; + + private ReloadImageListener(File img) { + this.img = img; + } + + @Override + public void actionPerformed(ActionEvent ev) { + try { + icon.setImage(ImageIO.read(img)); + } catch (IOException e) { + String msg = String.format("Error: Could not load image %s", img.getPath()); + + System.out.printf("%s\n", msg); + + e.printStackTrace(); + + JOptionPane.showInternalMessageDialog(ImgViewer.this, msg, "Error loading image", + JOptionPane.ERROR_MESSAGE); + } + } + } + + private final class ChangeImageListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent ev) { + JFileChooser jfc = new JFileChooser(); + jfc.setMultiSelectionEnabled(false); + + int res = jfc.showOpenDialog(ImgViewer.this); + + if (res != JFileChooser.APPROVE_OPTION) { + return; + } + + try { + File tmp = jfc.getSelectedFile(); + + icon.setImage(ImageIO.read(tmp)); + + img = tmp; + + setTitle("Image Viewer - " + img.getName()); + } catch (IOException e) { + String msg = String.format("Error: Could not load image %s", img.getPath()); + + System.out.printf("%s\n", msg); + + e.printStackTrace(); + + JOptionPane.showInternalMessageDialog(null, msg, "Error loading image", + JOptionPane.ERROR_MESSAGE); + } + } + } + + private static final long serialVersionUID = -4727584150861526435L; + + private File img; + + private boolean initted; + + private ImageIcon icon; + + private ImgChain desktop; + + private JLabel lab; + + public ImgViewer(ImgChain desk, File img) { + super("Image Viewer - " + img.getName(), true, true, true, true); + initted = false; + + this.img = img; + this.desktop = desk; + + initted = setupGUI(img); + } + + private boolean setupGUI(File img) { + setSize(320, 240); + setLayout(new GridLayout(1, 1)); + + JMenuBar bar = setupMenubar(img); + + setJMenuBar(bar); + + lab = loadLabel(img); + if (lab == null) { + return false; + } + + add(lab); + + return true; + } + + private JMenuBar setupMenubar(File img) { + JMenuBar bar = new JMenuBar(); + + JMenu fileMenu = new JMenu("File"); + fileMenu.setMnemonic('F'); + + JMenuItem changeImage = new JMenuItem("Change Image"); + changeImage.setMnemonic('C'); + changeImage.addActionListener(new ChangeImageListener()); + + JMenuItem reloadImage = new JMenuItem("Reload Image"); + reloadImage.setMnemonic('R'); + reloadImage.addActionListener(new ReloadImageListener(img)); + + JMenuItem storeImage = new JMenuItem("Store Image"); + storeImage.setMnemonic('S'); + storeImage.addActionListener((ev) -> { + String inp = JOptionPane.showInternalInputDialog(this, "Enter name to store image under"); + + if (inp == null) + return; + + inp = inp.trim(); + + if (inp.equals("")) { + return; + } + + desktop.addImage(inp, icon.getImage()); + }); + + fileMenu.add(changeImage); + fileMenu.add(reloadImage); + fileMenu.addSeparator(); + fileMenu.add(storeImage); + + JMenu editMenu = new JMenu("Edit Image"); + editMenu.setMnemonic('E'); + + JMenuItem applyPipe = new JMenuItem("Apply Pipeline"); + applyPipe.setMnemonic('A'); + applyPipe.addActionListener((ev) -> { + PipelinePicker pick = new PipelinePicker(); + + pick.pack(); + pick.setVisible(true); + + if (pick.pipeName == null) { + System.out.println("WARN: picked null pipeline"); + return; + } + + Pipeline pipeline = ImgChain.chan.pipelineRepo.get(pick.pipeName); + + icon.setImage(pipeline.process(Utils.toBuffered(icon.getImage()))); + lab.repaint(); + }); + + editMenu.addSeparator(); + editMenu.add(applyPipe); + + bar.add(fileMenu); + bar.add(editMenu); + + return bar; + } + + private JLabel loadLabel(File img) { + JLabel lab = null; + + try { + URL imgURL = img.toURI().toURL(); + + icon = new ImageIcon(imgURL); + + setSize(icon.getIconWidth(), icon.getIconHeight()); + + lab = new JLabel(icon); + } catch (MalformedURLException e) { + String msg = String.format("Error: Could not load image %s", img.getPath()); + + System.out.printf("%s\n", msg); + + e.printStackTrace(); + + JOptionPane.showInternalMessageDialog(this, msg, "Error loading image", + JOptionPane.ERROR_MESSAGE); + } + + return lab; + } + + public boolean isInitialized() { + return initted; + } +} diff --git a/src/bjc/imgchain/pipeline/MutablePipeline.java b/src/bjc/imgchain/pipeline/MutablePipeline.java new file mode 100644 index 0000000..6272b30 --- /dev/null +++ b/src/bjc/imgchain/pipeline/MutablePipeline.java @@ -0,0 +1,102 @@ +package bjc.imgchain.pipeline; + +import java.awt.Image; +import java.util.ArrayList; +import java.util.List; + +/** + * An editable {@link Pipeline} + * + * @author acm + * + */ +public class MutablePipeline implements Pipeline { + private final List stages; + + private String name; + + /** + * Create a new unnamed mutable pipeline. + */ + public MutablePipeline() { + stages = new ArrayList<>(); + + name = "Unnamed Pipeline"; + } + + /** + * Create a new named mutable pipeline. + * + * @param name + * The name of the pipeline. + */ + public MutablePipeline(String name) { + stages = new ArrayList<>(); + + this.name = name; + } + + @Override + public Image process(Image input) { + Image proc = input; + + for (PipelineStage stage : stages) { + System.out.println("Applying stage " + stage.name()); + proc = stage.process(proc); + System.out.println("Applied stage " + stage.name()); + } + + return proc; + } + + @Override + public List stages() { + return stages; + } + + @Override + public String name() { + return name; + } + + /** + * Set the name of the pipeline. + * + * @param nam + * The name of the pipeline. + */ + public void name(String nam) { + name = nam; + } + + /** + * Append a pipeline stage to the end of this pipeline. + * + * @param stag + * The stage to add. + */ + public void addStage(PipelineStage stag) { + stages.add(stag); + } + + /** + * Remove a pipeline stage. + * + * @param stag + * The stage to remove. + */ + public void removeStage(PipelineStage stag) { + stages.remove(stag); + } + + /** + * Remove a pipeline stage by index. + * + * @param idx + * The index of the stage to remove. + */ + public void removeStage(int idx) { + stages.remove(idx); + } + +} diff --git a/src/bjc/imgchain/pipeline/Pipeline.java b/src/bjc/imgchain/pipeline/Pipeline.java new file mode 100644 index 0000000..faba6d5 --- /dev/null +++ b/src/bjc/imgchain/pipeline/Pipeline.java @@ -0,0 +1,37 @@ +package bjc.imgchain.pipeline; + +import java.awt.Image; +import java.util.List; + +/** + * Represents a pipeline for processing images. + * + * @author acm + * + */ +public interface Pipeline { + /** + * Process an image using the stages. + * + * @param input + * The input image, or null if no image is input. + * @return The output image, or null if no image is output. + */ + Image process(Image input); + + /** + * Get the stages of the pipeline. + * + * @return The stages of the pipeline. + */ + List stages(); + + /** + * Get the name of the pipeline. + * + * @return The name of the pipeline. + */ + default String name() { + return "Unnamed Pipeline"; + } +} diff --git a/src/bjc/imgchain/pipeline/PipelinePicker.java b/src/bjc/imgchain/pipeline/PipelinePicker.java new file mode 100644 index 0000000..92478a6 --- /dev/null +++ b/src/bjc/imgchain/pipeline/PipelinePicker.java @@ -0,0 +1,63 @@ +package bjc.imgchain.pipeline; + +import java.awt.BorderLayout; +import java.awt.GridLayout; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import bjc.imgchain.ImgChain; + +public class PipelinePicker extends JDialog { + private static final long serialVersionUID = 1L; + + public String pipeName; + + public PipelinePicker() { + super(); + + setModalityType(ModalityType.APPLICATION_MODAL); + setTitle("Add a Stage"); + + setupGUI(); + } + + private void setupGUI() { + setLayout(new BorderLayout()); + + DefaultListModel pipeModel = new DefaultListModel<>(); + for (String pipeName : ImgChain.chan.pipelineRepo.keySet()) { + pipeModel.addElement(pipeName); + } + + JList pipeList = new JList<>(pipeModel); + JScrollPane listScroll = new JScrollPane(pipeList); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(2, 1)); + + JButton addStage = new JButton("Select Pipe"); + addStage.addActionListener((ev) -> { + pipeName = pipeList.getSelectedValue(); + + setVisible(false); + dispose(); + }); + + JButton cancel = new JButton("Cancel"); + cancel.addActionListener((ev) -> { + setVisible(false); + dispose(); + }); + + buttonPanel.add(addStage); + buttonPanel.add(cancel); + + add(listScroll, BorderLayout.CENTER); + add(buttonPanel, BorderLayout.PAGE_END); + } +} diff --git a/src/bjc/imgchain/pipeline/PipelineStage.java b/src/bjc/imgchain/pipeline/PipelineStage.java new file mode 100644 index 0000000..46966c3 --- /dev/null +++ b/src/bjc/imgchain/pipeline/PipelineStage.java @@ -0,0 +1,52 @@ +package bjc.imgchain.pipeline; + +import java.awt.Image; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +/** + * A stage in an image processing pipeline. + * + * @author acm + * + */ +public interface PipelineStage { + /** + * Get the input/output type of this stage. + * + * @return The type of the stage. + * + */ + StageType getType(); + + /** + * Pass an image through the stage. + * + * @param inp + * The input image, if one is needed + * @return The output image, if one is provided. + */ + Image process(Image inp); + + /** + * Get the name of this stage. + * + * @return The name of this stage. + */ + String name(); + + /** + * Get a brief description of what this stage does. + * + * @return A brief description of what the stage does. + */ + String description(); + + /** + * Get an editor for this stage. + * + * @return + */ + JComponent getEditor(); +} diff --git a/src/bjc/imgchain/pipeline/StageType.java b/src/bjc/imgchain/pipeline/StageType.java new file mode 100644 index 0000000..f54588d --- /dev/null +++ b/src/bjc/imgchain/pipeline/StageType.java @@ -0,0 +1,21 @@ +package bjc.imgchain.pipeline; + +/** + * The type of stage a given {@link PipelineStage} is. + * @author acm + * + */ +public enum StageType { + /** + * No image -> Image + */ + IMGSOURCE, + /** + * Image -> Image + */ + IMGTRANS, + /** + * Image -> No image + */ + IMGSINK +} diff --git a/src/bjc/imgchain/pipeline/stages/AbstractPipelineStage.java b/src/bjc/imgchain/pipeline/stages/AbstractPipelineStage.java new file mode 100644 index 0000000..4183f4f --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/AbstractPipelineStage.java @@ -0,0 +1,33 @@ +package bjc.imgchain.pipeline.stages; + +import bjc.imgchain.pipeline.PipelineStage; +import bjc.imgchain.pipeline.StageType; + +/** + * Abstract base class for stages. + * + * @author acm + * + */ +public abstract class AbstractPipelineStage implements PipelineStage { + private final StageType type; + + protected AbstractPipelineStage(StageType type) { + this.type = type; + } + + @Override + public StageType getType() { + return type; + } + + @Override + public String description() { + return name(); + } + + @Override + public String toString() { + return name(); + } +} diff --git a/src/bjc/imgchain/pipeline/stages/AbstractPixelStage.java b/src/bjc/imgchain/pipeline/stages/AbstractPixelStage.java new file mode 100644 index 0000000..44e3c60 --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/AbstractPixelStage.java @@ -0,0 +1,30 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.Image; +import java.awt.image.BufferedImage; + +import bjc.imgchain.pipeline.StageType; +import bjc.imgchain.utils.Utils; + +public abstract class AbstractPixelStage extends AbstractPipelineStage { + + protected AbstractPixelStage(StageType type) { + super(type); + } + + @Override + public Image process(Image inp) { + BufferedImage buf = (BufferedImage) inp; + + for (int y = 0; y < buf.getHeight(); y++) { + for (int x = 0; x < buf.getWidth(); x++) { + int[] pix = Utils.toARGBQuad(buf.getRGB(x, y)); + buf.setRGB(x, y, Utils.fromARGBQuad(pix)); + } + } + + return buf; + } + + public abstract int[] processPixel(int[] pix); +} diff --git a/src/bjc/imgchain/pipeline/stages/BrightnessStage.java b/src/bjc/imgchain/pipeline/stages/BrightnessStage.java new file mode 100644 index 0000000..4f0feb2 --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/BrightnessStage.java @@ -0,0 +1,141 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.GridLayout; +import java.awt.Image; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; +import javax.swing.border.TitledBorder; + +import bjc.imgchain.pipeline.StageType; +import bjc.imgchain.utils.LabeledInputPanel; + +public class BrightnessStage extends AbstractPixelStage { + public BrightnessStage() { + this(1, 0, 0, 0, 1, 0, 0, 0, 1); + } + + public BrightnessStage(int rr, int rg, int rb, int gr, int gg, int gb, + int br, int bg, int bb) { + super(StageType.IMGTRANS); + + this.rr = rr; + this.rg = rg; + this.rb = rb; + this.gr = gr; + this.gg = gg; + this.gb = gb; + this.br = br; + this.bg = bg; + this.bb = bb; + } + + + private int rr, rg, rb; + private int gr, gg, gb; + private int br, bg, bb; + + @Override + public int[] processPixel(int[] pix) { + int[] ret = new int[4]; + + ret[0] = pix[0]; + ret[1] = (int) ((pix[1] + rr) + (pix[2] + rg) + (pix[3] + rb)); + ret[2] = (int) ((pix[1] + gr) + (pix[2] + gg) + (pix[3] + gb)); + ret[3] = (int) ((pix[1] + br) + (pix[2] + bg) + (pix[3] + bb)); + + ret[1] = Math.max(0, Math.min(255, ret[1])); + ret[2] = Math.max(0, Math.min(255, ret[2])); + ret[3] = Math.max(0, Math.min(255, ret[3])); + + return ret; + } + + @Override + public String name() { + return "Tint"; + } + + @Override + public String description() { + return "Add/remove colors"; + } + + @Override + public JComponent getEditor() { + JPanel holder = new JPanel(); + holder.setLayout(new GridLayout(3, 1)); + + JPanel rSkew = new JPanel(); + rSkew.setLayout(new GridLayout(1, 3)); + rSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Red Balance")); + + JPanel gSkew = new JPanel(); + gSkew.setLayout(new GridLayout(1, 3)); + gSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Green Balance")); + + JPanel bSkew = new JPanel(); + bSkew.setLayout(new GridLayout(1, 3)); + bSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Blue Balance")); + + + LabeledInputPanel rpercRed = new LabeledInputPanel("% Red", rr); + LabeledInputPanel rpercGreen = new LabeledInputPanel("% Green", rg); + LabeledInputPanel rpercBlue = new LabeledInputPanel("% Blue", rb); + rSkew.add(rpercRed); + rSkew.add(rpercGreen); + rSkew.add(rpercBlue); + + LabeledInputPanel gpercRed = new LabeledInputPanel("% Red", gr); + LabeledInputPanel gpercGreen = new LabeledInputPanel("% Green", gg); + LabeledInputPanel gpercBlue = new LabeledInputPanel("% Blue", gb); + gSkew.add(gpercRed); + gSkew.add(gpercGreen); + gSkew.add(gpercBlue); + + LabeledInputPanel bpercRed = new LabeledInputPanel("% Red", br); + LabeledInputPanel bpercGreen = new LabeledInputPanel("% Green", bg); + LabeledInputPanel bpercBlue = new LabeledInputPanel("% Blue", bb); + bSkew.add(bpercRed); + bSkew.add(bpercGreen); + bSkew.add(bpercBlue); + + rpercRed.addPropertyChangeListener("value", (ev) -> { + rr = (Integer)rpercRed.field.getValue(); + }); + gpercRed.addPropertyChangeListener("value", (ev) -> { + gr = (Integer)gpercRed.field.getValue(); + }); + bpercRed.addPropertyChangeListener("value", (ev) -> { + br = (Integer)bpercRed.field.getValue(); + }); + + rpercGreen.addPropertyChangeListener("value", (ev) -> { + rg = (Integer)rpercGreen.field.getValue(); + }); + gpercGreen.addPropertyChangeListener("value", (ev) -> { + gg = (Integer)gpercGreen.field.getValue(); + }); + bpercGreen.addPropertyChangeListener("value", (ev) -> { + bg = (Integer)bpercGreen.field.getValue(); + }); + + rpercBlue.addPropertyChangeListener("value", (ev) -> { + rb = (Integer)rpercBlue.field.getValue(); + }); + gpercBlue.addPropertyChangeListener("value", (ev) -> { + gb = (Integer)gpercBlue.field.getValue(); + }); + bpercBlue.addPropertyChangeListener("value", (ev) -> { + bb = (Integer)bpercBlue.field.getValue(); + }); + + holder.add(rSkew); + holder.add(gSkew); + holder.add(bSkew); + + return holder; + } + +} \ No newline at end of file diff --git a/src/bjc/imgchain/pipeline/stages/ColorSkewStage.java b/src/bjc/imgchain/pipeline/stages/ColorSkewStage.java new file mode 100644 index 0000000..ba9d127 --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/ColorSkewStage.java @@ -0,0 +1,141 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.GridLayout; +import java.awt.Image; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; +import javax.swing.border.TitledBorder; + +import bjc.imgchain.pipeline.StageType; +import bjc.imgchain.utils.LabeledInputPanel; + +public class ColorSkewStage extends AbstractPixelStage { + public ColorSkewStage() { + this(1, 0, 0, 0, 1, 0, 0, 0, 1); + } + + public ColorSkewStage(double rr, double rg, double rb, double gr, double gg, double gb, + double br, double bg, double bb) { + super(StageType.IMGTRANS); + + this.rr = rr; + this.rg = rg; + this.rb = rb; + this.gr = gr; + this.gg = gg; + this.gb = gb; + this.br = br; + this.bg = bg; + this.bb = bb; + } + + + private double rr, rg, rb; + private double gr, gg, gb; + private double br, bg, bb; + + @Override + public int[] processPixel(int[] pix) { + int[] ret = new int[4]; + + ret[0] = pix[0]; + ret[1] = (int) ((pix[1] * rr) + (pix[2] * rg) + (pix[3] * rb)); + ret[2] = (int) ((pix[1] * gr) + (pix[2] * gg) + (pix[3] * gb)); + ret[3] = (int) ((pix[1] * br) + (pix[2] * bg) + (pix[3] * bb)); + + ret[1] = Math.min(255, ret[1]); + ret[2] = Math.min(255, ret[2]); + ret[3] = Math.min(255, ret[3]); + + return ret; + } + + @Override + public String name() { + return "Color Skew"; + } + + @Override + public String description() { + return "Adjust color balance"; + } + + @Override + public JComponent getEditor() { + JPanel holder = new JPanel(); + holder.setLayout(new GridLayout(3, 1)); + + JPanel rSkew = new JPanel(); + rSkew.setLayout(new GridLayout(1, 3)); + rSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Red Balance")); + + JPanel gSkew = new JPanel(); + gSkew.setLayout(new GridLayout(1, 3)); + gSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Green Balance")); + + JPanel bSkew = new JPanel(); + bSkew.setLayout(new GridLayout(1, 3)); + bSkew.setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Blue Balance")); + + + LabeledInputPanel rpercRed = new LabeledInputPanel("% Red", rr); + LabeledInputPanel rpercGreen = new LabeledInputPanel("% Green", rg); + LabeledInputPanel rpercBlue = new LabeledInputPanel("% Blue", rb); + rSkew.add(rpercRed); + rSkew.add(rpercGreen); + rSkew.add(rpercBlue); + + LabeledInputPanel gpercRed = new LabeledInputPanel("% Red", gr); + LabeledInputPanel gpercGreen = new LabeledInputPanel("% Green", gg); + LabeledInputPanel gpercBlue = new LabeledInputPanel("% Blue", gb); + gSkew.add(gpercRed); + gSkew.add(gpercGreen); + gSkew.add(gpercBlue); + + LabeledInputPanel bpercRed = new LabeledInputPanel("% Red", br); + LabeledInputPanel bpercGreen = new LabeledInputPanel("% Green", bg); + LabeledInputPanel bpercBlue = new LabeledInputPanel("% Blue", bb); + bSkew.add(bpercRed); + bSkew.add(bpercGreen); + bSkew.add(bpercBlue); + + rpercRed.addPropertyChangeListener("value", (ev) -> { + rr = (Double)rpercRed.field.getValue(); + }); + gpercRed.addPropertyChangeListener("value", (ev) -> { + gr = (Double)gpercRed.field.getValue(); + }); + bpercRed.addPropertyChangeListener("value", (ev) -> { + br = (Double)bpercRed.field.getValue(); + }); + + rpercGreen.addPropertyChangeListener("value", (ev) -> { + rg = (Double)rpercGreen.field.getValue(); + }); + gpercGreen.addPropertyChangeListener("value", (ev) -> { + gg = (Double)gpercGreen.field.getValue(); + }); + bpercGreen.addPropertyChangeListener("value", (ev) -> { + bg = (Double)bpercGreen.field.getValue(); + }); + + rpercBlue.addPropertyChangeListener("value", (ev) -> { + rb = (Double)rpercBlue.field.getValue(); + }); + gpercBlue.addPropertyChangeListener("value", (ev) -> { + gb = (Double)gpercBlue.field.getValue(); + }); + bpercBlue.addPropertyChangeListener("value", (ev) -> { + bb = (Double)bpercBlue.field.getValue(); + }); + + holder.add(rSkew); + holder.add(gSkew); + holder.add(bSkew); + + return holder; + } + +} diff --git a/src/bjc/imgchain/pipeline/stages/GaussStage.java b/src/bjc/imgchain/pipeline/stages/GaussStage.java new file mode 100644 index 0000000..2b04ddf --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/GaussStage.java @@ -0,0 +1,110 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.awt.image.ConvolveOp; +import java.awt.image.Kernel; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +import bjc.imgchain.pipeline.StageType; +import bjc.imgchain.utils.LabeledInputPanel; + +public class GaussStage extends AbstractPipelineStage { + private int m; + private double sig, k; + + public GaussStage() { + super(StageType.IMGTRANS); + } + + @Override + public Image process(Image inp) { + BufferedImage buf = (BufferedImage) inp; + + ConvolveOp cop = new ConvolveOp(genKern()); + + BufferedImage ret = cop.createCompatibleDestImage(buf, null); + + cop.filter(buf, ret); + + return ret; + } + + private Kernel genKern() { + float[][] w = new float[m][m]; + + float sum = 0.0f; + + for (int i = 0; i < m - 1; i++) { + for (int j = 0; j < m - 1; j++) { + double s = i - (m / 2.0); + double t = j - (m / 2.0); + + double r2 = (s * s) + (t * t); + + double frac = r2 / (2.0 * sig * sig); + + double val = k * Math.exp(-frac); + + int idx1 = m - i; + int idx2 = m - j; + + float fval = (float) val; + + w[idx1 - 1][idx2 - 1] = fval; + sum += fval; + } + } + + float invsum = 1 / sum; + + float[] dat = new float[m * m]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < m; j++) { + dat[(i * m) + j] = w[i][j] * sum; + } + } + + return new Kernel(m, m, dat); + } + + @Override + public String name() { + return "Gaussian"; + } + + @Override + public String description() { + return "Perform a gaussian blur"; + } + + @Override + public JComponent getEditor() { + JPanel holder = new JPanel(); + holder.setLayout(new GridLayout(3, 1)); + + LabeledInputPanel mField = new LabeledInputPanel("Size of kernel", 3); + mField.addPropertyChangeListener("value", (ev) -> { + m = (Integer) mField.field.getValue(); + }); + LabeledInputPanel sigField = new LabeledInputPanel("Value for sigma", 3.0); + sigField.addPropertyChangeListener("value", (ev) -> { + sig = (Double) sigField.field.getValue(); + }); + LabeledInputPanel kField = new LabeledInputPanel("Value for k", 1.0); + kField.addPropertyChangeListener("value", (ev) -> { + k = (Double) kField.field.getValue(); + }); + + holder.add(mField); + holder.add(sigField); + holder.add(kField); + + return holder; + } + +} diff --git a/src/bjc/imgchain/pipeline/stages/GreyscaleStage.java b/src/bjc/imgchain/pipeline/stages/GreyscaleStage.java new file mode 100644 index 0000000..71bb743 --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/GreyscaleStage.java @@ -0,0 +1,45 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.Image; +import java.awt.image.BufferedImage; + +import javax.swing.JComponent; +import javax.swing.JLabel; + +import bjc.imgchain.pipeline.StageType; +import bjc.imgchain.utils.Utils; + +public class GreyscaleStage extends AbstractPixelStage { + + public GreyscaleStage() { + super(StageType.IMGTRANS); + } + + public int[] processPixel(int[] pix) { + int[] ret = new int[4]; + + int avg = (pix[1] + pix[2] + pix[3]) / 3; + + ret[0] = pix[0]; + ret[1] = avg; + ret[2] = avg; + ret[3] = avg; + + return ret; + } + @Override + public String name() { + return "Greyscale"; + } + + @Override + public String description() { + return "Convert an image into greyscale color"; + } + + @Override + public JComponent getEditor() { + return new JLabel("No configuration possible"); + } + +} diff --git a/src/bjc/imgchain/pipeline/stages/IDStage.java b/src/bjc/imgchain/pipeline/stages/IDStage.java new file mode 100644 index 0000000..93aaebd --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/IDStage.java @@ -0,0 +1,44 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.Image; + +import javax.swing.JComponent; +import javax.swing.JLabel; + +import bjc.imgchain.pipeline.StageType; + +/** + * A pipeline stage that does nothing. + * + * @author acm + * + */ +public class IDStage extends AbstractPipelineStage { + + /** + * Create a new identity stage. + */ + public IDStage() { + super(StageType.IMGTRANS); + } + + @Override + public String name() { + return "Identity"; + } + + @Override + public Image process(Image inp) { + return inp; + } + + @Override + public String description() { + return "Passes an image straight through."; + } + + @Override + public JComponent getEditor() { + return new JLabel("Nothing to edit"); + } +} diff --git a/src/bjc/imgchain/pipeline/stages/NegativeStage.java b/src/bjc/imgchain/pipeline/stages/NegativeStage.java new file mode 100644 index 0000000..028139c --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/NegativeStage.java @@ -0,0 +1,33 @@ +package bjc.imgchain.pipeline.stages; + +import javax.swing.JComponent; +import javax.swing.JLabel; + +import bjc.imgchain.pipeline.StageType; + +public class NegativeStage extends AbstractPixelStage { + public NegativeStage() { + super(StageType.IMGTRANS); + } + + @Override + public String name() { + return "Negative"; + } + + @Override + public String description() { + return "Invert image colors"; + } + + @Override + public JComponent getEditor() { + return new JLabel("No configuration available"); + } + + @Override + public int[] processPixel(int[] pix) { + return new int[] { pix[0], 255 - pix[1], 255 - pix[2], 255 - pix[3] }; + } + +} diff --git a/src/bjc/imgchain/pipeline/stages/StagePicker.java b/src/bjc/imgchain/pipeline/stages/StagePicker.java new file mode 100644 index 0000000..8b30ee4 --- /dev/null +++ b/src/bjc/imgchain/pipeline/stages/StagePicker.java @@ -0,0 +1,68 @@ +package bjc.imgchain.pipeline.stages; + +import java.awt.BorderLayout; +import java.awt.GridLayout; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import bjc.imgchain.utils.ImmutableTableModel; + +public class StagePicker extends JDialog { + private static final long serialVersionUID = 1L; + + public String stageName; + + public StagePicker() { + super(); + + setModalityType(ModalityType.APPLICATION_MODAL); + setTitle("Add a Stage"); + + setupGUI(); + } + + private void setupGUI() { + setLayout(new BorderLayout()); + + String[] columnNames = new String[] { "Stage Name", "Stage Description" }; + + /* + * :AddStage + */ + String[][] data = new String[][] { { "Identity", "Pass through image unchanged" }, + { "Greyscale", "Convert a color image into greyscale" }, + { "Color Skew", "Adjust color balance" }, { "Sepia", "Make your image sepia toned" }, + { "Negative", "Invert your images colors" }, { "Gaussian Blur", "Blur images" } }; + + JTable stageTable = new JTable(new ImmutableTableModel(data, columnNames)); + + JScrollPane tableScroll = new JScrollPane(stageTable); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(2, 1)); + + JButton addStage = new JButton("Add Stage"); + addStage.addActionListener((ev) -> { + stageName = (String) stageTable.getModel().getValueAt(stageTable.getSelectedRow(), 0); + + setVisible(false); + dispose(); + }); + + JButton cancel = new JButton("Cancel"); + cancel.addActionListener((ev) -> { + setVisible(false); + dispose(); + }); + + buttonPanel.add(addStage); + buttonPanel.add(cancel); + + add(tableScroll, BorderLayout.CENTER); + add(buttonPanel, BorderLayout.PAGE_END); + } +} diff --git a/src/bjc/imgchain/utils/ImmutableTableModel.java b/src/bjc/imgchain/utils/ImmutableTableModel.java new file mode 100644 index 0000000..515f0a0 --- /dev/null +++ b/src/bjc/imgchain/utils/ImmutableTableModel.java @@ -0,0 +1,17 @@ +package bjc.imgchain.utils; + +import javax.swing.table.DefaultTableModel; + +public class ImmutableTableModel extends DefaultTableModel { + private static final long serialVersionUID = 6459890821518409439L; + + public ImmutableTableModel(Object[][] data, Object[] columnNames) { + super(data, columnNames); + } + + + @Override + public boolean isCellEditable(int row, int column) { + return false; + } +} \ No newline at end of file diff --git a/src/bjc/imgchain/utils/LabeledInputPanel.java b/src/bjc/imgchain/utils/LabeledInputPanel.java new file mode 100644 index 0000000..7eed8b0 --- /dev/null +++ b/src/bjc/imgchain/utils/LabeledInputPanel.java @@ -0,0 +1,42 @@ +package bjc.imgchain.utils; + +import java.awt.BorderLayout; + +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JPanel; + +/** + * A component for getting formatted input with a label. + * + * @author bjculkin + * + */ +public class LabeledInputPanel extends JPanel { + private static final long serialVersionUID = 1031310890698539040L; + + /** + * The field input is stored in. + */ + public final JFormattedTextField field; + + /** + * Create a new labeled input component. + * + * @param label + * The label for the component. + * @param val + * The initial value for the field. + */ + public LabeledInputPanel(String label, Object val) { + super(); + setLayout(new BorderLayout()); + + JLabel xLabel = new JLabel(label); + + field = new JFormattedTextField(val); + + add(xLabel, BorderLayout.LINE_START); + add(field, BorderLayout.CENTER); + } +} \ No newline at end of file diff --git a/src/bjc/imgchain/utils/Utils.java b/src/bjc/imgchain/utils/Utils.java new file mode 100644 index 0000000..38e2ec2 --- /dev/null +++ b/src/bjc/imgchain/utils/Utils.java @@ -0,0 +1,54 @@ +package bjc.imgchain.utils; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; + +public class Utils { + public static BufferedImage toBuffered(Image img) { + if (img instanceof BufferedImage) { + return (BufferedImage) img; + } + + /* + * Create a buffered image with transparency + */ + BufferedImage bufimg = new BufferedImage(img.getWidth(null), img.getHeight(null), + BufferedImage.TYPE_INT_ARGB); + + /* + * Draw the image on to the buffered image + */ + Graphics2D graph = bufimg.createGraphics(); + graph.drawImage(img, 0, 0, null); + graph.dispose(); + + /* + * Return the buffered image + */ + return bufimg; + } + + public static int[] toARGBQuad(int rbg) { + return new int[] { (rbg >> 24) & 0xff, (rbg >> 16) & 0xff, (rbg >> 8) & 0xff, rbg & 0xff }; + } + + public static int fromARGBQuad(int[] argb) { + return (argb[0] << 24) | (argb[1] << 16) | (argb[2] << 8) | argb[3]; + } + + public static int[][] padarray(int[][] arr, int padWith, int numOfPads) { + int[][] temp = new int[arr.length + numOfPads * 2][arr[0].length + numOfPads * 2]; + for (int i = 0; i < temp.length; i++) { + for (int j = 0; j < temp[i].length; j++) { + temp[i][j] = padWith; + } + } + for (int i = 0; i < arr.length; i++) { + for (int j = 0; j < arr[i].length; j++) { + temp[i + numOfPads][j + numOfPads] = arr[i][j]; + } + } + return temp; + } +} -- cgit v1.2.3