Kim Ryan
asked on
Browser based image selection rectangle
I have developed a web based image editing tool with ImageMagick.
I create a form that displays a jpeg image. I can let the user type in co-ordinates to crop the image to, but I would also like to let them do this visually.
I would like to be able to crop an image by clicking in the image to specify the new top left hand corner, drag the mouse while holding down selection button, and then release the mouse to specify the new bottom right corner for the image. During the 'drag' it would be nice to see the bounding rectangle appear.
I imagine a Java applet would be the tool to do this. Could it also report back the top left and bottom right coordinates of the rectangle in pixels? These would need to be relative to the image, ie position 0,0 is the top left corner of the original image. Once I have the co-ords, I can get ImageMagick to create a new cropped jpeg file.
Any help appreciated.
I create a form that displays a jpeg image. I can let the user type in co-ordinates to crop the image to, but I would also like to let them do this visually.
I would like to be able to crop an image by clicking in the image to specify the new top left hand corner, drag the mouse while holding down selection button, and then release the mouse to specify the new bottom right corner for the image. During the 'drag' it would be nice to see the bounding rectangle appear.
I imagine a Java applet would be the tool to do this. Could it also report back the top left and bottom right coordinates of the rectangle in pixels? These would need to be relative to the image, ie position 0,0 is the top left corner of the original image. Once I have the co-ords, I can get ImageMagick to create a new cropped jpeg file.
Any help appreciated.
Here is a small application that does approximately what you requested:
import java.awt.*;
import java.awt.event.*;
public class MyApp extends WindowAdapter{
public static void main(String [] args){
Image img = Toolkit.getDefaultToolkit( ).getImage ("image.gi f");
Frame f = new Frame("Cropping selection");
MediaTracker tracker = new MediaTracker(f);
tracker.addImage(img,1);
try{
tracker.waitForAll();
} catch (InterruptedException e){
return;
}
ImageCropSelection ics = new ImageCropSelection(img);
f.reshape(50,50,500,500);
f.setLayout(null);
ics.reshape(50,50,img.getW idth(null) ,img.getHe ight(null) );
f.add(ics);
f.addWindowListener(new MyApp());
f.show();
}
public void windowClosing(WindowEvent evt){
evt.getWindow().dispose();
System.exit(0);
}
}
class ImageCropSelection extends Canvas{
private Image image;
int cropX,cropY,cropWidth,crop Height;
Rectangle selectionRect;
public ImageCropSelection(Image img){
image = img;
cropWidth = img.getWidth(null);
cropHeight = img.getHeight(null);
}
public void paint(Graphics g){
g.clipRect(0,0,cropWidth,c ropHeight) ;
g.drawImage(image,-cropX,- cropY,this );
g.setXORMode(Color.white);
if (selectionRect!=null)
g.drawRect(selectionRect.x ,selection Rect.y,sel ectionRect .width,sel ectionRect .height);
g.setPaintMode();
}
public boolean mouseDown(Event evt, int x, int y){
selectionRect = new Rectangle();
selectionRect.x = x;
selectionRect.y = y;
selectionRect.width = 0;
selectionRect.height = 0;
return false;
}
public boolean mouseDrag(Event evt, int x, int y){
if (x>selectionRect.x)
selectionRect.width = x-selectionRect.x;
else
selectionRect.x = x;
if (y>selectionRect.y)
selectionRect.height = y-selectionRect.y;
else
selectionRect.y = y;
repaint();
return false;
}
public boolean mouseUp(Event evt, int x, int y){
cropX += selectionRect.x;
cropY += selectionRect.y;
cropWidth = selectionRect.width;
cropHeight = selectionRect.height;
selectionRect = null;
repaint();
return false;
}
}
To keep it from blinking when you make the selection, add the following method to the ImageCropSelection class:
public void update(Graphics g){
Image doubleBufferingImage = createImage(bounds().width ,bounds(). height);
Graphics dbGraphics = doubleBufferingImage.getGr aphics();
dbGraphics.setColor(getBac kground()) ;
dbGraphics.fillRect(0,0,bo unds().wid th,bounds( ).height);
dbGraphics.setColor(getFor eground()) ;
paint(dbGraphics);
g.drawImage(doubleBufferin gImage,0,0 ,null);
}
Make sure you have an "image.gif" image in the dir where the application is.
Hope this helps, Sasha.
import java.awt.*;
import java.awt.event.*;
public class MyApp extends WindowAdapter{
public static void main(String [] args){
Image img = Toolkit.getDefaultToolkit(
Frame f = new Frame("Cropping selection");
MediaTracker tracker = new MediaTracker(f);
tracker.addImage(img,1);
try{
tracker.waitForAll();
} catch (InterruptedException e){
return;
}
ImageCropSelection ics = new ImageCropSelection(img);
f.reshape(50,50,500,500);
f.setLayout(null);
ics.reshape(50,50,img.getW
f.add(ics);
f.addWindowListener(new MyApp());
f.show();
}
public void windowClosing(WindowEvent evt){
evt.getWindow().dispose();
System.exit(0);
}
}
class ImageCropSelection extends Canvas{
private Image image;
int cropX,cropY,cropWidth,crop
Rectangle selectionRect;
public ImageCropSelection(Image img){
image = img;
cropWidth = img.getWidth(null);
cropHeight = img.getHeight(null);
}
public void paint(Graphics g){
g.clipRect(0,0,cropWidth,c
g.drawImage(image,-cropX,-
g.setXORMode(Color.white);
if (selectionRect!=null)
g.drawRect(selectionRect.x
g.setPaintMode();
}
public boolean mouseDown(Event evt, int x, int y){
selectionRect = new Rectangle();
selectionRect.x = x;
selectionRect.y = y;
selectionRect.width = 0;
selectionRect.height = 0;
return false;
}
public boolean mouseDrag(Event evt, int x, int y){
if (x>selectionRect.x)
selectionRect.width = x-selectionRect.x;
else
selectionRect.x = x;
if (y>selectionRect.y)
selectionRect.height = y-selectionRect.y;
else
selectionRect.y = y;
repaint();
return false;
}
public boolean mouseUp(Event evt, int x, int y){
cropX += selectionRect.x;
cropY += selectionRect.y;
cropWidth = selectionRect.width;
cropHeight = selectionRect.height;
selectionRect = null;
repaint();
return false;
}
}
To keep it from blinking when you make the selection, add the following method to the ImageCropSelection class:
public void update(Graphics g){
Image doubleBufferingImage = createImage(bounds().width
Graphics dbGraphics = doubleBufferingImage.getGr
dbGraphics.setColor(getBac
dbGraphics.fillRect(0,0,bo
dbGraphics.setColor(getFor
paint(dbGraphics);
g.drawImage(doubleBufferin
}
Make sure you have an "image.gif" image in the dir where the application is.
Hope this helps, Sasha.
If you have any further questions or you want more explanations on what/how I did that, please don't hesitate to ask...
Oops, the application I posted does not work correctly if you try to select a "wrong" square, with negative width or height. This is a fixed one:
import java.awt.*;
import java.awt.event.*;
public class MyApp extends WindowAdapter{
public static void main(String [] args){
Image img = Toolkit.getDefaultToolkit( ).getImage ("image.gi f");
Frame f = new Frame("Cropping selection");
MediaTracker tracker = new MediaTracker(f);
tracker.addImage(img,1);
try{
tracker.waitForAll();
} catch (InterruptedException e){
return;
}
ImageCropSelection ics = new ImageCropSelection(img);
f.reshape(50,50,500,500);
f.setLayout(null);
ics.reshape(50,50,img.getW idth(null) ,img.getHe ight(null) );
f.add(ics);
f.addWindowListener(new MyApp());
f.show();
}
public void windowClosing(WindowEvent evt){
evt.getWindow().dispose();
System.exit(0);
}
}
class ImageCropSelection extends Canvas{
private Image image;
int cropX,cropY,cropWidth,crop Height;
int dragX, dragY;
Point pressPoint;
public ImageCropSelection(Image img){
image = img;
cropWidth = img.getWidth(null);
cropHeight = img.getHeight(null);
}
public void paint(Graphics g){
g.clipRect(0,0,cropWidth,c ropHeight) ;
g.drawImage(image,-cropX,- cropY,this );
g.setXORMode(Color.white);
if (pressPoint!=null){
// Normalize the rectangle:
int x,y,width,height;
if (dragX<pressPoint.x){
x = dragX;
width = pressPoint.x-dragX;
}
else{
x = pressPoint.x;
width = dragX-pressPoint.x;
}
if (dragY<pressPoint.y){
y = dragY;
height = pressPoint.y-dragY;
}
else{
y = pressPoint.y;
height = dragY-pressPoint.y;
}
g.drawRect(x,y,width,heigh t);
}
g.setPaintMode();
}
public boolean mouseDown(Event evt, int x, int y){
pressPoint = new Point(x,y);
return false;
}
public boolean mouseDrag(Event evt, int x, int y){
dragX = x;
dragY = y;
repaint();
return false;
}
public boolean mouseUp(Event evt, int x, int y){
if (pressPoint.x<x){
cropX += pressPoint.x;
cropWidth = x-pressPoint.x;
}
else{
cropX += x;
cropWidth = pressPoint.x-x;
}
if (pressPoint.y<y){
cropY += pressPoint.y;
cropHeight = y-pressPoint.y;
}
else{
cropY += y;
cropHeight = pressPoint.y-y;
}
pressPoint = null;
repaint();
return false;
}
}
import java.awt.*;
import java.awt.event.*;
public class MyApp extends WindowAdapter{
public static void main(String [] args){
Image img = Toolkit.getDefaultToolkit(
Frame f = new Frame("Cropping selection");
MediaTracker tracker = new MediaTracker(f);
tracker.addImage(img,1);
try{
tracker.waitForAll();
} catch (InterruptedException e){
return;
}
ImageCropSelection ics = new ImageCropSelection(img);
f.reshape(50,50,500,500);
f.setLayout(null);
ics.reshape(50,50,img.getW
f.add(ics);
f.addWindowListener(new MyApp());
f.show();
}
public void windowClosing(WindowEvent evt){
evt.getWindow().dispose();
System.exit(0);
}
}
class ImageCropSelection extends Canvas{
private Image image;
int cropX,cropY,cropWidth,crop
int dragX, dragY;
Point pressPoint;
public ImageCropSelection(Image img){
image = img;
cropWidth = img.getWidth(null);
cropHeight = img.getHeight(null);
}
public void paint(Graphics g){
g.clipRect(0,0,cropWidth,c
g.drawImage(image,-cropX,-
g.setXORMode(Color.white);
if (pressPoint!=null){
// Normalize the rectangle:
int x,y,width,height;
if (dragX<pressPoint.x){
x = dragX;
width = pressPoint.x-dragX;
}
else{
x = pressPoint.x;
width = dragX-pressPoint.x;
}
if (dragY<pressPoint.y){
y = dragY;
height = pressPoint.y-dragY;
}
else{
y = pressPoint.y;
height = dragY-pressPoint.y;
}
g.drawRect(x,y,width,heigh
}
g.setPaintMode();
}
public boolean mouseDown(Event evt, int x, int y){
pressPoint = new Point(x,y);
return false;
}
public boolean mouseDrag(Event evt, int x, int y){
dragX = x;
dragY = y;
repaint();
return false;
}
public boolean mouseUp(Event evt, int x, int y){
if (pressPoint.x<x){
cropX += pressPoint.x;
cropWidth = x-pressPoint.x;
}
else{
cropX += x;
cropWidth = pressPoint.x-x;
}
if (pressPoint.y<y){
cropY += pressPoint.y;
cropHeight = y-pressPoint.y;
}
else{
cropY += y;
cropHeight = pressPoint.y-y;
}
pressPoint = null;
repaint();
return false;
}
}
ASKER
Thanks for the answer. I am not sure about the part where you say I should have an image.gif in my dir. I have a client based PerlMagick app the will genearte a web page containing a JPEG. I would like the java app to display the bounding rectangle over the image and then return the coordinates to the client program running on Unix aserver.
Also, xabi, if you can also solve this problem as easily/neatly in java script, I can offer another 400 pts. I only deleted the question because mplungjan suggested it would be very hard to do.
Also, xabi, if you can also solve this problem as easily/neatly in java script, I can offer another 400 pts. I only deleted the question because mplungjan suggested it would be very hard to do.
Is this implementable at all in JS??
teraplane: You asked for a Java solution and I provided one... It appears that a Java solution is not exactly what you need. I am not familiar with PerlMagick, I just posted a solution of the part I am familiar with...
teraplane: You asked for a Java solution and I provided one... It appears that a Java solution is not exactly what you need. I am not familiar with PerlMagick, I just posted a solution of the part I am familiar with...
Tereplane I did it. Grab the zip file with all the code here:
todoweb.webhostme.com/drag .zip
xabi
PS: If you need help just ask. I will be so glad with 400 more points :)
todoweb.webhostme.com/drag
xabi
PS: If you need help just ask. I will be so glad with 400 more points :)
s/Tereplane/Teraplane
:)
:)
ASKER
Sasha_Mapa, to explain... I have a Perl based program that runs on a Unix server. It works fine but the interface is CGI/forms based. To do the cropping I need the selection rectangle, which means embedding Java/Javascript solution in a web page. Your solution is fine. My only queries, how to embed the applet in a web page ans transmit the co-ords back to the program running on the server.
xabi, your solution is great, just what I need!. 400 points await you in the Javascript area.
xabi, your solution is great, just what I need!. 400 points await you in the Javascript area.
How to embedd an applet in a webpage:
<APPLET CODE=MyApplet.class WIDTH=300 HEIGHT=300> </APPLET>
How to transmit the coords to the server:
Have the server listen to some port and open a socket from the client to the server and transmit the coordinates.
Anyway, if you can use a JavaScript solution, it's probably better than java at least because JS is easier...
<APPLET CODE=MyApplet.class WIDTH=300 HEIGHT=300> </APPLET>
How to transmit the coords to the server:
Have the server listen to some port and open a socket from the client to the server and transmit the coordinates.
Anyway, if you can use a JavaScript solution, it's probably better than java at least because JS is easier...
ASKER
Sasha_Mapa, can you propose your answer again and I will give you the points anyway, as you still solved the problem for me. I gave xabi points in the Javascript area.
Umm, thanks! :-) Did you try to run it? It's really cool :-)
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Btw, you could just click on the "Accept Comment as Answer" button on the titlebar of my comment...
ASKER
Haven't tried to run it but will keep the code in case I want to try a Java solution at some time.
VERY cool, Xabi!!!
Sasha: Cool too, especially since it does the actual crop too
But how about
MyApp.java:88: The method boolean mouseDown(java.awt.Event, int, int) declared in class ImageCropSelection is not deprecated, but overrides a deprecated method of the same signature declared in class java.awt.Component.
public boolean mouseDown(Event evt, int x, int y){
^
MyApp.java:94: The method boolean mouseDrag(java.awt.Event, int, int) declared in class ImageCropSelection is not deprecated, but overrides a deprecated method of the same signature declared in class java.awt.Component.
public boolean mouseDrag(Event evt, int x, int y){
^
MyApp.java:103: The method boolean mouseUp(java.awt.Event, int, int) declared in class ImageCropSelection is not deprecated, but overrides a deprecated method of the same signature declared in class java.awt.Component.
public boolean mouseUp(Event evt, int x, int y){
^
MyApp.java:20: Note: The method void reshape(int, int, int, int) in class java.awt.Component has been deprecated.
f.reshape(50,50,500,500);
^
MyApp.java:22: Note: The method void reshape(int, int, int, int) in class java.awt.Component has been deprecated.
ics.reshape(50,50,img.getW idth(null) ,img.getHe ight(null) );
^
Note: MyApp.java uses or overrides a deprecated API. Please consult the documentation for a better alternative.
1 warning
Sasha: Cool too, especially since it does the actual crop too
But how about
MyApp.java:88: The method boolean mouseDown(java.awt.Event, int, int) declared in class ImageCropSelection is not deprecated, but overrides a deprecated method of the same signature declared in class java.awt.Component.
public boolean mouseDown(Event evt, int x, int y){
^
MyApp.java:94: The method boolean mouseDrag(java.awt.Event, int, int) declared in class ImageCropSelection is not deprecated, but overrides a deprecated method of the same signature declared in class java.awt.Component.
public boolean mouseDrag(Event evt, int x, int y){
^
MyApp.java:103: The method boolean mouseUp(java.awt.Event, int, int) declared in class ImageCropSelection is not deprecated, but overrides a deprecated method of the same signature declared in class java.awt.Component.
public boolean mouseUp(Event evt, int x, int y){
^
MyApp.java:20: Note: The method void reshape(int, int, int, int) in class java.awt.Component has been deprecated.
f.reshape(50,50,500,500);
^
MyApp.java:22: Note: The method void reshape(int, int, int, int) in class java.awt.Component has been deprecated.
ics.reshape(50,50,img.getW
^
Note: MyApp.java uses or overrides a deprecated API. Please consult the documentation for a better alternative.
1 warning
And I apologise for having been instrumental in getting the javascript question deleted and having underestimated my fellow experts.
(I still hope I was right about it being a complex thing to program - the 20K of javascript sort of support me in this ;-)
Michel
(I still hope I was right about it being a complex thing to program - the 20K of javascript sort of support me in this ;-)
Michel
I am used to programming in Java 1.0, and some of the methods I used are deprecated since 1.1. The same things can be achieved with 1.1 using mouseListeners and setBounds() instead of reshape().
Who are you, mplungjan? What is your relation to this question?
Who are you, mplungjan? What is your relation to this question?
Sorry - I was the one who, in the javascript section, where teraplane first asked - told him to go to the java section since the JavaScript needed to do the select would be quite difficult...
Michel
Michel
Michel:
did you see my javascript code for this option?
xabi
did you see my javascript code for this option?
xabi
Yes, xabi - it is VERY elegant. Where does the black lines in the rectangle come from??? Some border?
just 1 pixel width layers :)
xabi
xabi
xabi