Note: This file has moved to notablog.

Note: See the html-ified and greatly enhanced version of this article.
lloyd asks:
>      I need to:
>      Create an application that would view, zoom, rotate and move
>      images (firstly gif, png & jpg, later on tiff and all the rest).

     I _strongly_ suggest you look into SVG and the apache Batik
project.  All of this gets MUCH easier if you're just building an SVG
document that explains how to render the image, and the Batik library
or the Adobe SVG plugin take care of rendering it.

>      I use JPanel in JFrame to view, rotate and scale images using
>      Graphics2D.

     I presume you're doing this with AffineTransforms?

>      How can I move the images inside JPanel if the size of the zoomed
>      image is much bigger then my window?

     The size of the "zoomed image" is bigger than your window, but
you're only going to render the chunk that fits inside your window,
right?  Come to think of it, when I was messing with this stuff I
don't think I ever needed to zoom the image bigger than the window.

>      How can I center the images in the window if the size of the
>      image is smaller then window?

     This is way too complicated for me to explain it in depth in the
time I have, but basically you keep track of your image dimension,
window dimension, image rotation, move offset, and and I guess in your
case zoom level.  Come rendering time, you carry out a sequence of
AffineTransforms based on the numbers.

     I suggest for now you assume symetric images, to simplify the
math, and worry about asymetric images later - basically you handle
his by calculating the numbers independently for width vs. height.
Also, remember that for now we're assuming that this is all in the
render step, you're just checking a set of numbers (scaling, etc) that
you've been keeping track of.

     The big gotcha to watch out for is order-of-operations.  The one
I found to work was: rotate, translate, scale.  Do NOT assume you can
switch the sequence and it'll still work the same.

     First you rotate it around it's center coordinate, i.e.

myAffineTransform.rotate(degrees, originalHeight/2, originalWidth/2))

     Do this first, because you just plain don't want to deal with the
grief of rotating an off-centered image and then figuring out the
right set of offsets to translate it back to center.  By doing this
first, you keep it simple.

     Then you figure out how much you'll need to offset it.  This is
where you have to stop and think carefully about things like, how big
the image is, how big you plan to scale it, how big the space you plan
to draw it in is, etc.  It's not that it takes any genius or twisty math,
but it's easy to forget a step.

     In this example, since we're assuming square images and square
windows, it's easy, but even to say this clearly I have to stop and
use a step by step list:

     1) take either dimension of the original image (width or height)

     2) multiply dimension by the scaling amount you're planning to
        use in the last step, to get the scaled image dimension.

     3) subtract scaled image dimension from the corresponding
        dimension of the window to get the difference.

     4) divide the difference in two to get the offset

     5) translate the image up and over by the offset with
        affineTransform.translate()

     Now, if you're going to use rectangular images or rectangular
windows, this is complicated in two ways.  First, you have to do that
series of steps above independently for each window.  Second, you have
to figure out how the rotation affected the image height and width.
If you're dealing with rotations that don't divide easily into 90
degree chunks, it's even more complicated, since you can't just flip
the width/height around.

     Now, to further complicate matters, if you're going to deal with
images that may be larger than the window, you have to figure out how
the negative values, etc, affect things.  However, other than that it
really doesn't change the steps - you still have to figure out how
much to shove it horizontally and vertically to keep it centerd.
You'll have to use clipping, described at the URL below, to keep java
from drawing any of the image that's outside the window:

     http://java.sun.com/docs/books/tutorial/2d/display/clipping.html

     Anyway, so finally, you scale it with an affineTransform.scale().
Since you did all the annoying math before you scaled it, the scaled
image should be centered nicely in your drawing area.

Steven J. Owens
puff@guild.net