/*
* JImgView.java
*
* Created on 17. April 2006, 11:36
*
* Copyright (C) 17. April 2006 <Reiner>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package jexifviewer;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Rectangle2D;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.File;
import javax.imageio.ImageIO;
import java.util.ArrayList;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.FileImageOutputStream;
import shared.cvshelper.Cvs;
/**
*
* @author reiner
*/
@Cvs
(
header = "$Header: /home/reiner/cvs/Java/JExifViewer/src/jexifviewer/JImgView.java,v 1.8 2010/04/02 12:12:13 reiner Exp $"
)
public final class JImgView extends JComponent implements Printable
{
protected BufferedImage m_img;
private JIfdData m_ifd;
private int m_orientation = 0;
private float m_zoom = 0.0f;
private boolean m_bZoomDirty = false;
private JImageCache m_imgCache;
private Point m_moveStartPoint;
private boolean m_bMove = false;
private boolean m_bMoveOutSide = false;
private Point m_viewPos;
private boolean m_bPreloadDirty = false;
/** Creates a new instance of JImgView */
public JImgView(int cacheSize)
{
m_imgCache = new JImageCache(cacheSize, Main.m_settings.getCallGcAfterCacheOp());
// Keyboard action on the image viewer
// full / normal view
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "esc");
getActionMap().put("esc", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
Main.m_mainFrame.toggleView();
}
});
// Zoom in / out
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, InputEvent.CTRL_DOWN_MASK), "ctrl_+");
getActionMap().put("ctrl_+", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
setZoomUp();
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, InputEvent.CTRL_DOWN_MASK), "ctrl_-");
getActionMap().put("ctrl_-", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
setZoomDown();
}
});
// fit
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK), "ctrl_enter");
getActionMap().put("ctrl_enter", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
resetZoom();
}
});
// 100%
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_MULTIPLY, InputEvent.CTRL_DOWN_MASK), "ctrl_100%");
getActionMap().put("ctrl_100%", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
setZoom(1.0f);
}
});
// 50%
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, InputEvent.CTRL_DOWN_MASK), "ctrl_50%");
getActionMap().put("ctrl_50%", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
setZoom(.5f);
}
});
// Scrolling
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD2, 0), "ctrl_scrolldown");
getActionMap().put("ctrl_scrolldown", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
scrollImg(0, Main.m_settings.getImgScrollDelta());
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD8, 0), "ctrl_scrollup");
getActionMap().put("ctrl_scrollup", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
scrollImg(0, -Main.m_settings.getImgScrollDelta());
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD4, 0), "ctrl_scrollleft");
getActionMap().put("ctrl_scrollleft", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
scrollImg(-Main.m_settings.getImgScrollDelta(), 0);
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD6, 0), "ctrl_scrollright");
getActionMap().put("ctrl_scrollright", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
scrollImg(Main.m_settings.getImgScrollDelta(), 0);
}
});
// diagonal
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD7, 0), "ctrl_scrollupleft");
getActionMap().put("ctrl_scrollupleft", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
scrollImg(-Main.m_settings.getImgScrollDelta(), -Main.m_settings.getImgScrollDelta());
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD9, 0), "ctrl_scrollupright");
getActionMap().put("ctrl_scrollupright", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
scrollImg(Main.m_settings.getImgScrollDelta(), -Main.m_settings.getImgScrollDelta());
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD1, 0), "ctrl_scrolldownleft");
getActionMap().put("ctrl_scrolldownleft", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
scrollImg(-Main.m_settings.getImgScrollDelta(), Main.m_settings.getImgScrollDelta());
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD3, 0), "ctrl_scrolldownright");
getActionMap().put("ctrl_scrolldownright", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
scrollImg(Main.m_settings.getImgScrollDelta(), Main.m_settings.getImgScrollDelta());
}
});
// special
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD5, 0), "ctrl_scrollcenter");
getActionMap().put("ctrl_scrollcenter", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
{
Dimension size = getSize();
JViewport viewPort = (JViewport)getParent();
assert viewPort != null;
Dimension dimView = viewPort.getExtentSize();
setViewPosition(new Point((int)(size.width/2 - dimView.width/2), (int)(size.height/2 - dimView.height)));
}
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD0, 0), "ctrl_scrollhome");
getActionMap().put("ctrl_scrollhome", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
setViewPosition(new Point(0, 0));
}
});
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DECIMAL, 0), "ctrl_scrollend");
getActionMap().put("ctrl_scrollend", new AbstractAction()
{
public void actionPerformed(ActionEvent event)
{
if (m_zoom != 0.0f)
{
Dimension size = getSize();
setViewPosition(new Point((int)size.width, (int)size.height));
}
}
});
// Mouse
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent ev)
{
if (getZoom() != 0.0f)
{
m_moveStartPoint = ev.getPoint();
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
m_bMoveOutSide = false;
m_bMove = true;
}
}
public void mouseReleased(MouseEvent ev)
{
if (m_bMove)
{
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
m_bMove = false;
}
}
public void mouseExited(MouseEvent ev)
{
if (m_bMove)
{
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
m_bMoveOutSide = true;
}
}
public void mouseEntered(MouseEvent ev)
{
if (m_bMove)
{
if (m_bMoveOutSide)
{
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
m_bMoveOutSide = false;
}
}
}
});
addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseDragged(MouseEvent ev)
{
if (m_bMove && !m_bMoveOutSide)
{
JViewport viewPort = (JViewport)getParent();
assert viewPort != null;
Rectangle rect = viewPort.getViewRect();
if (rect.contains(ev.getPoint()))
{
Point pt = new Point(viewPort.getViewPosition());
pt.translate(m_moveStartPoint.x - ev.getPoint().x, m_moveStartPoint.y - ev.getPoint().y);
setViewPosition(pt);
}
}
}
});
}
private void scrollImg(int dx, int dy)
{
JViewport viewPort = (JViewport)getParent();
assert viewPort != null;
Point pt = new Point(viewPort.getViewPosition());
pt.translate(dx, dy);
setViewPosition(pt);
}
private boolean doSetViewPosition(Point _pt, boolean bForceSet)
{
boolean flag = true;
JViewport viewPort = (JViewport)getParent();
assert viewPort != null;
Point pt = new Point(_pt);
if (pt.x < 0) pt.x = 0;
if (pt.y < 0 ) pt.y = 0;
Dimension dimCtrl = getSize();
Dimension dimView = viewPort.getExtentSize();
if (pt.x > dimCtrl.width - dimView.width)
{
pt.x = dimCtrl.width - dimView.width;
flag = false;
}
if (pt.y > dimCtrl.height - dimView.height)
{
pt.y = dimCtrl.height - dimView.height;
flag = false;
}
if (flag || bForceSet)
viewPort.setViewPosition(pt);
return flag;
}
private void setViewPosition(Point pt)
{
doSetViewPosition(pt, true);
}
private boolean setViewPosition(Point pt, boolean bForce)
{
return doSetViewPosition(pt, bForce);
}
public void setZoomDown()
{
setZoom(getRealZoom() - Main.m_settings.getZoomDelta());
}
public void setZoomUp()
{
setZoom(getRealZoom() + Main.m_settings.getZoomDelta());
}
/**
* must be called after the zoom is done !
*/
private void adjustViewPosByZoom(float oldZoom)
{
float zoom = getRealZoom();
float zoomDelta = zoom - oldZoom;
final JViewport viewPort = (JViewport)getParent();
assert viewPort != null;
Dimension dimView = viewPort.getExtentSize();
m_viewPos = viewPort.getViewPosition();
m_viewPos.translate(Math.round((m_viewPos.x + (float)dimView.width/2.0f)*zoomDelta/oldZoom), Math.round((m_viewPos.y + (float)dimView.height/2.0f)*zoomDelta/oldZoom));
if (!setViewPosition(m_viewPos, false))
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
setViewPosition(m_viewPos);
}
});
}
}
public void resetZoom()
{
m_zoom = 0.0f;
m_bZoomDirty = true;
internalSetZoom();
repaint();
if (Main.m_settings.isDebug())
System.err.println("Reset zoom");
}
public void setZoom(float zoom)
{
if (zoom > 0.01 && zoom <= 8.0f)
{
final float oldZoom = getRealZoom();
m_zoom = zoom;
m_bZoomDirty = true;
internalSetZoom();
repaint();
if (Main.m_settings.isDebug())
System.err.println("Zoom: " + m_zoom);
adjustViewPosByZoom(oldZoom);
}
}
public float getZoom()
{
return m_zoom;
}
public float getRealZoom()
{
float zoom = m_zoom;
if (m_zoom == 0.0f)
{
if (m_img != null)
{
Dimension dim = getSize();
int w = m_img.getWidth();
int h = m_img.getHeight();
double dx, dy;
switch(getOrientation())
{
case 1: // 0�
dx = (double)dim.width / (double)w;
dy = (double)dim.height / (double)h;
zoom = (float)Math.min(dx, dy);
break;
case 2: // <->
dx = (double)dim.width / (double)w;
dy = (double)dim.height / (double)h;
zoom = (float)Math.min(dx, dy);
break;
case 3: // 180�
dx = (double)dim.width / (double)w;
dy = (double)dim.height / (double)h;
zoom = (float)Math.min(dx, dy);
break;
case 4: // 180� + <->
dx = (double)dim.width / (double)w;
dy = (double)dim.height / (double)h;
zoom = (float)Math.min(dx, dy);
break;
case 5: // -90� + <->
dx = (double)dim.width / (double)h;
dy = (double)dim.height / (double)w;
zoom = (float)Math.min(dx, dy);
break;
case 6: // -90�
dx = (double)dim.width / (double)h;
dy = (double)dim.height / (double)w;
zoom = (float)Math.min(dx, dy);
break;
case 7: // 90� + <->
dx = (double)dim.width / (double)h;
dy = (double)dim.height / (double)w;
zoom = (float)Math.min(dx, dy);
break;
case 8: // 90�
dx = (double)dim.width / (double)h;
dy = (double)dim.height / (double)w;
zoom = (float)Math.min(dx, dy);
break;
}
}
}
return zoom;
}
private void internalSetZoom()
{
if (m_img != null)
{
int w = m_img.getWidth();
int h = m_img.getHeight();
if (w >= 0 && h>= 0)
{
Dimension dim;
if (m_zoom == 0.0f)
setPreferredSize(dim = new Dimension(1, 1));
else setPreferredSize(dim = new Dimension((int)(w*m_zoom), (int)(h*m_zoom)));
m_bZoomDirty = false;
revalidate();
// getSize will not say the actual size if we call it now
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
Main.m_mainFrame.updateTitle(m_ifd);
}
});
}
}
}
static public int getNextOrientation(int orientation, boolean bUp)
{
switch(orientation)
{
case 1:
orientation = bUp ? 6 : 8;
break;
case 2:
orientation = bUp ? 7 : 5;
break;
case 3:
orientation = bUp ? 8 : 6;
break;
case 4:
orientation = bUp ? 5 : 7;
break;
case 5:
orientation = bUp ? 2 : 4;
break;
case 6:
orientation = bUp ? 3 : 1;
break;
case 7:
orientation = bUp ? 4 : 2;
break;
case 8:
orientation = bUp ? 1 : 3;
break;
}
return orientation;
}
static public int getNextFlipOrientation(int orientation)
{
switch(orientation)
{
case 1:
orientation = 2;
break;
case 2:
orientation = 1;
break;
case 3:
orientation = 4;
break;
case 4:
orientation = 3;
break;
case 5:
orientation = 6;
break;
case 6:
orientation = 5;
break;
case 7:
orientation = 8;
break;
case 8:
orientation = 7;
break;
}
return orientation;
}
public boolean isImage()
{
return m_img != null;
}
public JIfdData getIfdData()
{
return m_ifd;
}
protected int getOrientation(JIfdData ifd)
{
if (ifd != null)
{
int orientation = ifd.getOrientation();
if (orientation <= 0 || orientation > 8) orientation = 1;
return orientation;
}
return 0;
}
public int getOrientation()
{
return (m_orientation == 0 ? getOrientation(m_ifd) : m_orientation);
}
public int getCustomOrientation()
{
return m_orientation;
}
public void setOrientation(int orientation)
{
m_orientation = orientation;
repaint();
}
public void clearCache()
{
m_imgCache.clear();
}
public void removeFromCache(String file)
{
m_imgCache.remove(file);
}
public void preloadImg(final JIfdData ifd)
{
if (ifd != null)
{
if (m_imgCache.get(ifd.getFilePath()) == null)
{
Thread th = new Thread()
{
public void run()
{
try
{
final BufferedImage img = ImageIO.read(new File(ifd.getFilePath()));
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
m_imgCache.add(new JImageData(ifd.getFilePath(), img));
Main.m_mainFrame.doPreload();
}
});
}
catch (Exception ex)
{
}
}
};
if (Main.m_settings.isDebug())
System.err.println("Preload: " + ifd.getFilePath());
th.start();
}
}
}
public void setJpg(JIfdData ifd)
{
if (Main.m_settings.getSaveImage() != 0 && m_orientation != 0 && m_ifd != null && m_ifd.getOrientation() != m_orientation)
{
if (Main.m_settings.getSaveImage() == 1 || (Main.m_settings.getSaveImage() == 2 && JOptionPane.showConfirmDialog(Main.m_mainFrame, String.format(Main.getString("img_saveorientation_ask"), m_ifd.getFilePath()), Main.getMessageBoxCaption(), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION))
{
saveOrientation(false);
}
}
if (ifd == null)
{
m_ifd = null;
m_img = null;
m_orientation = 0;
m_bPreloadDirty = false;
}
else if (m_ifd == null || !ifd.getFilePath().equals(m_ifd.getFilePath()))
{
m_bZoomDirty = true;
m_ifd = ifd;
JImageData data = m_imgCache.get(m_ifd.getFilePath());
if (data != null)
m_img = data.getImage();
else
{
try
{
m_img = ImageIO.read(new File(ifd.getFilePath()));
m_imgCache.add(new JImageData(m_ifd.getFilePath(), m_img));
}
catch(IOException ex)
{}
}
m_img.setAccelerationPriority(0.85f);
m_orientation = 0;
m_bPreloadDirty = true;
}
repaint();
if (Main.m_settings.getCallGcInSetJpg())
System.gc();
if (Main.m_settings.isDebug())
System.err.println("Memory: (total, free, max): " + Runtime.getRuntime().totalMemory() + ", " + Runtime.getRuntime().freeMemory() + ", " + Runtime.getRuntime().maxMemory());
}
public int print(Graphics pg, PageFormat pageFormat, int pageIndex)
{
if (pageIndex == 0)
{
pg.translate((int)pageFormat.getImageableX(), (int)pageFormat.getImageableY());
RepaintManager currentManager = RepaintManager.currentManager(this);
currentManager.setDoubleBufferingEnabled(false);
Graphics2D g2d = (Graphics2D)pg;
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
doPaint(pg, new Dimension((int)pageFormat.getImageableWidth(), (int)pageFormat.getImageableHeight()), true);
currentManager.setDoubleBufferingEnabled(true);
System.gc();
return PAGE_EXISTS;
}
else return NO_SUCH_PAGE;
}
public void paintComponent(Graphics g)
{
Graphics gd = g.create();
if (m_bZoomDirty) internalSetZoom();
if (doPaint(gd, m_zoom == 0.0f ? getSize() : getPreferredSize(), false))
{
if (m_bPreloadDirty)
{
m_bPreloadDirty = false;
Main.m_mainFrame.doPreload();
}
}
}
protected void doScaleRotate(BufferedImage img, JIfdData ifd, Graphics g, Dimension dim)
{
Graphics2D g2d = (Graphics2D)g;
double dx=0, dy=0, d = 0;
int xOffset = 0;
int yOffset = 0;
int w = img.getWidth(null);
int h = img.getHeight(null);
switch(ifd != null ? getOrientation(ifd) : getOrientation())
{
case 1: // 0�
dx = (double)dim.width / (double)w;
dy = (double)dim.height / (double)h;
d = Math.min(dx, dy);
g2d.scale(d, d);
if ((int)(w*d + .5) < dim.width)
xOffset = (int)((dim.width - w*d)/(2.0*d) + 0.5);
if ((int)(h*d + .5) < dim.height)
yOffset = (int)((dim.height - h*d)/(2.0*d) + 0.5);
g2d.translate(xOffset, yOffset);
break;
case 2: // <->
dx = (double)dim.width / (double)w;
dy = (double)dim.height / (double)h;
d = Math.min(dx, dy);
g2d.translate(w*d, 0);
g2d.scale(-d, d);
if ((int)(w*d + .5) < dim.width)
xOffset = (int)((dim.width - w*d)/(2.0*d) + 0.5);
if ((int)(h*d + .5) < dim.height)
yOffset = (int)((dim.height - h*d)/(2.0*d) + 0.5);
g2d.translate(-xOffset, yOffset);
break;
case 3: // 180�
dx = (double)dim.width / (double)w;
dy = (double)dim.height / (double)h;
d = Math.min(dx, dy);
g2d.rotate(Math.PI);
g2d.translate(-w*d, -h*d);
g2d.scale(d, d);
if ((int)(w*d + .5) < dim.width)
xOffset = (int)((dim.width - w*d)/(2.0*d) + 0.5);
if ((int)(h*d + .5) < dim.height)
yOffset = (int)((dim.height - h*d)/(2.0*d) + 0.5);
g2d.translate(-xOffset, -yOffset);
break;
case 4: // 180� + <->
dx = (double)dim.width / (double)w;
dy = (double)dim.height / (double)h;
d = Math.min(dx, dy);
g2d.rotate(Math.PI);
g2d.translate(0, -h*d);
g2d.scale(-d, d);
if ((int)(w*d + .5) < dim.width)
xOffset = (int)((dim.width - w*d)/(2.0*d) + 0.5);
if ((int)(h*d + .5) < dim.height)
yOffset = (int)((dim.height - h*d)/(2.0*d) + 0.5);
g2d.translate(xOffset, -yOffset);
break;
case 5: // -90� + <->
dx = (double)dim.width / (double)h;
dy = (double)dim.height / (double)w;
d = Math.min(dx, dy);
g2d.rotate(Math.PI/2);
g2d.scale(d, -d);
if ((int)(h*d + .5) < dim.width)
xOffset = (int)((dim.width - h*d)/(2.0*d) + 0.5);
if ((int)(w*d + .5) < dim.height)
yOffset = (int)((dim.height - w*d)/(2.0*d) + 0.5);
g2d.translate(yOffset, xOffset);
break;
case 6: // -90�
dx = (double)dim.width / (double)h;
dy = (double)dim.height / (double)w;
d = Math.min(dx, dy);
g2d.rotate(Math.PI/2);
g2d.translate(0, -h*d);
g2d.scale(d, d);
if ((int)(h*d + .5) < dim.width)
xOffset = (int)((dim.width - h*d)/(2.0*d) + 0.5);
if ((int)(w*d + .5) < dim.height)
yOffset = (int)((dim.height - w*d)/(2.0*d) + 0.5);
g2d.translate(yOffset, -xOffset);
break;
case 7: // 90� + <->
dx = (double)dim.width / (double)h;
dy = (double)dim.height / (double)w;
d = Math.min(dx, dy);
g2d.rotate(-Math.PI/2);
g2d.translate(-w*d, h*d);
g2d.scale(d, -d);
if ((int)(h*d + .5) < dim.width)
xOffset = (int)((dim.width - h*d)/(2.0*d) + 0.5);
if ((int)(w*d + .5) < dim.height)
yOffset = (int)((dim.height - w*d)/(2.0*d) + 0.5);
g2d.translate(-yOffset, -xOffset);
break;
case 8: // 90�
dx = (double)dim.width / (double)h;
dy = (double)dim.height / (double)w;
d = Math.min(dx, dy);
g2d.rotate(-Math.PI/2);
g2d.translate(-w*d, 0);
g2d.scale(d, d);
if ((int)(h*d + .5) < dim.width)
xOffset = (int)((dim.width - h*d)/(2.0*d) + 0.5);
if ((int)(w*d + .5) < dim.height)
yOffset = (int)((dim.height - w*d)/(2.0*d) + 0.5);
g2d.translate(yOffset, xOffset);
break;
}
}
public boolean doPaint(Graphics g, Dimension dim, boolean bPrint)
{
if (m_ifd != null && m_img != null)
{
double dx=0, dy=0, d = 0;
Graphics2D g2d = (Graphics2D)g;
int w = m_img.getWidth();
int h = m_img.getHeight();
if (dim == null) dim = new Dimension(w, h);
if (h > 0 && w > 0)
{
doScaleRotate(m_img, null, g, dim);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, Main.m_settings.getInterpolation());
Rectangle rect = g2d.getClipBounds();
if (rect == null)
rect = new Rectangle(0, 0, dim.width, dim.height);
if (!bPrint)
{
g2d.setColor(Main.m_settings.getImgViewBackColor());
g2d.fillRect((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
}
g2d.drawImage(m_img, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, this);
return true;
}
else
{
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
JViewport viewPort = (JViewport)getParent();
assert (viewPort != null);
Point pt = viewPort.getViewPosition();
dim = viewPort.getExtentSize();
String str = Main.getString("img_wait");
Rectangle2D rect = g2d.getFontMetrics().getStringBounds(str, g2d);
g2d.drawString(str, (int)pt.getX() + (int)((dim.getWidth() - rect.getWidth())/2), (int)pt.getY() + (int)((dim.getHeight() + rect.getHeight())/2));
if (Main.m_settings.isDebug())
System.err.println("no image !");
repaint(250);
}
}
return false;
}
public boolean saveOrientation(boolean bReload)
{
boolean flag = false;
if (m_ifd != null && m_orientation != 0)
{
RandomAccessFile file = null;
try
{
file = new RandomAccessFile(m_ifd.getFilePath(), "rw");
flag = m_ifd.updateTag(file, JExifTag.EXIFTAG_TAG_ORIENTATION, m_orientation);
}
catch(IOException ex)
{}
finally
{
try
{
if (file != null) file.close();
}
catch (IOException ex)
{}
}
}
if (flag)
{
if (bReload)
{
if (m_ifd.readFromFile(m_ifd.getFilePath()))
{
m_orientation = 0;
setJpg(m_ifd);
}
}
Main.m_mainFrame.updateTableItem(m_ifd);
}
else JOptionPane.showMessageDialog(Main.m_mainFrame, String.format(Main.getString("img_saveorientation_failed"), m_ifd.getFilePath()), Main.getMessageBoxCaption(), JOptionPane.ERROR_MESSAGE);
return flag;
}
public boolean export()
{
boolean flag = false;
if (m_img != null)
{
JExportDialog dia = new JExportDialog(Main.m_mainFrame, this);
dia.m_quality = Main.m_settings.getExportQuality();
dia.m_scaleFactor = Main.m_settings.getExportScaleFactor();
dia.m_bOptimizedHuf = Main.m_settings.isExportJpegOptimizedHufTable();
dia.m_mimeType = Main.m_settings.getExportMimeType();
dia.m_file = new File(m_ifd.getFilePath());
if (dia.doModal())
{
Main.m_mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Main.m_settings.setExportQuality((float)dia.m_quality);
Main.m_settings.setExportScalefactor((float)dia.m_scaleFactor);
Main.m_settings.setExportJpegOptimizedHufTable(dia.m_bOptimizedHuf);
Main.m_settings.setExportMimeType(dia.m_mimeType);
if (dia.m_mimeType.equals("image/png"))
flag = savePNG(dia.m_file, m_img, null, dia.m_scaleFactor);
else if (dia.m_mimeType.equals("image/jpeg"))
flag = saveJPG(dia.m_file, m_img, null, dia.m_scaleFactor, dia.m_quality, dia.m_bOptimizedHuf);
else if (dia.m_mimeType.equals("image/bmp"))
flag = saveBMP(dia.m_file, m_img, null, dia.m_scaleFactor);
Main.m_mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
return flag;
}
protected boolean saveJPG(File file, BufferedImage img, JIfdData ifd, double scaleFactor, double quality, boolean bOptimizedHuffman)
{
boolean flag = false;
if (img != null)
{
FileImageOutputStream fios = null;
try
{
if (file.exists())
file.delete();
BufferedImage bi = getBufferedImage(img, ifd, scaleFactor);
Iterator iter = ImageIO.getImageWritersByMIMEType("image/jpeg");
if (iter.hasNext())
{
ImageWriter writer = (ImageWriter)iter.next();
if (Main.m_settings.isDebug())
System.err.println(writer.getClass().getName());
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality((float)quality);
if (bOptimizedHuffman && iwp instanceof JPEGImageWriteParam)
{
JPEGImageWriteParam iwpjpeg = (JPEGImageWriteParam)iwp;
// this can reduce size, but can also break badly written decoders
iwpjpeg.setOptimizeHuffmanTables(true);
}
writer.setOutput(fios = new FileImageOutputStream(file));
writer.write(null, new IIOImage(bi, null, null), iwp);
writer.dispose();
flag = true;
}
}
catch(IOException ex)
{
if (Main.m_settings.isDebug())
System.err.println(ex.toString());
}
if (fios != null)
{
try
{
fios.close();
}
catch(IOException ex)
{}
}
}
return flag;
}
protected boolean saveBMP(File file, BufferedImage img, JIfdData ifd, double scaleFactor)
{
boolean flag = false;
if (img != null)
{
FileImageOutputStream fios = null;
try
{
if (file.exists())
file.delete();
BufferedImage bi = getBufferedImage(img, ifd, scaleFactor);
Iterator iter = ImageIO.getImageWritersByMIMEType("image/bmp");
if (iter.hasNext())
{
ImageWriter writer = (ImageWriter)iter.next();
if (Main.m_settings.isDebug())
System.err.println(writer.getClass().getName());
writer.setOutput(fios = new FileImageOutputStream(file));
writer.write(new IIOImage(bi, null, null));
writer.dispose();
flag = true;
}
}
catch(IOException ex)
{
if (Main.m_settings.isDebug())
System.err.println(ex.toString());
}
if (fios != null)
{
try
{
fios.close();
}
catch(IOException ex)
{}
}
}
return flag;
}
protected boolean savePNG(File file, BufferedImage img, JIfdData ifd, double scaleFactor)
{
boolean flag = false;
if (img != null)
{
FileImageOutputStream fios = null;
try
{
if (file.exists())
file.delete();
BufferedImage bi = getBufferedImage(img, ifd, scaleFactor);
PngEncoderB pngEnc = new PngEncoderB(bi);
pngEnc.setCompressionLevel(9);
fios = new FileImageOutputStream(file);
byte [] data = pngEnc.pngEncode(false);
if (data != null)
fios.write(data);
flag = true;
}
catch(IOException ex)
{
if (Main.m_settings.isDebug())
System.err.println(ex.toString());
}
if (fios != null)
{
try
{
fios.close();
}
catch(IOException ex)
{}
}
}
return flag;
}
public Dimension getImageSize(BufferedImage img, JIfdData ifd, double scaleFactor)
{
int w = img.getWidth(null), h = img.getHeight(null);
int d;
switch(ifd == null ? getOrientation() : getOrientation(ifd))
{
case 5: // -90° + <->
case 6: // -90°
case 7: // 90° + <->
case 8: // 90°
d = w;
w = h;
h = d;
break;
}
h = (int)(scaleFactor * (double)h + .5);
w = (int)(scaleFactor * (double)w + .5);
return new Dimension(w, h);
}
public BufferedImage getBufferedImage(BufferedImage img, JIfdData ifd, double scaleFactor)
{
Dimension dim = getImageSize(img, ifd, scaleFactor);
BufferedImage bi = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(dim.width, dim.height);
Graphics g = bi.createGraphics();
doScaleRotate(img, ifd, g, dim);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, Main.m_settings.getInterpolation());
g.drawImage(img, 0, 0, null);
g.dispose();
return bi;
}
}
final class JImageData
{
private BufferedImage m_image;
private String m_file;
public JImageData(String file, BufferedImage image)
{
m_image = image;
m_file = file;
}
public String getFile()
{
return m_file;
}
public BufferedImage getImage()
{
return m_image;
}
public String toString()
{
return m_file;
}
}
final class JImageCache
{
private ArrayList<JImageData> m_list;
private int m_size;
private boolean m_bCallGc;
private void trace()
{
if (Main.m_settings.isDebug())
{
System.err.println("Cache:");
for (JImageData data : m_list)
System.err.println("\t" + data);
}
}
public JImageCache(int size, boolean bCallGc)
{
m_size = size;
m_bCallGc = bCallGc;
m_list = new ArrayList<JImageData>();
}
protected JImageData find(String file)
{
if (m_list != null && m_list.size() > 0)
{
for (int i=m_list.size()-1; i>=0; i--)
{
if (m_list.get(i).getFile().equals(file))
return m_list.get(i);
}
/*
for (JImageData data : m_list)
{
if (data.getFile().equals(file))
return data;
}
*/
}
return null;
}
public void remove(String file)
{
if (m_list != null && m_list.size() > 0)
{
boolean flag = false;
for (int i=m_list.size()-1; i>=0; i--)
{
if (m_list.get(i).getFile().equals(file))
{
m_list.remove(i);
flag = true;
break;
}
}
if (flag && m_bCallGc)
System.gc();
}
if (Main.m_settings.isDebug())
trace();
}
public void add(JImageData data)
{
if (find(data.getFile()) == null)
{
m_list.add(data);
boolean flag = false;
while(m_list.size() > m_size)
{
m_list.remove(0);
flag = true;
}
if (flag && m_bCallGc)
System.gc();
}
if (Main.m_settings.isDebug())
trace();
}
public JImageData get(String file)
{
return find(file);
}
public void clear()
{
m_list.clear();
if (m_bCallGc)
System.gc();
}
}