# Manipulating BufferedImage

• 01-08-2011, 12:43 PM
ace_quorthon
Manipulating BufferedImage
Dear all,

Short project explanation:
I'm writing a collection of simple network-based multiplayer games (with a friend). The network part works, and we have some unfinished (working) games at alpha phase. We are improving the base of the games all the time (since we try to keep the base of all games the same).

Problem explanation:
I am trying to paint an image on my gameboard with certain manipulations.
• Rotation
• Translation
• Scaling
• Opacity

The first three manipulations work. I have the Image object, manipulate the AffineTransform of the grahics2D object, and paint the image based on this manipulated affinetransform (you can see the implementation in the code below).

The fourth manipulation seems to require a different approach. I found out that I can use a filter on a BufferedImage to make it opaque: Drawing an Image (The Java™ Tutorials > 2D Graphics > Working with Images). See 'Filtering Images'.

So I have been fooling around with this code, trying different approaches to (possibly) applying all 4 manipulations on one image. This is what I got so far:

Code:

/**
* This paints the image of this drawable on the board.
*/
@Override
public void paintComponent(Graphics2D g2d, ImageObserver observer, Camera2D camera, ABoardObject abo)
{
//This is the position of the object relative to the camera's postion.
Vector2 relativeCenter = camera.translatePosition(abo.getCenter());
//Get the rotation of the object.
double rotation = abo.getRotation();
//This is the relative lupos based on the cameras position.
Vector2 relLUPos = camera.translatePosition(abo.getLUPosition());
//This is the scale of the boardobject, or our own overriden scale.
Vector2 scale = null;
if(overrideScale == null)
scale = abo.getScale();
else scale =
overrideScale;

if(this.painttransparent)
{
//We create an empty bufferedimage based on the actual bufferedimage.
int w = buffImage.getWidth(null);
int h = buffImage.getHeight(null);
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

//We retrieve the affinetransform which we may manipulate based on the
//rotation, translation, scaling we want to perform.
Graphics2D big2d = (Graphics2D)bi.getGraphics();
AffineTransform newXform = (AffineTransform)(big2d.getTransform());

//We actually perform the manipulations.
newXform = manipulateTransform(newXform, rotation, relativeCenter, relLUPos, scale);

//We draw the bufferedimage (this will not yet be shown on screen).
big2d.drawImage(buffImage, newXform, null);

//Create a rescale filter op that makes the image 50% opaque
float[] scales = { 0.5f, 0.5f, 0.5f, 0.5f };
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);

// Draw the image, applying the filter
g2d.drawImage(bi, rop, 0, 0); //(int)relLUPos.x, (int)relLUPos.y);
}
else
{
//We retrieve the affinetransform which we may manipulate based on the
//rotation, translation, scaling we want to perform.
AffineTransform newXform = (AffineTransform)(g2d.getTransform().clone());

//We actually perform the manipulations.
newXform = manipulateTransform(newXform, rotation, relativeCenter, relLUPos, scale);

//Then we draw the image on the manipulated affinetransform.
g2d.drawImage(getImage(), newXform, observer);
}

//We also paint the bounds for debug purposes.
//abo.getBounds().paintComponent(g2d, observer, camera);
}

private AffineTransform manipulateTransform(AffineTransform xform, double rot, Vector2 rotcenter, Vector2 transpos, Vector2 scale)
{
//Rotate the affinetransform around the relative center of the boardobject.
if((rot + rotationOffset) != 0)
xform.rotate(rot + rotationOffset, rotcenter.x, rotcenter.y);

//Translate the affinetransform based on the relative position of the object.
//Note that we need the UNROTATED LUPOSITION for this, since the affinetransform already takes care of
//calculating the rotated LUPosition.
xform.translate(transpos.x, transpos.y);

//Scale the affinetransform based on the objects scale, or an overriden scalefactor.
if(scale.x != 1 || scale.y != 1)
xform.scale(scale.x, scale.y);

return xform;
}

I thinks some code explanation is needed here. If not, just skip the Italic sections.

I have split up my actual boardobject (or game-object if you like that name better) into the logical board information (ABoardObject), and the drawing information (ADrawable). This class is the ImageDrawable, which extends the ADrawable class. This way I can give an ABoardobject any ADrawable object, and it will be able to draw the representing form.

To paint the ADrawable, we need to have information from the ABoardObject. Since my board is often bigger than my viewpanel, I have my own camera. We want to paint the drawable based on the positions of the boardobject and the camera. So the first things you see is that I retreive this data. The LUPos is the Left Upper Corner Position of the object, since every object has an invisible rectangle around it

The else statement of the paintComponent method works fine. This is where I draw a rotated, translated and scaled image. The if-statement is where I got stuck. I am trying to first rotate, translate, and scale the bufferedimage, and then paint the bufferedimage opaque at 50%. The opaque painting works fine. However I cant get the bufferedImage to be rotated/translated/scaled as well. Note that the translation CAN be done by performing
Code:

g2d.drawImage(bi, rop, (int)relLUPos.x, (int)relLUPos.y);
Code:

g2d.drawImage(bi, rop, 0, 0);
However I also need to be able to rotate and scale it, so it is not good enough.

Any tips are very much appreciated. I dont need a fully coded answer, I'd just like some ideas to work with.
• 01-08-2011, 05:35 PM
ace_quorthon
Ok looks like I found part of the answer to my question:
Scaling, Shearing, Translating, and Rotating a Buffered Image | Example Depot

The link in my initial post send me in the wrong direction.
The result looks like this:

Code:

if(this.painttransparent)
{
//We perform the manipulations on an affinetransform.
AffineTransform at = manipulateTransform(new AffineTransform(), rotation, relativeCenter, relLUPos, scale);

//Create an atOP that can actually perform the filtering
//operation on the normal image.
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
BufferedImage bifiltered = op.filter(buffImage, null);

//Create a rescale filter op that makes the image 50% opaque
float[] scales = { 0.5f, 0.5f, 0.5f, 0.5f };
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);

// Draw the image, applying the filter
g2d.drawImage(bifiltered, rop, 0, 0);
}

Instead of getting the Graphics2D from the blank bufferedimage (as suggested by the link in my initial post), I simply had to create a new AffineTransform, manipulate that, and then filter the image by an AffineImageOp.filter call.

The only problem that remains is that the g2d.drawImage call is now very very sluggish. It can take up to 25 ms to paint ONE small (32x32 pix) image this way, let alone my level design grid with 400 - 10000 of those images. I dont paint all of them obviously. I'll search for answers to that problem myself first.

Since the above is a different problem, I'll leave this thread as resolved.