Skip to main content

Doh! More confused now than ever. Drawing on a scaled image.

11 replies [Last post]
Darrin
Offline
Joined: 2006-02-17

I know I've posted a similar question before, but I'm still hoping someone here can either point me to a forum to help me out or see the error of my ways.

I recently read the majority of "Building Imaging Applications with Java" and I am now more confused than when I started (AWT or JAI or Java2D or ....).

All I want to know is how to draw a little line on an image that has been scaled. I've searched the Internet, read everything I can find, looked at examples (none of which did what I needed, but they wre all I could find) and basically banged my head against the computer for over a week now with no progress.

This is what I am seeing:

Currently, if the image is shown as full size, everything works just great, but if I zoom first, then try to draw a line, the line is too small, and it is not drawn where it should be.

Here is exactly what I am doing now. Maybe some kind soul will be able to tell me the error of my ways.

1) I get the user click points and save them off. These points are relative to the screen, and will be adjusted back to the image which is at 0,0.

For example, if the user clicks at 328, 54, that will be 0,0 on the image.

2) I create a line object using the points from the screen. For an example, say 328, 54, 687, 54 (a straight line across the top of the image that is 360 pixels wide).

3) I then call a method that first does a translate like this (in Java):

Graphics2D g2D = gti.getGraphics2D();

g2D.translate(transformPointX, transformPointY);

For this example, the transformPointX = 328, and Y = 54 (the same as the click point)

4) The method then does a scale. For this example, the scale is 100%, so no change is made.

5) The method then shifts the line to be drawn by adjusting the begin and end points. The formula for the adjustment is like this:

newX = oldX - (view width / 2) - (image width / 2)
newY = oldY - (view height / 2) - (image height / 2)

For this example, it works just fine. Now let me explain what happens when I zoom in to 90% of the image size.

First off, the line that I drew when the image was 100%, is scaled correctly, and in the position you would expect to see it in (across the top).

Going through the same steps again

1) The click points are saved off (the begin point is 346, 77, the end point is 669, 77).

2) The line object is created using the points I just mentioned.

3) I then call a method that first does a translate like this (in Java):

Graphics2D g2D = gti.getGraphics2D();

g2D.translate(transformPointX, transformPointY);

For this example, the transformPointX = 346, and Y = 77 (like the click point)

4) The method then does a scale. For this example, the scale is 90%. It does it like this:

g2D.scale(zoom, zoom);

where zoom = 0.90

5) Then the points of the line get reset by subtracting using the formula I gave above (the view area - the ORIGINAL image size)

Here is an image of what is happening.

<br />
______________________________ 100<br />
 ___________________________   90<br />
   _______________________     80<br />
     ___________________       70<br />
       _______________         60<br />
         ___________           40<br />
           _______             30<br />
             ___               20<br />
              _                10</p>
<p>

So, if you started at 100%, drew a line across the top, then went to 90%, tried to a line across the top, then when the draw took place you would see the screen looking like what it does as the 90 with the line at the 100 as well. If you then zoomed out again to 80% and tried to draw a line across the top, you would see the line at the 80, the 90 and the 100. If you do this all the way to 10%, then you would see what I have above.

If someone could explain to me what it is I'm doing wrong I'd really apprecite it.

There is a good bit of code, but the interesting aprts are marked with //!!!!!!!!!!!! to make it easy to navagate.

</p>
<p>private void drawLine()<br />
    {<br />
        m_jsPane = (JScrollPane) m_tabbedPane.getSelectedComponent();<br />
        m_port = m_jsPane.getViewport();</p>
<p>        GrabAndScrollLabel gnsLabel = (GrabAndScrollLabel) m_port.getView();</p>
<p>        m_currentImageIcon = (ImageIcon) gnsLabel.getIcon();</p>
<p>        m_currentImage = m_currentImageIcon.getImage();</p>
<p>        try<br />
        {<br />
            MediaTracker tracker = new MediaTracker(this);<br />
            tracker.addImage(m_currentImage, 0);<br />
            tracker.waitForID(0);<br />
        }<br />
        catch (InterruptedException ie)<br />
        {<br />
            System.out.println("Interupted in draw");<br />
        }</p>
<p>        int iw = m_currentImage.getWidth(this);<br />
        int ih = m_currentImage.getHeight(this);</p>
<p>        m_buffIm = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB);</p>
<p>        big = m_buffIm.createGraphics();</p>
<p>        big.drawImage(m_currentImage, 0, 0, this);</p>
<p>        Point point1 = m_clickedPointArray[0];<br />
        Point point2 = m_clickedPointArray[1];</p>
<p>        //Reset the click point count...finished with line<br />
        m_clickPointCount = 0;</p>
<p>        OraLine oraLine = new OraLine(point1, point2, Color.BLUE);</p>
<p>        gnsLabel.getOraImage().getImageModHolder().addShape(oraLine);</p>
<p>        final GrabAndScrollLabel gnsFinal = gnsLabel;</p>
<p>        ImageIcon icon = (ImageIcon) gnsLabel.getIcon();<br />
        ImageIcon newIcon = new ImageIcon(icon.getImage())<br />
        {<br />
            public void paintIcon(Component c, Graphics g, int x, int y)<br />
            {<br />
              super.paintIcon(c, g, x, y);</p>
<p>              GraphicTransformInfo gti = createGraphicTransformInfo((Graphics2D)g,<br />
                                                      x,<br />
                                                      y);</p>
<p>              //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!<br />
              gnsFinal.applyModifications(gti);</p>
<p>            }<br />
        };</p>
<p>        gnsLabel.setIcon(newIcon);</p>
<p>        m_port.setView(gnsLabel);</p>
<p>        m_jsPane.setViewport(m_port);</p>
<p>        getRootPane().revalidate();<br />
        getRootPane().repaint();<br />
    }</p>
<p>private GraphicTransformInfo createGraphicTransformInfo(Graphics2D g2D, int x, int y)<br />
    {<br />
      GraphicTransformInfo gti = new GraphicTransformInfo();</p>
<p>      gti.setClickPoints(m_clickPointArray);<br />
      gti.setGraphics2D(g2D);</p>
<p>      m_jsPane = (JScrollPane) m_tabbedPane.getSelectedComponent();<br />
      m_port = m_jsPane.getViewport();<br />
      GrabAndScrollLabel gnsLabel = (GrabAndScrollLabel) m_port.getView();</p>
<p>      m_currentImageIcon = (ImageIcon) gnsLabel.getIcon();<br />
      m_currentImage = m_currentImageIcon.getImage();</p>
<p>      gti.setContainerWidth(gnsLabel.getWidth());<br />
      gti.setContainerHeight(gnsLabel.getHeight());</p>
<p>      gti.setTransformPoint(new Point(x,y));</p>
<p>      gti.setImageWidth(gnsLabel.getOraImage().getOriginalImageWidth());<br />
      System.out.println("ImageWidth = " + gti.getImageWidth());</p>
<p>      gti.setImageHeight(gnsLabel.getOraImage().getOriginalImageHeight());<br />
      System.out.println("ImageHeight = " + gti.getImageHeight());</p>
<p>      gti.setPortalWidth(m_port.getWidth());<br />
      gti.setPortalHeight(m_port.getHeight());</p>
<p>      return gti;<br />
    }</p>
<p>public BufferedImage applyModifications(GraphicTransformInfo gti)<br />
    {<br />
      ImageIcon icon = oraImage.getImageIcon();<br />
      BufferedImage bi = new BufferedImage(oraImage.getOriginalImageWidth(),<br />
                                           oraImage.getOriginalImageHeight(),<br />
                                           BufferedImage.TYPE_INT_ARGB);</p>
<p>      ImageModHolder mods = oraImage.getImageModHolder();</p>
<p>      Graphics2D g2D = gti.getGraphics2D();</p>
<p>      //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</p>
<p>      g2D.translate(gti.getTransformPoint().x, gti.getTransformPoint().y);</p>
<p>      g2D.scale(mods.getZoomFactor(), mods.getZoomFactor());</p>
<p>      Iterator shapeIter = mods.getShapeList().iterator();</p>
<p>      while(shapeIter.hasNext())<br />
      {<br />
        OraShape shape = (OraShape)shapeIter.next();</p>
<p>        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!<br />
        shape.draw(gti);  //see draw(GraphicTransformInfo)  below<br />
      }</p>
<p>      return bi;<br />
    }</p>
<p>public void draw(GraphicTransformInfo gti)<br />
    {<br />
        Graphics2D g2D = gti.getGraphics2D();</p>
<p>        g2D.setColor(color);<br />
        g2D.setStroke(new BasicStroke(shapeSize));</p>
<p>        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!<br />
        Point start = gti.resetPoint(begin);<br />
        Point stop  = gti.resetPoint(end);</p>
<p>        g2D.drawLine(start.x, start.y, stop.x, stop.y);<br />
    }</p>
<p>public Point resetPoint(Point oldPoint)<br />
    {<br />
        double xOffset = (nPortalWidth / 2) - (nImageWidth / 2);<br />
        double yOffset = (nPortalHeight / 2) - (nImageHeight / 2);</p>
<p>        System.out.println("Offset X = " + (int)xOffset +<br />
                           "  Y = " + (int)yOffset);</p>
<p>        Point newPoint = new Point(oldPoint);</p>
<p>        if(nPortalHeight < nContainerHeight)<br />
        {<br />
          //Shunken viewport in vertical direction<br />
            newPoint.y = oldPoint.y;<br />
        }<br />
        else<br />
        {<br />
          //!!!!!!!!!!!!!!!!!!!!!!<br />
          newPoint.y -= yOffset;<br />
        }</p>
<p>        if(nPortalWidth < nContainerWidth)<br />
        {<br />
          //Shunken viewport in horizontal direction<br />
            newPoint.x = oldPoint.x;<br />
        }<br />
        else<br />
        {<br />
          //!!!!!!!!!!!!!!!!!!!!!!<br />
            newPoint.x -= xOffset;<br />
        }</p>
<p>        return new Point(newPoint);<br />
    }<br />

Thanks. I really appreciate any insight you may have, even if it is just a link to an article that deals with what I am trying to do.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Darrin
Offline
Joined: 2006-02-17

OK,

I took a look at it but didn't get much out of it...no scale, no image, and it was doing what I am now (reversing out the translate by using the negative of the value).

I did notice though that you need to apply the operations in reverse if you do a roatate (which...some day I hope...this little SIMPLE applet will do). That will come in handy.

BTW, I noticed you doing a g2.rotate(-1 * Math.PI/2).

It used to be (figure it still is) faster to do a g2.rotate(0 - Math.PI/2).

Anyway, thanks for trying to help. It really is appreciated.

Darrin
Offline
Joined: 2006-02-17

Small update..doing the reverse transform the left x is still wrong. It looked to be correct, but as you move up the image it is clear that it isn't right. It is wrong proportionally at least though!

zander
Offline
Joined: 2003-06-13

What about this;
[code]
import java.awt.*;
import javax.swing.*;

public class Example extends JPanel {
private ImageIcon image;
private Point from, to;
double scale;
public Example(ImageIcon image, int scale) {
this.image = image;
from = new Point (0, 5);
to = new Point(20, 40);
this.scale = scale/100d;
}

public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
double offsetX = image.getIconWidth()/2.0;
double offsetY = image.getIconHeight()/2.0;
g2.translate(offsetX, offsetY); // move to center of image
g2.scale(scale, scale); // scale
g2.translate(0-offsetX * (1/scale), 0-offsetY * (1/scale)); // move back
g2.drawImage(image.getImage(), 0, 0, this);

g2.drawLine(from.x, from.y, to.x, to.y);
}

public static void main(String[] args) {
JFrame a = new JFrame();
ImageIcon image = new ImageIcon("/usr/local/kde/share/wallpapers/No-Ones-Laughing-3.jpg");
int scale;
try {
scale = Integer.parseInt(args[0]);
} catch(Exception e) {
scale = 100; // in percent
}
a.getContentPane().add(new Example(image, scale));
a.setSize(new Dimension(400, 400));
a.show();
}
}
[/code]

Darrin
Offline
Joined: 2006-02-17

Thanks.

That may help, and I finally found somthing else on the net searching for AffineTransfer and drawImage together.

Darrin
Offline
Joined: 2006-02-17

The applet has been reworked and now the lines draw as they should but I still have a problem when I zoom, and I think the question I need to ask now is this:

We have an image which has been zoomed. No lines are yet drawn on it.

A line is added.

The image is then zoomed in or out one more time.

How do we get the line to remap to the newly zoomed image?

What I am doing is:

0) Load up an image from a file.
1) Create a zoomed image by using the Image.getScaledInstance() method.
2) Create a BufferedImage with the width and height of the image AFTER the zoom has taken place.
3) Create a Graphics2D object by the createGraphics from this BufferedImage.
4) Place the image into the BufferedImage by doing a BufferedImage.drawImage
5) Select two points to be the start and end of the line.
6) Draw the line using Graphics2D.drawLine(start.x, start.y, end.x, end.y).
7) Zoom the image again (using the Image.getScaledInstance() method.
8) Create a new ImageIcon using the image from above and override its paintIcon method like this:

[code]

ImageIcon newIcon = new ImageIcon(newImage)
{
public void paintIcon(Component c, Graphics g, int x, int y)
{
super.paintIcon(c, g, x, y);
//********************************
zoom(); //<---- ZOOM call
//********************************
}
};
[/code]

Now in the zoom routine, what do I need to do to get the lines to draw at the proper location and size?

Remember, the points of the lines (stored in a list as home grown line objects) are in screen coordinates, not image coordinates since they are just click points

Note that when I use these points to draw a line, all is well (the line gets drawn where it should), but I have problems when I zoom the image.

Thanks!

zander
Offline
Joined: 2003-06-13

> I know I've posted a similar question before, but I'm
> still hoping someone here can either point me to a
> forum to help me out or see the error of my ways.
>
> I recently read the majority of "Building Imaging
> Applications with Java" and I am now more confused
> than when I started (AWT or JAI or Java2D or ....).
>
> All I want to know is how to draw a little line on an
> image that has been scaled.

Thats it? The image gets scaled and the line has a position and size relavite to (for example) the 100% one, and has to be redrawn correcty after scaling?

> I've searched the
> Internet, read everything I can find, looked at
> examples (none of which did what I needed, but they
> wre all I could find) and basically banged my head
> against the computer for over a week now with no
> progress.

Hmm, what can I say... Already though about paying someone else for the solution?
Here is a class that has a paint with rotation; which is already quite complex, so have a look there.
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/uic/uicompiler/uic/themes...
see method paintBorder()

Let us know.

Darrin
Offline
Joined: 2006-02-17

I want to learn how to do this. I just need one example...just one that draws a line on a scaled image without leaning on a home grown framework.

I'll look at the example you sent.

Thanks.

Darrin
Offline
Joined: 2006-02-17

Almost forgot.

Looking at it, it seems to me that I shouldn't have to worry about the offset (offsetX = viewX / 2 - imageX / 2....newX -= offsetX). In fact, I'm not even sure why this offset works for the 100% case! After all, why would the image be offset by that amount to start with?

Wwhat I think should happen is that once the translater is done to get the point from the screen to the image (the 328, 54 to 0, 0), then the scale is applied, the point should then be translated back from 0,0 to 328, 54. That sure isn't what I am doing, and in fact, I don't even know how (if it is even possible) to do a reverse translate like that.

Is it? If so, how is it done?

I have a Graphics2D object that I use to translate from 328,54 to 0,0, but how do I use it to translate from 0, 0 to 328, 54?

MANY thanks folks. This has been driving me nuts! I just ordered another book (hopefully better than the one I just read at explaining what I need) so maybe it will tell how to do this when it gets to me in a week or so.

zander
Offline
Joined: 2003-06-13

>I have a Graphics2D object that I use to translate from 328,54 to 0,0,

g2.translate(328,54);

> but how do I use it to translate from 0, 0 to 328, 54?
g2.translate(-328,-54);

Darrin
Offline
Joined: 2006-02-17

Thanks. I'll give that a try...don't know if it will cure my ills or not, but it seems to make more sense that applying an offset.

Darrin
Offline
Joined: 2006-02-17

That didn't cure it (only about the millionth thing I've tried).

What I see it doing now is that it is drawing correctly at 100% (as always) but now the zoom no longer works.

Also, the Y axis and scale of the line is still wrong when trying to draw on a zoomed image.

HOWEVER, at least doing this the left side x is correct, although the right side isn't.

Here is what I changed:

[code]
public BufferedImage applyModifications(GraphicTransformInfo gti)
{
...
Graphics2D g2D = gti.getGraphics2D();

g2D.translate(gti.getTransformPoint().x, gti.getTransformPoint().y);

System.out.println("Transform Point " + gti.getTransformPoint());
g2D.scale(mods.getZoomFactor(), mods.getZoomFactor());

g2D.translate(-gti.getTransformPoint().x, -gti.getTransformPoint().y);
//Added this line ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Iterator shapeIter = mods.getShapeList().iterator();

while(shapeIter.hasNext())
{
OraShape shape = (OraShape)shapeIter.next();
shape.draw(gti);
// ^^^^ No longer applies the offset
}

return bi;
}

[/code]

I guess that I could treat a zoom differently from a draw (currently they both call the same applyModifications) so I can cure the Zoom malfunction by removing the second transform, but I still have the issue with the Y axis and scale being off.

Has anyone ever seen an example of how do draw a line on a zoomed image in Java that doesn't use some sort of home grown framework?