/**
* This file is part of VisiCut.
* Copyright (C) 2011 - 2013 Thomas Oster <thomas.oster@rwth-aachen.de>
* RWTH Aachen University - 52062 Aachen, Germany
*
* VisiCut is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VisiCut is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with VisiCut. If not, see <http://www.gnu.org/licenses/>.
**/
package com.t_oster.visicut.vectorize;
import com.t_oster.visicut.managers.PreferencesManager;
import com.t_oster.visicut.misc.DialogHelper;
import com.t_oster.visicut.misc.FileUtils;
import com.t_oster.visicut.misc.Helper;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
*
* @author Thomas Oster <thomas.oster@rwth-aachen.de>
*/
public class VectorizeDialog extends javax.swing.JDialog
{
private DialogHelper dialog;
private File tmpResult = FileUtils.getNonexistingWritableFile("vectorize.svg");
private File tmpBitmap = FileUtils.getNonexistingWritableFile("vectorize1.bmp");
private File tmpBitmap2 = FileUtils.getNonexistingWritableFile("vectorize.bmp");
/**
* Creates new form VectorizeDialogForm
*/
public VectorizeDialog(java.awt.Frame parent, boolean modal)
{
super(parent, modal);
initComponents();
tmpResult.deleteOnExit();
tmpBitmap.deleteOnExit();
tmpBitmap2.deleteOnExit();
dialog = new DialogHelper(this, "Vectorize");
ChangeListener cl = new ChangeListener()
{
public void stateChanged(ChangeEvent ce)
{
triggerUpdate();
}
};
ActionListener al = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
triggerUpdate();
}
};
this.cbBlur.addChangeListener(cl);
this.cbHighpassFilter.addChangeListener(cl);
this.cbInvert.addChangeListener(cl);
this.jSlider1.addChangeListener(cl);
this.tfBlur.addActionListener(al);
this.tfFilterValue.addActionListener(al);
}
private File inputFile = null;
public static final String PROP_INPUTFILE = "inputFile";
public File getInputFile()
{
return inputFile;
}
public void setInputFile(File inputFile)
{
File oldInputFile = this.inputFile;
this.inputFile = inputFile;
try
{
BufferedImage img = ImageIO.read(inputFile);
//TODO make svg panel to have according with and height...
inputPanel.setImage(img);
triggerUpdate();
}
catch (IOException ex)
{
dialog.showErrorMessage(ex);
}
firePropertyChange(PROP_INPUTFILE, oldInputFile, inputFile);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents()
{
bindingGroup = new org.jdesktop.beansbinding.BindingGroup();
btOk = new javax.swing.JToggleButton();
btCancel = new javax.swing.JButton();
jSlider1 = new javax.swing.JSlider();
jLabel1 = new javax.swing.JLabel();
inputPanel = new com.t_oster.uicomponents.ImagePanel();
cbInvert = new javax.swing.JCheckBox();
cbHighpassFilter = new javax.swing.JCheckBox();
tfFilterValue = new javax.swing.JTextField();
cbBlur = new javax.swing.JCheckBox();
tfBlur = new javax.swing.JTextField();
svgPanel = new com.t_oster.uicomponents.SVGPanel();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("com/t_oster/visicut/vectorize/resources/VectorizeDialog"); // NOI18N
btOk.setText(bundle.getString("OK")); // NOI18N
btOk.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(java.awt.event.ActionEvent evt)
{
btOkActionPerformed(evt);
}
});
btCancel.setText(bundle.getString("CANCEL")); // NOI18N
btCancel.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(java.awt.event.ActionEvent evt)
{
btCancelActionPerformed(evt);
}
});
jSlider1.setMaximum(255);
jSlider1.setValue(125);
jLabel1.setText(bundle.getString("TRESHOLD")); // NOI18N
inputPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(bundle.getString("INPUT"))); // NOI18N
javax.swing.GroupLayout inputPanelLayout = new javax.swing.GroupLayout(inputPanel);
inputPanel.setLayout(inputPanelLayout);
inputPanelLayout.setHorizontalGroup(
inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
inputPanelLayout.setVerticalGroup(
inputPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 175, Short.MAX_VALUE)
);
cbInvert.setText(bundle.getString("INVERT")); // NOI18N
cbHighpassFilter.setText(bundle.getString("FILTER")); // NOI18N
tfFilterValue.setText("4");
org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, cbHighpassFilter, org.jdesktop.beansbinding.ELProperty.create("${selected}"), tfFilterValue, org.jdesktop.beansbinding.BeanProperty.create("enabled"), "filterenabled");
bindingGroup.addBinding(binding);
cbBlur.setText(bundle.getString("BLUR")); // NOI18N
tfBlur.setText("1");
binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ, cbBlur, org.jdesktop.beansbinding.ELProperty.create("${selected}"), tfBlur, org.jdesktop.beansbinding.BeanProperty.create("enabled"), "blurenabled");
bindingGroup.addBinding(binding);
svgPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(bundle.getString("PREVIEW"))); // NOI18N
javax.swing.GroupLayout svgPanelLayout = new javax.swing.GroupLayout(svgPanel);
svgPanel.setLayout(svgPanelLayout);
svgPanelLayout.setHorizontalGroup(
svgPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
svgPanelLayout.setVerticalGroup(
svgPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(inputPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(svgPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(btCancel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(btOk))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addGroup(layout.createSequentialGroup()
.addComponent(cbBlur)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tfBlur))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(cbInvert, javax.swing.GroupLayout.PREFERRED_SIZE, 107, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
.addComponent(cbHighpassFilter)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(tfFilterValue, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addGap(77, 271, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSlider1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(inputPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(svgPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btOk)
.addComponent(btCancel)))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel1))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(cbInvert)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cbHighpassFilter)
.addComponent(tfFilterValue, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cbBlur)
.addComponent(tfBlur, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 27, Short.MAX_VALUE)))
.addContainerGap())
);
bindingGroup.bind();
pack();
}// </editor-fold>//GEN-END:initComponents
private void btCancelActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_btCancelActionPerformed
{//GEN-HEADEREND:event_btCancelActionPerformed
for (File f : new File[]{tmpBitmap, tmpBitmap2, tmpResult})
{
if (f != null && f.exists())
{
f.delete();
}
}
tmpResult = null;
this.dispose();
}//GEN-LAST:event_btCancelActionPerformed
public File getResult()
{
return tmpResult;
}
private static String[] supportedFileTypes = new String[]{"jpg","png","bmp"};
public static boolean supportsFileType(File f)
{
for (String e : supportedFileTypes)
{
if (f.getName().toLowerCase().endsWith(e))
{
return true;
}
}
return false;
}
private void runProgram(String[] cmd) throws IOException, InterruptedException
{
Runtime run = Runtime.getRuntime() ;
Process pr = run.exec(cmd);
String error = "";
try
{
int r;
while ((r = pr.getErrorStream().read()) >= 0)
{
error += (char) r;
}
}
catch (IOException e)
{
}
pr.waitFor();
if (!"".equals(error))
{
throw new IOException("Errors:"+error);
}
}
private void mkbitmap(Double highpass, Double blur, double treshold, File input, File output) throws IOException, InterruptedException
{
List<String> mkbitmap = new ArrayList<String>(10);
File mkBitmapExe = new File(Helper.getVisiCutFolder(), "mkbitmap"+(Helper.isWindows() ? ".exe" : ""));
mkbitmap.add(mkBitmapExe.exists() ? mkBitmapExe.getAbsolutePath() : PreferencesManager.getInstance().getPreferences().getMkbitmapPath());
mkbitmap.add("-s");
mkbitmap.add("2");
if (highpass != null)
{
mkbitmap.add("-f");
mkbitmap.add(""+highpass);
}
else
{
mkbitmap.add("-n");
}
if (blur != null)
{
mkbitmap.add("-b");
mkbitmap.add(""+blur);
}
mkbitmap.add("-t");
mkbitmap.add(""+treshold);
mkbitmap.add("-o");
mkbitmap.add(output.getAbsolutePath());
mkbitmap.add(input.getAbsolutePath());
runProgram(mkbitmap.toArray(new String[0]));
}
private void potrace(boolean invert, File input, File output) throws IOException, InterruptedException
{
List<String> potrace = new ArrayList<String>(10);
File potraceExe = new File(Helper.getVisiCutFolder(), "potrace"+(Helper.isWindows() ? ".exe" : ""));
potrace.add(potraceExe.exists() ? potraceExe.getAbsolutePath() : PreferencesManager.getInstance().getPreferences().getPotracePath());
potrace.add("-b");
potrace.add("svg");
if (invert)
{
potrace.add("-i");
}
potrace.add("-o");
potrace.add(output.getAbsolutePath());
potrace.add(input.getAbsolutePath());
runProgram(potrace.toArray(new String[0]));
}
private void doVectorize() throws IOException, InterruptedException
{
BufferedImage in = ImageIO.read(inputFile);
//copy the input to an RGB image, otherwise creating BMP can fail
BufferedImage out = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g = out.createGraphics();
g.setBackground(Color.WHITE);
g.clearRect(0, 0, out.getWidth(), out.getHeight());
g.drawImage(in, 0, 0, null);
g.dispose();
ImageIO.write(out, "BMP", tmpBitmap);
double treshold = this.jSlider1.getValue() / (double) this.jSlider1.getMaximum();
Double highpass = cbHighpassFilter.isSelected() ? Double.parseDouble(tfFilterValue.getText()) : null;
Double blur = cbBlur.isSelected() ? Double.parseDouble(tfBlur.getText()) : null;
//convert bitmap to black white using filters etc
mkbitmap(highpass, blur, treshold, tmpBitmap, tmpBitmap2);
//convert black white bitmap to svg
potrace(cbInvert.isSelected(), tmpBitmap2, tmpResult);
}
private final Runnable updateThread = new Runnable(){
@Override
public void run()
{
try
{
svgPanel.setEnabled(false);
while (true)
{
synchronized (updateThread)
{
updateAgain = false;
}
doVectorize();
synchronized (updateThread)
{
if (!updateAgain)
{
break;
}
}
}
svgPanel.setSvgFile(tmpResult);
svgPanel.repaint();
svgPanel.setEnabled(true);
}
catch (Exception ex)
{
dialog.showErrorMessage(ex, java.util.ResourceBundle.getBundle("com/t_oster/visicut/vectorize/resources/VectorizeDialog").getString("ERRORMSG"));
}
synchronized(updateThread)
{
updating = false;
}
}
};
private boolean updating = false;
private boolean updateAgain = false;
private void triggerUpdate()
{
synchronized(updateThread)
{
if (!updating)
{
updating = true;
new Thread(updateThread).start();
}
else
{
updateAgain = true;
}
}
}
private void btOkActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_btOkActionPerformed
{//GEN-HEADEREND:event_btOkActionPerformed
try
{
this.doVectorize();
tmpBitmap.delete();
tmpBitmap2.delete();
this.dispose();
}
catch (Exception ex)
{
tmpResult = null;
dialog.showErrorMessage(ex, java.util.ResourceBundle.getBundle("com/t_oster/visicut/vectorize/resources/VectorizeDialog").getString("ERRORMSG"));
}
}//GEN-LAST:event_btOkActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btCancel;
private javax.swing.JToggleButton btOk;
private javax.swing.JCheckBox cbBlur;
private javax.swing.JCheckBox cbHighpassFilter;
private javax.swing.JCheckBox cbInvert;
private com.t_oster.uicomponents.ImagePanel inputPanel;
private javax.swing.JLabel jLabel1;
private javax.swing.JSlider jSlider1;
private com.t_oster.uicomponents.SVGPanel svgPanel;
private javax.swing.JTextField tfBlur;
private javax.swing.JTextField tfFilterValue;
private org.jdesktop.beansbinding.BindingGroup bindingGroup;
// End of variables declaration//GEN-END:variables
}