0

I cannot seem to figure out how to draw a transparent and rotated image. I need to be able to draw an image that is transparent and rotated to a certain degree.

I tried this code:

// draws an image that is rotated to a certain degree
public static void drawRotatedImage(BufferedImage image_, int x, int y, int degrees, float scale) {

    // graphics used for the utilities of drawing the image (processing)
    Graphics2D utilGraphics;

    // make rectangular image
    int radius = (int) Math.sqrt(image_.getWidth() * image_.getWidth() + image_.getHeight() * image_.getHeight());
    BufferedImage image1 = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);

    utilGraphics = image1.createGraphics();
// centers image
    utilGraphics.drawImage(image_, image1.getWidth() / 2 - image_.getWidth() / 2, image1.getHeight() / 2 - image_.getHeight() / 2, null);

    // scale image
    int nw = (int) (image1.getWidth() * scale);
    int nh = (int) (image1.getHeight() * scale);

    BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
    utilGraphics.drawImage(image1, 0, 0, nw, nh, null);

    // Rotation information

    double rotationRequired = Math.toRadians (degrees);
    double locationX = image.getWidth() / 2;
    double locationY = image.getHeight() / 2;

    AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
    AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);

    ImageProducer filteredImgProd = new FilteredImageSource(op.filter(image, null).getSource(), filter);
    Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);

    // Drawing the rotated image at the required drawing locations
    g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null);
}

The filter variable is defined as:

private static final ImageFilter filter = new RGBImageFilter() {
    int transparentColor = new Color(0, 0, 0, 0).getRGB() | 0x0000ffcc;

    public final int filterRGB(int x, int y, int rgb) {
        if ((rgb | 0x0000ffcc) == transparentColor) {
            return 0x0000ffcc & rgb;
        } else {
            return rgb;
        }
    }
};
Abra
  • 19,142
  • 7
  • 29
  • 41
  • Not sure what the purpose of `g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null);` is for, `transparentImg` is already an instance of `Image` – MadProgrammer Dec 31 '22 at 08:00
  • `utilGraphics.drawImage(image1, 0, 0, nw, nh, null);` seems odd, as you're painting the image, which is represented by `utilGraphics` back onto itself .... ? – MadProgrammer Dec 31 '22 at 08:08
  • 1. I used that for something else, forgot to delete it. – Bumhole Stupid Dec 31 '22 at 09:12
  • 2. I created a scale variable, that was multiplied by the previous width to allow it to become multiplied by a float. I can't really explain it well, but I hope you understood. – Bumhole Stupid Dec 31 '22 at 09:13
  • You don't understand - the `utilGraphics` is the `Graphics` context from `image1` (used to center the original image), you then paint itself to itself - you never create a new `Graphics` context for `image` (rotate) – MadProgrammer Dec 31 '22 at 10:06

1 Answers1

0

This ...

    BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
    centeredGraphics.drawImage(image1, 0, 0, nw, nh, null);

You're creating a new BufferedImage (image), but you never actually paint anything to it, instead, you paint image1 to it's own Graphics context.

Now, if you wanted a transparent image, you should have used...

BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);

instead of...

BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);

And I never used g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null); as it just doesn't make sense to me (transparentImg is already an Image ‍♂️)

Now, having said all that, I would "suggest" you take each step individually, start by scaling the original image using something like Java: maintaining aspect ratio of JPanel background image and the rotate the image using something like Rotate a buffered image in Java (which will generate a image large enough to contain the rotated image)

Also, if you "create" a Graphics context, you should also dispose of it when you no longer need it, otherwise you could end up with a memory leak.

"Fixed" code...

Just to be clear, I would still recommend sing ARGB instead of RGB for centeredImage as your filter workflow never seemed to work for, but I started with a transparent image anyway

public Image rotateAndScaleImage(BufferedImage originalImage, int degrees, float scale) {
    // make rectangular image
    int radius = (int) Math.sqrt(originalImage.getWidth() * originalImage.getWidth() + originalImage.getHeight() * originalImage.getHeight());
    BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);

    Graphics2D graphics = centeredImage.createGraphics();
    // centers image

    int xPos = (centeredImage.getWidth() - originalImage.getWidth()) / 2;
    int yPos = (centeredImage.getHeight() - originalImage.getHeight()) / 2;

    graphics.drawImage(originalImage, xPos, yPos, null);
    graphics.dispose();

    // scale image
    int nw = (int) (centeredImage.getWidth() * scale);
    int nh = (int) (centeredImage.getHeight() * scale);

    BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
    graphics = image.createGraphics();
    // No scaling is done ???
    graphics.drawImage(centeredImage, 0, 0, nw, nh, null);

    // Rotation information
    double rotationRequired = Math.toRadians(degrees);
    double locationX = centeredImage.getWidth() / 2;
    double locationY = centeredImage.getHeight() / 2;

    AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
    AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);

    ImageProducer filteredImgProd = new FilteredImageSource(op.filter(centeredImage, null).getSource(), filter);
    Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);

    return transparentImg;
}

private static final ImageFilter filter = new RGBImageFilter() {
    int transparentColor = new Color(0, 0, 0, 0).getRGB() | 0x0000ffcc;

    public final int filterRGB(int x, int y, int rgb) {
        if ((rgb | 0x0000ffcc) == transparentColor) {
            return 0x0000ffcc & rgb;
        } else {
            return rgb;
        }
    }
};

Oh, and I'm returning an Image because I painted directly to a component for testing

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Did you test this over black? Because for me, I cannot get it to work. It can rotate, but it doesn't have a transparent background. Instead, the background is black. Is there something wrong on my side? Or is there some kind of solution? I am new to swing as a whole, so I'm having a hard time. – Bumhole Stupid Dec 31 '22 at 09:11
  • @BumholeStupid I tested this with a transparent PNG (using `BufferedImage.TYPE_INT_RGB`) and it generated a image with a black background - the "filter" didn't seem to work as you expect it - are you input images transparent? If they are, and as I stated, used `BufferedImage.TYPE_INT_ARGB` instead – MadProgrammer Dec 31 '22 at 09:56