/** * 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 }