Link to home
Start Free TrialLog in
Avatar of Kim Ryan
Kim RyanFlag for Australia

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.

Avatar of xabi
xabi

I did it for you with javascript but you removed the question. If you are interested just ask.

xabi
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.gif");
    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.getWidth(null),img.getHeight(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,cropHeight;
  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,cropHeight);
    g.drawImage(image,-cropX,-cropY,this);
    g.setXORMode(Color.white);
    if (selectionRect!=null)
      g.drawRect(selectionRect.x,selectionRect.y,selectionRect.width,selectionRect.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.getGraphics();
  dbGraphics.setColor(getBackground());
  dbGraphics.fillRect(0,0,bounds().width,bounds().height);
  dbGraphics.setColor(getForeground());
  paint(dbGraphics);
  g.drawImage(doubleBufferingImage,0,0,null);
}



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.gif");
    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.getWidth(null),img.getHeight(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,cropHeight;
  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,cropHeight);
    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,height);
    }
    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;
  }


}
Avatar of Kim Ryan

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.
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...
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 :)

s/Tereplane/Teraplane

:)
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.
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...
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
Avatar of Sasha_Mapa
Sasha_Mapa

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Btw, you could just click on the "Accept Comment as Answer" button on the titlebar of my comment...
Haven't tried to run it but will keep the code in case I want to try a Java solution at some time.
Avatar of Michel Plungjan
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.getWidth(null),img.getHeight(null));
               ^
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 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?
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:

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