/* * @(#)PBMViewer.java * * Copyright (c) 2012 Werner Randelshofer, Goldau, Switzerland. * All rights reserved. * * You may not use, copy or modify this file, except in compliance with the * license agreement you entered into with Werner Randelshofer. * For details see accompanying license terms. */ package org.monte.pbmdemo; import java.awt.Image; import java.awt.Toolkit; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.monte.media.gui.Worker; import org.monte.media.ilbm.ColorCyclingMemoryImageSource; import org.monte.media.ilbm.ILBMDecoder; import org.monte.media.pbm.PBMDecoder; import org.monte.media.io.ByteArrayImageInputStream; /** * PBM and ILBM Image Viewer. * * @author Werner Randelshofer * @version $Id: PBMViewer.java 277 2012-10-22 20:03:33Z werner $ */ public class PBMViewer extends javax.swing.JPanel { private class Handler implements DropTargetListener { /** * Called when a drag operation has encountered the * <code>DropTarget</code>. <P> * * @param dtde the <code>DropTargetDragEvent</code> */ @Override public void dragEnter(DropTargetDragEvent event) { if (event.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { event.acceptDrag(DnDConstants.ACTION_COPY); } else { event.rejectDrag(); } } /** * The drag operation has departed the * <code>DropTarget</code> without dropping. <P> * * @param dte the <code>DropTargetEvent</code> */ @Override public void dragExit(DropTargetEvent event) { // Nothing to do } /** * Called when a drag operation is ongoing on the * <code>DropTarget</code>. <P> * * @param dtde the <code>DropTargetDragEvent</code> */ @Override public void dragOver(DropTargetDragEvent event) { if (event.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { event.acceptDrag(DnDConstants.ACTION_COPY); } else { event.rejectDrag(); } } /** * The drag operation has terminated with a drop on this * <code>DropTarget</code>. This method is responsible for undertaking * the transfer of the data associated with the gesture. The * <code>DropTargetDropEvent</code> provides a means to obtain a * <code>Transferable</code> object that represents the data object(s) * to be transfered.<P> From this method, the * <code>DropTargetListener</code> shall accept or reject the drop via * the acceptDrop(int dropAction) or rejectDrop() methods of the * <code>DropTargetDropEvent</code> parameter. <P> Subsequent to * acceptDrop(), but not before, * <code>DropTargetDropEvent</code>'s getTransferable() method may be * invoked, and data transfer may be performed via the returned * <code>Transferable</code>'s getTransferData() method. <P> At the * completion of a drop, an implementation of this method is required to * signal the success/failure of the drop by passing an appropriate * <code>boolean</code> to the * <code>DropTargetDropEvent</code>'s dropComplete(boolean success) * method. <P> Note: The actual processing of the data transfer is not * required to finish before this method returns. It may be deferred * until later. <P> * * @param dtde the <code>DropTargetDropEvent</code> */ @Override @SuppressWarnings("unchecked") public void drop(DropTargetDropEvent event) { if (event.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { event.acceptDrop(DnDConstants.ACTION_COPY); try { List<File> files = (List<File>) event.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); showAmigaImages(files); } catch (IOException e) { JOptionPane.showConfirmDialog( PBMViewer.this, "Could not access the dropped data.", "PBMViewer: Drop Failed", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE); } catch (UnsupportedFlavorException e) { JOptionPane.showConfirmDialog( PBMViewer.this, "Unsupported data flavor.", "PBMViewer: Drop Failed", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE); } } else { event.rejectDrop(); } } /** * Called if the user has modified the current drop gesture. <P> * * @param dtde the <code>DropTargetDragEvent</code> */ @Override public void dropActionChanged(DropTargetDragEvent event) { // Nothing to do } } private Handler handler = new Handler(); /** * Creates new form PBMViewer */ public PBMViewer() { initComponents(); new DropTarget(this, handler); new DropTarget(label, handler); } protected BufferedImage getAmigaPicture(File f) throws IOException { return ImageIO.read(f); } protected BufferedImage getAmigaPictureViaByteArray(File f) throws IOException { return getAmigaPicture(getData(f)); } protected byte[] getData(File f) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); FileInputStream in = new FileInputStream(f); try { byte[] buf = new byte[512]; for (int len = in.read(buf); len != -1; len = in.read(buf)) { out.write(buf, 0, len); } } finally { in.close(); } return out.toByteArray(); } protected BufferedImage getAmigaPicture(byte[] data) throws IOException { return ImageIO.read(new ByteArrayImageInputStream(data)); } protected Image getAmigaPictureWithColorCycling(File f) throws IOException { FileInputStream in = new FileInputStream(f); try { ArrayList<ColorCyclingMemoryImageSource> imageList = new PBMDecoder(in).produce(); if (imageList.isEmpty()) { in.close(); in = new FileInputStream(f); imageList = new ILBMDecoder(in).produce(); } ColorCyclingMemoryImageSource ccmis = imageList.get(0); if (ccmis.isColorCyclingAvailable()) { ccmis.start(); } return Toolkit.getDefaultToolkit().createImage(ccmis); } finally { in.close(); } } public void showAmigaImages(final List<File> files) { label.setEnabled(false); if (label.getIcon() instanceof ImageIcon) { ImageIcon icon = (ImageIcon) label.getIcon(); label.setIcon(null); label.setDisabledIcon(null); icon.getImage().flush(); } new Worker<Image>() { @Override protected Image construct() throws Exception { for (File f : files) { //return getAmigaPicture(f); //return getAmigaPictureViaByteArray(f); return getAmigaPictureWithColorCycling(f); } return null; } @Override protected void done(Image value) { if (value == null) { failed(new IOException("Could not load image.")); return; } label.setText(null); ImageIcon icon = new ImageIcon(value); label.setIcon(icon); label.setDisabledIcon(icon); SwingUtilities.getWindowAncestor(PBMViewer.this).pack(); } @Override protected void failed(Throwable error) { error.printStackTrace(); label.setText("<html><b>Error</b><br>" + error.getMessage()); SwingUtilities.getWindowAncestor(PBMViewer.this).pack(); } @Override protected void finished() { label.setEnabled(true); } }.start(); } /** * 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() { label = new javax.swing.JLabel(); setLayout(new java.awt.BorderLayout()); label.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); label.setText("Drop PBM or ILBM file here."); add(label, java.awt.BorderLayout.CENTER); }// </editor-fold>//GEN-END:initComponents public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JFrame f = new JFrame("PBM Image Viewer"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.add(new PBMViewer()); f.setSize(200, 200); f.setVisible(true); } }); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel label; // End of variables declaration//GEN-END:variables }