package com.nativelibs4java.opencl.demos.interactiveimage; import com.nativelibs4java.opencl.demos.SetupUtils; import java.awt.dnd.DropTarget; import java.net.MalformedURLException; import java.awt.datatransfer.DataFlavor; import java.awt.Image; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DnDConstants; import java.awt.datatransfer.Transferable; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetListener; import java.awt.Dimension; import java.awt.Point; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.nativelibs4java.opencl.*; import com.ochafik.io.ReadText; import com.ochafik.io.WriteText; import javax.swing.*; import java.awt.event.*; import javax.imageio.*; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.image.*; import java.io.*; import java.awt.FileDialog; import java.util.*; import com.ochafik.swing.UndoRedoUtils; import com.ochafik.swing.syntaxcoloring.TokenMarker; import com.ochafik.swing.syntaxcoloring.CCTokenMarker; import com.ochafik.swing.syntaxcoloring.JEditTextArea; import com.ochafik.util.SystemUtils; import org.bridj.Platform; import static com.nativelibs4java.opencl.demos.interactiveimage.Utils.*; /** mvn compile exec:java -Dexec.mainClass=com.nativelibs4java.opencl.demos.interactiveimage.InteractiveImageDemo */ public class InteractiveImageDemo extends JPanel { JSplitPane imgSrcSplitPane, imgsSplitPane; JLabel origImgLab, resultImgLab, instructionsLabel, timeLabel, progressLabel; JScrollPane origImgScroll, resultImgScroll; JEditTextArea sourceTextArea; JComboBox devicesCombo, examplesCombo; //JTextArea sourceTextArea; JButton runButton; BufferedImage image, result; JProgressBar progressBar; JComponent[] toDisable; File lastOpenedFile; static final String RUN_ACTION = "run", SAVE_ACTION = "save"; File persistentFile = new File(new File(new File(System.getProperty("user.home"), ".javacl"), getClass().getSimpleName()), "Test.cl"); boolean load() { if (!persistentFile.exists()) return false; sourceTextArea.setText(ReadText.readText(persistentFile)); return true; } void save() { try { WriteText.writeText(sourceTextArea.getText(), persistentFile); } catch (Exception ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(this, traceToHTML(ex), "Failed to save file", JOptionPane.ERROR_MESSAGE); } } class SaveAction extends AbstractAction { public void actionPerformed(ActionEvent e) { save(); } } void run() { save(); try { final BufferedImage bufferedImage = getImage(); if (bufferedImage == null) return; // Could just be this : final CLContext context = JavaCL.createBestContext(); final CLContext context = getContext(); if (context == null) return; final Point initialViewPosition = origImgScroll.getViewport().getViewPosition(); for (JComponent c : toDisable) c.setEnabled(false); resultImgLab.setText(null); resultIcon(null); resultImgLab.setToolTipText(null); result = null; timeLabel.setVisible(false); progressBar.setIndeterminate(true); progressBar.setVisible(true); setProgress("Initializing..."); final long[] elapsedTimeNanos = new long[] { -1L }; new Thread() { public void run() { try { setProgress("Creating OpenCL queue..."); CLQueue queue = context.createDefaultQueue(); setProgress("Compiling program..."); CLProgram program = context.createProgram(sourceTextArea.getText()); CLKernel[] kernels = program.createKernels(); if (kernels.length == 0) throw new RuntimeException("No kernels found in the source code ! (please mark a function with __kernel)"); setProgress("Creating OpenCL images..."); int width = bufferedImage.getWidth(), height = bufferedImage.getHeight(); CLImage2D imageIn = context.createImage2D(CLMem.Usage.InputOutput, bufferedImage, false); CLImage2D imageOut = context.createImage2D(CLMem.Usage.InputOutput, imageIn.getFormat(), width, height); long startTimeNanos = System.nanoTime(); CLEvent lastEvent = null; CLImage2D finalImageOut = null; for (CLKernel kernel : kernels) { setProgress("Running kernel '" + kernel.getFunctionName() + "'..."); try { kernel.setArgs(imageIn, imageOut); finalImageOut = imageOut; imageOut = imageIn; imageIn = finalImageOut; lastEvent = kernel.enqueueNDRange(queue, new int[] { width, height }, lastEvent); } catch (CLException ex) { throw new RuntimeException("Error occurred while running kernel '" + kernel.getFunctionName() + "': " + ex, ex); } } lastEvent.waitFor(); elapsedTimeNanos[0] = System.nanoTime() - startTimeNanos; setProgress("Reading the image output..."); result = finalImageOut.read(queue); imageIn.release(); imageOut.release(); for (CLKernel kernel : kernels) kernel.release(); program.release(); queue.release(); SwingUtilities.invokeLater(new Runnable() { public void run() { resultIcon(result == null ? null : new ImageIcon(result)); resultImgLab.setToolTipText(result == null ? null : "Click to save this image"); SwingUtilities.invokeLater(new Runnable() { public void run() { origImgScroll.getViewport().setViewPosition(initialViewPosition); }}); }}); } catch (Exception ex) { ex.printStackTrace(); resultError(ex); } finally { SwingUtilities.invokeLater(new Runnable() { public void run() { setProgress(null); for (JComponent c : toDisable) c.setEnabled(true); progressBar.setIndeterminate(false); progressBar.setVisible(false); if (elapsedTimeNanos[0] >= 0) { timeLabel.setText("Completed in " + (elapsedTimeNanos[0] / 1000000.0) + " msecs"); timeLabel.setVisible(true); } }}); } }}.start(); } catch (Exception ex) { ex.printStackTrace(); resultError(ex); } } class RunAction extends AbstractAction { public void actionPerformed(ActionEvent e) { run(); } } String runKeyStroke = "F5"; int spacing = 10; class Example { public Example(String caption, String fileName) { this.fileName = fileName; this.caption = caption; } public final String fileName, caption; @Override public String toString() { return caption; } } public InteractiveImageDemo() { super(new BorderLayout()); devicesCombo = new JComboBox(); List<CLDevice> devices = new ArrayList<CLDevice>(); try { for (CLPlatform platform : JavaCL.listPlatforms()) { for (CLDevice device : platform.listAllDevices(true)) { devicesCombo.addItem(device); devices.add(device); } } if (!devices.isEmpty()) devicesCombo.setSelectedItem(CLPlatform.getBestDevice(Arrays.asList(CLPlatform.DeviceFeature.MaxComputeUnits), devices)); } catch (Exception ex) { ex.printStackTrace(); devicesCombo.setToolTipText(traceToHTML(ex)); } if (devices.isEmpty()) { devicesCombo.addItem("No OpenCL Device detected"); } examplesCombo = new JComboBox(); examplesCombo.addItem("Examples..."); { final String signature = "__kernel void transform(__global read_only image2d inputImage, __global write_only image2d outputImage)"; examplesCombo.setToolTipText("Kernel samples in the form of :\n'" + signature + "'"); for (Example example : new Example[] { //"Blur", new Example("Convolution", "Convolution"), new Example("Sobel Operator", "SobelFilter"), new Example("Desaturate Colors", "DesaturateColors"), new Example("Richardson-Lucy Deconvolution", "RichardsonLucyDeconvolution"), new Example("Luminance Threshold", "LuminanceThreshold"), new Example("Naive Denoising", "NaiveDenoising"), new Example("Identity", "Identity"), new Example("Image Info", "QueryFormat") }) { examplesCombo.addItem(example); } examplesCombo.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { String t = sourceTextArea.getText(); if (t.trim().length() > 0) t = t + "\n"; sourceTextArea.setText(t + "__kernel " + signature + " {\n\tint x = get_global_id(0), y = get_global_id(1);\n\t// write here\n}"); }}); examplesCombo.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { Object selection = examplesCombo.getSelectedItem(); if (selection instanceof Example) { loadExample(((Example)selection).fileName); examplesCombo.setSelectedIndex(0); } }}); } JPanel srcPanel = new JPanel(new BorderLayout()); sourceTextArea = textArea(new CCTokenMarker()); srcPanel.add("Center", withTitle("Image transformation kernel source code", sourceTextArea)); runButton = new JButton("Run (" + runKeyStroke + ")"); { Box toolbar = Box.createHorizontalBox(); for (JComponent c : new JComponent[] { examplesCombo, runButton, devicesCombo }) c.setMaximumSize(c.getPreferredSize()); runButton.putClientProperty("JButton.buttonType", "bevel"); examplesCombo.putClientProperty("JComboBox.isPopDown", Boolean.TRUE); devicesCombo.putClientProperty("JComboBox.isPopDown", Boolean.TRUE); toolbar.add(examplesCombo); toolbar.add(runButton); toolbar.add(devicesCombo); toolbar.add(createLinkLabel("Khronos OpenCL Documentation", "http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/")); toolbar.add(Box.createHorizontalStrut(spacing)); toolbar.add(createLinkLabel("JavaCL FAQ", "http://code.google.com/p/javacl/wiki/FAQ")); toolbar.add(Box.createHorizontalStrut(spacing)); toolbar.add(Box.createHorizontalGlue()); toolbar.add(progressLabel = new JLabel()); toolbar.add(Box.createHorizontalStrut(spacing)); toolbar.add(progressBar = new JProgressBar()); progressBar.putClientProperty("JProgressBar.style", "circular"); toolbar.add(timeLabel = new JLabel()); progressBar.setMaximumSize(progressBar.getPreferredSize()); progressLabel.setVisible(false); progressBar.setVisible(false); timeLabel.setVisible(false); srcPanel.add("South", toolbar); } origImgScroll = new JScrollPane(origImgLab = new JLabel()); resultImgScroll = new JScrollPane(resultImgLab = new JLabel()); for (JScrollPane sp : Arrays.asList(origImgScroll, resultImgScroll)) { sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); } resultVertScrollModel = resultImgScroll.getVerticalScrollBar().getModel(); resultHorzScrollModel = resultImgScroll.getHorizontalScrollBar().getModel(); origImgLab.setDropTarget(new DropTarget(origImgLab, DnDConstants.ACTION_COPY, imgDropTargetListener)); add("Center", imgSrcSplitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT, imgsSplitPane = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, withTitle("Input", origImgScroll), withTitle("Output", resultImgScroll) ), srcPanel )); imgSrcSplitPane.setResizeWeight(0.5); imgsSplitPane.setResizeWeight(0.5); origImgLab.setToolTipText("Click to load a different image"); origImgLab.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { chooseImage(); }}); resultImgLab.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { saveResult(); }}); runButton.addActionListener(new RunAction()); toDisable = new JComponent[] { examplesCombo, runButton, devicesCombo, sourceTextArea, //origImgLab, resultImgLab }; UndoRedoUtils.registerNewUndoManager(sourceTextArea, sourceTextArea.getDocument()); for (JComponent focusable : Arrays.asList(sourceTextArea, examplesCombo, devicesCombo, runButton)) { InputMap im = focusable.getInputMap(); ActionMap am = focusable.getActionMap(); im.put(KeyStroke.getKeyStroke(runKeyStroke), RUN_ACTION); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, isMac() ? KeyEvent.META_MASK : KeyEvent.CTRL_MASK), SAVE_ACTION); am.put(RUN_ACTION, new RunAction()); am.put(SAVE_ACTION, new SaveAction()); } } protected DropTargetListener imgDropTargetListener = new DropTargetListener() { public void dragEnter(DropTargetDragEvent dtde) { try { if ( dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor) || dtde.isDataFlavorSupported(DataFlavor.stringFlavor) || dtde.isDataFlavorSupported(DataFlavor.imageFlavor)) dtde.acceptDrag(DnDConstants.ACTION_COPY); else dtde.rejectDrag(); } catch (Exception ex) { ex.printStackTrace(); } dtde.acceptDrag(DnDConstants.ACTION_COPY); } public void dragExit(DropTargetEvent dte) {} public void dragOver(DropTargetDragEvent dtde) {} public void dropActionChanged(DropTargetDragEvent dtde) {} public void drop(DropTargetDropEvent dtde) { try { if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { dtde.acceptDrop(DnDConstants.ACTION_COPY); java.util.List<File> files = (java.util.List<File>)dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); if (files != null && !files.isEmpty()) { readImage(files.get(0).toURI().toURL()); } dtde.dropComplete(true); } else if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { dtde.acceptDrop(DnDConstants.ACTION_COPY); readImage(new URL((String)dtde.getTransferable().getTransferData(DataFlavor.stringFlavor))); dtde.dropComplete(true); return ; } catch (MalformedURLException ex) {} } else if (dtde.isDataFlavorSupported(DataFlavor.imageFlavor)) { dtde.acceptDrop(DnDConstants.ACTION_COPY); Image image = (Image)dtde.getTransferable().getTransferData(DataFlavor.imageFlavor); if (image instanceof BufferedImage) setImage((BufferedImage)image); dtde.dropComplete(true); return; } dtde.rejectDrop(); } catch (Exception ex) { origImgLab.setToolTipText(traceToHTML(ex)); } } }; BufferedImage getImage() { if (image == null) chooseImage(); return image; } void readImage(URL url) { try { setImage(null); InputStream in = url.openStream(); if (in == null) return; lastOpenedFile = new File(url.getFile()); setImage(ImageIO.read(in)); in.close(); } catch (Exception ex) { ex.printStackTrace(); origImgLab.setText(traceToHTML(ex)); } } void setImage(BufferedImage image) { this.image = image; origImgLab.setText(null); origIcon(image == null ? null : new ImageIcon(image)); } void readImageResource(String name) { readImage(Platform.getClassLoader(getClass()).getResource("images/" + name)); } void chooseImage() { try { File f = chooseFile(lastOpenedFile, true); if (f == null) return; readImage(f.toURI().toURL()); } catch (Exception ex) { ex.printStackTrace(); origImgLab.setText(traceToHTML(ex)); } } String getOutputFormat(File file) { if (file != null) { String s = file.getName().toLowerCase(); if (s.matches(".*?\\.jpe?g")) return "jpeg"; for (String ex : new String[] { "png", "gif", "tiff", "pnm", "pbm" }) if (s.matches(".*?\\." + ex)) return ex; } return "png"; } static Pattern fileExtRx = Pattern.compile("(.*?)(\\.[^.]+)?"); void saveResult() { if (result == null) return; try { File f = null; if (lastOpenedFile != null) { Matcher matcher = fileExtRx.matcher(lastOpenedFile.getName()); if (matcher.matches()) { String body = matcher.group(1); String ext = matcher.group(2); f = new File(lastOpenedFile.getParentFile(), body + ".transformed" + (ext == null ? ".png" : ext)); } } f = chooseFile(f, false); if (f == null) return; ImageIO.write(result, getOutputFormat(f), f); } catch (Exception ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(this, traceToHTML(ex), "Failed to write image", JOptionPane.ERROR_MESSAGE); } } CLContext getContext() { Object selection = devicesCombo.getSelectedItem(); if (!(selection instanceof CLDevice)) return null; CLDevice device = (CLDevice)selection; CLContext context = JavaCL.createContext(null, device); return context; } void setProgress(final String caption) { System.out.println(caption); SwingUtilities.invokeLater(new Runnable() { public void run() { //progressLabel.setVisible(caption != null); //progressLabel.setText(caption); if (!isMac()) { progressBar.setStringPainted(caption != null); progressBar.setString(caption); } progressBar.setToolTipText(caption); }}); } BoundedRangeModel resultVertScrollModel, resultHorzScrollModel; void resultIcon(Icon icon) { if (icon == null) { resultImgScroll.getVerticalScrollBar().setModel(resultVertScrollModel); resultImgScroll.getHorizontalScrollBar().setModel(resultHorzScrollModel); } else { resultImgScroll.getVerticalScrollBar().setModel(origImgScroll.getVerticalScrollBar().getModel()); resultImgScroll.getHorizontalScrollBar().setModel(origImgScroll.getHorizontalScrollBar().getModel()); } resultImgLab.setIcon(icon); } void origIcon(Icon icon) { origImgLab.setIcon(icon); SwingUtilities.invokeLater(new Runnable() { public void run() { JScrollBar bar; BoundedRangeModel model; model = (bar = origImgScroll.getVerticalScrollBar()).getModel(); model.setValue((model.getMinimum() + model.getMaximum()) / 2); model = (bar = origImgScroll.getHorizontalScrollBar()).getModel(); model.setValue((model.getMinimum() + model.getMaximum()) / 2); }}); } void resultError(Exception ex) { String html = traceToHTML(ex); resultIcon(null); resultImgLab.setText(html); resultImgLab.setToolTipText(html); } void loadExample(String fileName) { try { String s = readTextResource("examples/" + fileName + ".cl"); sourceTextArea.setText(s); sourceTextArea.setCaretPosition(0); } catch (Exception ex) { ex.printStackTrace(); sourceTextArea.setText("Failed to load example '" + fileName + "' :\n" + traceToString(ex)); } } public static void main(String[] args) { SetupUtils.failWithDownloadProposalsIfOpenCLNotAvailable(); JFrame f = new JFrame("JavaCL's Interactive Image Transform Demo"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); InteractiveImageDemo demo = new InteractiveImageDemo(); f.getContentPane().add("Center", demo); f.setSize(1200, 800); f.setVisible(true); demo.getContext(); demo.readImageResource("lena.jpg"); if (!demo.load()) demo.loadExample("Convolution"); } }