/*******************************************************************************
* Rhythos Editor is a game editor and project management tool for making RPGs on top of the Rhythos Game system.
*
* Copyright (C) 2013 David Maletz
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package mrpg.editor.resource;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import mrpg.editor.MapEditor;
import mrpg.editor.WorkspaceBrowser;
public class CroppedImage extends ImageResource {
private static final long serialVersionUID = -5394199071824545816L;
public static final String EXT = "cimg", TYPE = "ci"; private static final short VERSION=1;
private ImageResource image; private int x = 0, y = 0, w, h; private BufferedImage cache;
public CroppedImage(File f, MapEditor editor){super(f, editor);}
public String getType(){return TYPE;}
public short getVersion(){return VERSION;}
public JDialog getProperties(){return new Properties(this);}
public BufferedImage getImage(){
if(cache == null){
cache = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
cache.getGraphics().drawImage(image.getImage(), -x, -y, null);
} return cache;
}
public void addToProject(Project p, boolean changeProject) throws Exception {
super.addToProject(p, changeProject);
if(changeProject){
image = (ImageResource)p.getById(image.getType(), image.getId());
}
}
public boolean isCompatible(Project p){
try{p.getById(image.getType(), image.getId()); return super.isCompatible(p);}catch(Exception e){return false;}
}
public void copyAssets(Project p) throws Exception {
if(!image.isCompatible(p)) image.copyAssets(p);
p.editor.getBrowser().addResource(Resource.readFile(image.copy(p.getFile(), p, false), p.editor), p);
}
public void writeInner(DataOutputStream out) throws Exception {
ImageResource.write(out, image); out.writeShort(x); out.writeShort(y); out.writeShort(w); out.writeShort(h);
}
public void readInner(DataInputStream in) throws Exception {
Project p = WorkspaceBrowser.getProject(this); image = ImageResource.read(in, p);
if(image == null) throw new Exception();
x = in.readShort(); y = in.readShort(); w = in.readShort(); h = in.readShort();
}
protected void read(File f) throws Exception {MapEditor.deferRead(this, MapEditor.DEF_IMG_RESOURCE);}
public static CroppedImage create(ImageResource im) {
try{
Resource parent = im.getParent(); String dir = parent.getFile().toString();
File f = new File(dir,"Cropped "+im.getName()+"."+EXT);
CroppedImage ret = new CroppedImage(f,im.editor); ret._setName(null); ret.image = im;
ret.x = 0; ret.y = 0; BufferedImage img = ret.image.getImage(); ret.w = img.getWidth(); ret.h = img.getHeight();
Project p = WorkspaceBrowser.getProject(im); ret.properties();
if(!((Properties)ret.properties).updated) throw new Exception();
ret.addToProject(p,false); im.editor.getBrowser().addResource(ret, parent); return ret;
}catch(Exception e){return null;}
}
public static CroppedImage create(Project p, ImageResource im, String name, int x, int y, int w, int h){return create(p, im, im.getParent(), name, x, y, w, h);}
public static CroppedImage create(Project p, ImageResource im, Resource parent, String name, int x, int y, int w, int h){
try{
String dir = parent.getFile().toString(); File f = new File(dir,name+"."+EXT); if(f.exists()) throw new Exception();
CroppedImage ret = new CroppedImage(f,im.editor); ret.newId(p);
ret.image = im; ret.x = x; ret.y = y; ret.w = w; ret.h = h;
parent.add(ret); ret.save(); ret.addToProject(p, false); return ret;
}catch(Exception e){return null;}
}
private static class Properties extends TypedResource.Properties {
private static final long serialVersionUID = -4987880557990107307L;
private ImageCropper cropper; private ImageResource img; private JComboBox lock;
public Properties(CroppedImage i){super(i, "Cropped Image Properties", true);}
public void addControls(JPanel settings){
JPanel inner = new JPanel(new BorderLayout()); inner.setBorder(BorderFactory.createTitledBorder("Image"));
cropper = new ImageCropper();
JScrollPane pane = new JScrollPane(cropper); pane.setPreferredSize(new Dimension(400, 350));
pane.setBorder(BorderFactory.createLoweredBevelBorder()); inner.add(pane, BorderLayout.CENTER);
JPanel p2 = new JPanel(); p2.add(new JLabel("Lock to Grid: "));
lock = new JComboBox(new String[]{"1","2","4","8","16","32","64"}); lock.addActionListener(this);
lock.setEditable(true); p2.add(lock); p2.add(new JLabel(" ")); JButton b = new JButton("Set");
b.setActionCommand(MapEditor.SET); b.addActionListener(this); p2.add(b); inner.add(p2, BorderLayout.SOUTH);
settings.add(inner);
}
public void updateControls(){
CroppedImage image = (CroppedImage)resource;
img = image.image; cropper.setImage(img.getImage(), image.x, image.y, image.w, image.h);
}
public void acceptControls(){
CroppedImage image = (CroppedImage)resource; image.image = img; image.x = cropper.x1; image.y = cropper.y1;
image.w = cropper.x2-cropper.x1; image.h = cropper.y2-cropper.y1; image.cache = null;
}
public boolean saveOnEdit(){return true;}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if(e.getSource() == lock){
try{cropper.lock = Integer.parseInt(lock.getSelectedItem().toString());}catch(Exception ex){lock.setSelectedItem(Integer.toString(cropper.lock));}
} if(command == MapEditor.SET){
ImageResource im = ImageResource.choose(WorkspaceBrowser.getProject(img), img);
if(im != null && im != resource){img = im; cropper.setImage(img.getImage());}
} else super.actionPerformed(e);
}
}
public String getExt(){return EXT;}
private static final Color blue = new Color(0xb8cfe5), blue_trans = new Color(0x33b8cfe5, true);
private static final BasicStroke stroke = new BasicStroke(2);
private static final int NONE=0, INSIDE=1, LEFT=2, RIGHT=3, UP=4, BOTTOM=5, UP_LEFT=6, BOTTOM_LEFT=7, UP_RIGHT=8, BOTTOM_RIGHT=9, SZ=8, SZ2 = SZ*2;
private static class ImageCropper extends JPanel implements MouseListener, MouseMotionListener {
private static final long serialVersionUID = -7395640251248933309L;
private int x1, y1, x2, y2; private BufferedImage image; private int mX, mY, pX1, pY1, pX2, pY2, type, lock = 1;
public ImageCropper(){setOpaque(false); addMouseListener(this); addMouseMotionListener(this);}
public void setImage(BufferedImage i){
image = i; x1 = Math.min(image.getWidth()-1, x1); y1 = Math.min(image.getHeight()-1, y1);
x2 = Math.min(image.getWidth(), Math.max(x2, x1+SZ2)); y2 = Math.min(image.getHeight(), Math.max(y2, y1+SZ2));
setPreferredSize(new Dimension(i.getWidth(), i.getHeight())); revalidate(); repaint();
}
public void setImage(BufferedImage i, int _x, int _y, int _w, int _h){
image = i; x1 = _x; y1 = _y; x2 = x1+_w; y2 = y1+_h; setPreferredSize(new Dimension(i.getWidth(), i.getHeight()));
}
public void mouseDragged(MouseEvent e){
int dX = e.getX()-mX, dY = e.getY()-mY;
switch(type){
case INSIDE:
x1 = Math.max(Math.min(image.getWidth()-(pX2-pX1), pX1+dX), 0); y1 = Math.max(Math.min(image.getHeight()-(pY2-pY1), pY1+dY), 0);
x2 = x1+(pX2-pX1); y2 = y1+(pY2-pY1); repaint(); break;
case LEFT: x1 = Math.min(x2-SZ2, Math.max(pX1+dX, 0)); repaint(); break;
case RIGHT: x2 = Math.max(Math.min(image.getWidth(), pX2+dX), x1+SZ2); repaint(); break;
case UP: y1 = Math.min(y2-SZ2, Math.max(pY1+dY, 0)); repaint(); break;
case BOTTOM: y2 = Math.max(y1+SZ2, Math.min(image.getHeight(), pY2+dY)); repaint(); break;
case UP_LEFT: x1 = Math.min(x2-SZ2, Math.max(pX1+dX, 0)); y1 = Math.min(y2-SZ2, Math.max(pY1+dY, 0)); repaint(); break;
case BOTTOM_LEFT: x1 = Math.min(x2-SZ2, Math.max(pX1+dX, 0)); y2 = Math.max(y1+SZ2, Math.min(image.getHeight(), pY2+dY)); repaint(); break;
case UP_RIGHT: x2 = Math.max(Math.min(image.getWidth(), pX2+dX), x1+SZ2); y1 = Math.min(y2-SZ2, Math.max(pY1+dY, 0)); repaint(); break;
case BOTTOM_RIGHT: x2 = Math.max(Math.min(image.getWidth(), pX2+dX), x1+SZ2); y2 = Math.max(y1+SZ2, Math.min(image.getHeight(), pY2+dY)); repaint(); break;
} if(lock != 1){
x1 = (x1/lock)*lock; x2 = Math.max(x1+lock, (x2/lock)*lock);
y1 = (y1/lock)*lock; y2 = Math.max(y1+lock, (y2/lock)*lock);
}
}
public void mouseMoved(MouseEvent e){
mX = e.getX(); mY = e.getY(); int l = x1+SZ, u = y1+SZ, r = x2-SZ-1, b = y2-SZ-1;
if(mX < x1-SZ || mY < y1-SZ || mX > x2+SZ-1 || mY > y2+SZ-1) type = NONE;
else if(mX < l && mY < u) type = UP_LEFT; else if(mX < l) type = (mY > b)?BOTTOM_LEFT:LEFT;
else if(mY < u) type = (mX > r)?UP_RIGHT:UP; else if(mX > r && mY > b) type = BOTTOM_RIGHT;
else if(mX > r) type = RIGHT; else if(mY > b) type = BOTTOM; else type = INSIDE;
switch(type){
case NONE: setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); break;
case INSIDE: setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); break;
case LEFT: setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); break;
case RIGHT: setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); break;
case UP: setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); break;
case BOTTOM: setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); break;
case UP_LEFT: setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); break;
case BOTTOM_LEFT: setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR)); break;
case UP_RIGHT: setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); break;
case BOTTOM_RIGHT: setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); break;
}
}
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mousePressed(MouseEvent e){
if(lock != 1){
x1 = (x1/lock)*lock; x2 = Math.max(x1+lock, (x2/lock)*lock);
y1 = (y1/lock)*lock; y2 = Math.max(y1+lock, (y2/lock)*lock); repaint();
} mX = e.getX(); mY = e.getY(); pX1 = x1; pY1 = y1; pX2 = x2; pY2 = y2; mouseMoved(e);
}
public void mouseReleased(MouseEvent e){}
public void paint(Graphics g){
g.drawImage(image, 0, 0, this); ((Graphics2D)g).setStroke(stroke);
g.setColor(blue_trans); g.fillRect(x1+1, y1+1, x2-x1-2, y2-y1-2); g.setColor(blue); g.drawRect(x1+1,y1+1,x2-x1-2,y2-y1-2);
}
}
}